On this section
Documentation
Concepts and platform behavior. Endpoint-by-endpoint detail lives in the API Reference; task-oriented walkthroughs in Guides. Prefer markdown? This page is mirrored at /docs.md and everything at once at /llms-full.txt.
Introduction
VendorStacks is a data API over public subprocessor and DPA disclosures. Given an entity (a domain) it answers: does this entity publish a disclosure, where does it live, and which vendors are on it — returned as a structured vendor stack across 13 categories with the quoted disclosure row as evidence. Reverse lookup returns the entities that list a given vendor or category. It is built for GTM enrichment pipelines and AI agents; extraction is deterministic — no LLM, no invented vendors.
Base URL: https://vendorradar-production.up.railway.app
. All responses are JSON and carry an X-Request-Id header.
Quick start
curl -X POST https://vendorradar-production.up.railway.app
/v1/keys \
-H "Content-Type: application/json" \
-d '{"email": "you@company.com"}'curl "https://vendorradar-production.up.railway.app /v1/check?url=stripe.com" \ -H "Authorization: Bearer vr_live_..."
curl "https://vendorradar-production.up.railway.app /v1/prospect?vendor=twilio&category=sms_messaging" \ -H "Authorization: Bearer vr_live_..."
Agent-first surface
- ›[vendorradar-production.up.railway.app /llms.txt](https://vendorradar-production.up.railway.app /llms.txt) — orientation file for LLMs
- ›[vendorradar-production.up.railway.app /openapi.json](https://vendorradar-production.up.railway.app /openapi.json) — OpenAPI 3.1 spec for codegen and tool generation
- ›[vendorradar-production.up.railway.app /skill](https://vendorradar-production.up.railway.app /skill) — installable SKILL.md for Claude Code and friends
- ›[vendorradar-production.up.railway.app /agents](https://vendorradar-production.up.railway.app /agents) — agent integration guide
- ›/llms-full.txt — every page of these docs as one markdown document
Authentication
Authenticated endpoints take a Bearer key: Authorization: Bearer vr_live_…. Keys are issued instantly — POST an email to /v1/keys and the key arrives in the same response. No verification click, which means an agent can self-provision mid-task.
The plaintext key is shown exactly once and never stored — only a SHA-256 hash is kept server-side, so a lost key cannot be recovered, only replaced. Rotate keys from the dashboard: create a new key, move traffic, revoke the old one. Revocation is immediate.
Credits & pricing
Everything is prepaid credits, and every lookup costs the same: 1 credit flat. If the entity is already in the index you get the answer in under a second; if it isn't, the API live-scans it automatically (15–90s) at no extra cost. Credits never expire.
| Operation | Endpoint | Cost | Latency |
|---|---|---|---|
| Lookup | GET /v1/check (live-scans automatically when unindexed) · GET /v1/company/{domain} (never scans) | 1 credit | <1s indexed · 15–90s live scan |
| Reverse-lookup page | GET /v1/prospect (10 entities) | 1 credit | <1s |
Packs: $10 → 1,100 credits · $50 → 6,000 · $250 → 35,000. New keys start with 25 free credits; the free plan allows 5 live scans per day (indexed lookups are uncapped) — any pack removes the cap. Machine-readable pricing is served at /v1/pricing.
When a request would cost more than the key holds, the API answers 402 Payment Required with the exact shortfall and a topup_url — nothing is charged, and the retry after top-up is idempotent. See Errors for the full anatomy.
Usage limits
| Limit | Value | Behavior |
|---|---|---|
| Request rate | 60 requests / minute / key | 429 with a Retry-After header (seconds). Never charged. |
| Free-plan live scans | 5 per day | Indexed lookups are uncapped. Exceeding the allowance returns 429 daily_scan_limit; any credit pack removes the cap. |
| Concurrent live scans | 3 per key, platform-wide | Additional scans queue. Concurrent requests for the same domain share one scan (and one charge) — per-domain dedup. |
| Key issuance | 3 keys / IP / day on /v1/keys | 429 beyond that; existing keys are unaffected. |
| Request body | 32KB max | 413 payload_too_large. |
- ›Set client timeouts to 120s — an unindexed domain triggers an automatic live scan that legitimately takes 15–90 seconds, at the same 1-credit price.
- ›The index already covers most entities you'll ask about, so most lookups return in under a second — latency, not price, is the only difference when a live scan kicks in.
- ›Need more than 60 rpm?
rpm_limitis configurable per key — contact us and we'll raise it.
Errors
Conventional HTTP status codes with a JSON body. No error response ever charges credits. Every response — success or error — carries an X-Request-Id header for correlation; quote it in support requests.
400 validation_error
{ "error": "validation_error",
"message": "url must be a domain, e.g. acme.com" }Malformed domain, unknown category slug, bad JSON, or missing required parameter.
401 unauthorized
{ "error": "unauthorized",
"message": "Missing or invalid API key. Get one instantly: POST https://vendorradar-production.up.railway.app
/v1/keys {\"email\": ...}" }Missing, malformed, or revoked key. The message self-documents key issuance, so an agent hitting 401 can self-provision without reading docs.
402 payment_required
{ "error": "payment_required",
"message": "This request costs 1 credit; balance is 0.",
"remaining_credits": 0,
"credits_needed": 1,
"topup_url": "https://vendorstacks.com/app/billing" }Nothing was charged. Agents: surface topup_url and the exact shortfall to a human, pause, and retry after top-up — the retry is idempotent. See the 402 guide for the full pattern.
404 not_found
{ "error": "not_found",
"message": "No cached profile for that domain." }Unknown route, or a /v1/company domain that has never been scanned.
413 payload_too_large
{ "error": "payload_too_large", "message": "Body exceeds 32KB." }429 rate_limited / daily_scan_limit
{ "error": "rate_limited",
"message": "60 requests/minute exceeded.",
"retry_after_seconds": 12 }{ "error": "daily_scan_limit",
"message": "Free plan allows 5 live scans per day. Indexed lookups remain available; any credit pack removes the cap.",
"retry_after_seconds": 41230 }rate_limited is the per-minute request cap (also sent as a Retry-After header) — back off and retry. daily_scan_limit is the free plan's live-scan allowance (5/day); indexed lookups keep working, and any pack removes the cap. Neither is ever charged.
500 internal_error
{ "error": "internal_error",
"message": "Something broke on our side.",
"request_id": "9f6f4a1e-…" }Include the request_id (also in the X-Request-Id header) when contacting support — it pins the exact log line.
502 scan_failed
{ "error": "scan_failed",
"message": "Could not reach the target's disclosure pages.",
"charged": 0 }Fairness rule: a lookup whose live scan fails — unreachable target, no disclosure located — always charges 0 credits.