Routing and fallbacks
How /v1/run picks vendors for a capability, falls through on misses and errors, and how to steer it.
Before you start
POST /v1/call/{provider}/{endpoint} runs one endpoint and never routes. POST /v1/run/{capability}
plans a list of endpoints that serve the capability and tries them in turn.
The plan
- Candidates. Every live endpoint for the capability. Endpoints whose circuit breaker is open, whose required inputs you did not send, or that your filters exclude drop out.
- Order. Your
provider.orderorsortwhen given; otherwise the capability's declared route, if it has one. With nothing to decide the order, GridRouter load-balances: vendors with recent errors go last and the rest are drawn with weight 1/price². - Walk. Each attempt is quoted, run and settled on its own. The run stops at the first
hit (or the first successful response with
"stop_when": "success"), and falls through on misses, vendor errors and vendors you have not added a key for.
A run stops immediately on account errors, because a fallback would fail the same way:
insufficient_balance, budget_blocked, account_frozen, scope_denied and
max_cost_exceeded. When no vendor could run because none has your key, the error is one
vendor_key_required naming all of them.
Every attempt is in the result's attempts, with its outcome (hit, miss, failed,
skipped), cost, latency and error code.
Provider preferences
Send them as provider in the run body, or save them in a preset.
| Field | Effect |
|---|---|
order | Try these first, in this order. Vendor slugs (hunter) or endpoint ids (hunter/email.find); at most 20 |
allow_fallbacks | Default true. With false, only the vendors in order are tried |
only / ignore | Keep only, or drop, these vendors or endpoints |
sort | price, reliability, latency, throughput or quality |
max_cost_micro / max_price_micro | Skip endpoints whose quote (or worst-case price) is above this. The lower of the two wins |
preferred_max_latency_ms | Endpoints with a median latency above this go after the ones under it |
data_policy.storage | any (default), reviewed (drop vendors whose data terms are not reviewed yet) or storable (only vendors whose results may be kept) |
data_policy.zdr | true stops GridRouter from storing this call's request and response bodies |
require_verified | Only vendors GridRouter has verified |
{
"input": { "first_name": "Ada", "last_name": "Lovelace", "domain": "example.com" },
"provider": { "order": ["prospeo", "hunter"], "allow_fallbacks": false },
"fields": ["email", "confidence"],
"stop_when": "hit"
}fields (at most 50) trims the result to the normalized output fields you name. preset: "@slug"
applies a saved preset; anything in the request body overrides it.
Cost caps
The per-request cap is max_cost_micro in the body or X-Grid-Max-Cost in USD as a header. An
attempt whose quote exceeds what is left is skipped, and a single call over the cap fails with
402 max_cost_exceeded before anything runs. POST /v1/quote/{provider}/{endpoint} prices a call
without making it.
Failed attempts and misses on per-success endpoints are never charged. At launch every call runs on
your own vendor keys and costs 0.