On this section
← All guides
Enrich a list of domains
Loop a CSV of domains through /v1/check with rate-limit handling, and write the disclosed stack back out as CSV.
Markdown mirror: /docs/guides/enrich-a-domain-list.md
The most common first job: you have a CSV of accounts, you want each row enriched with the vendors that entity discloses. Every lookup is 1 credit flat, so a 1,000-row list is exactly 1,000 credits (~$9 on the starter pack). Indexed domains answer in under a second; unindexed ones live-scan automatically at the same price — budget extra wall-clock time for those, not extra credits.
The loop
enrich.mjs (Node 18+, no dependencies)
import { readFileSync, writeFileSync } from "node:fs";
const KEY = process.env.VENDORSTACKS_KEY;
const domains = readFileSync("accounts.csv", "utf8")
.split("\n").map(l => l.split(",")[0].trim()).filter(Boolean);
const out = [["domain", "found", "sms_vendor", "email_vendor", "payments_vendor", "evidence_url"]];
for (const domain of domains) {
const res = await fetch(
`https://vendorradar-production.up.railway.app
/v1/check?url=${encodeURIComponent(domain)}`,
{ headers: { Authorization: `Bearer ${KEY}` } },
);
if (res.status === 429) {
// Honor Retry-After, then retry the same domain.
const wait = Number(res.headers.get("retry-after") ?? 2);
await new Promise(r => setTimeout(r, wait * 1000));
domains.push(domain);
continue;
}
if (res.status === 402) throw new Error("Out of credits — top up and re-run.");
const data = await res.json();
const first = (cat) => data.vendor_stack?.[cat]?.[0];
out.push([
domain,
String(data.found),
first("sms_messaging")?.vendor ?? "",
first("email")?.vendor ?? "",
first("payments")?.vendor ?? "",
first("sms_messaging")?.evidence?.source ?? "",
]);
}
writeFileSync("enriched.csv", out.map(r => r.join(",")).join("\n"));Details that matter
- ›The 429 branch re-queues the same domain after
Retry-Afterseconds — you lose no rows and no credits (rate-limited calls are never charged). - ›
found: falsemeans no disclosure was located, not "no vendors". Keep those rows and treat them as unknown. - ›The script is idempotent from the API's perspective — re-running charges again for each lookup, so checkpoint
outto disk periodically for very large lists. - ›Want today's truth for a handful of hot accounts? Re-run just those with
&fresh=true— same 1-credit price, just slower (15–90s; set a 120s timeout).
Full parameter reference: GET /v1/check. Rate-limit specifics: Usage limits.