- One surface, no webhook (the hello-world case) runs synchronously by default: the call returns the terminal Envelope in one
200. - Multiple surfaces, a
webhook, or?mode=asyncuse the durable path:202immediately with a parent job and one child per surface × region. PollGET /v1/jobs/:idor register a webhook.
Authorization: Bearer <API_KEY> and Content-Type: application/json.
Query parameters
sync forces the bounded inline path; async forces the durable 202 + poll path. Omitted: a single-surface no-webhook request runs sync, everything else async. The header Prefer: wait=30 is equivalent to ?mode=sync.flat projects each Envelope of a sync response to the five-field flat shape (surface, status, text, markdown, sources). One child → the flat object directly; several children → { "results": [...] }.Body
The prompt to run. This is the exact text sent to each surface.
prompt is accepted as an alias.One or more surfaces to capture. Each surface produces one child job per region.Allowed values:
chatgpt, claude, perplexity, gemini, copilot, google_ai_overview, google_ai_mode, google_search, google_news.Where to run each surface — at most 10 per request. One child is created per surface × region. Omitted: one untargeted (
GLOBAL) child per surface. A bare string like "US" is accepted as sugar for { "country": "US" }.How to acquire each surface:
auto (browser-first routing), or a pin — official-api, managed-vendor, own-fleet. A pin a surface cannot serve returns 422 with the supported methods named in the message.Receive a signed POST on each child’s terminal state instead of polling. Setting a webhook forces the async path. Recommended for fan-out.
Safe-retry key. This is a body field, not a header. Reusing the same key with the same body replays the original job (
202 + Idempotent-Replayed: true); reusing it with a different body returns 409 IDEMPOTENCY_CONFLICT.Request
Response 202 Accepted (async)
The parent job id has no dots. Each entry in children is a dotted 4-segment child id — <jobId>.<surface>.<method>.<regionKey> — where the method is what auto actually routed to and the region key is the lowercased country[:city][:language] (or GLOBAL when untargeted).
The parent job id (dotless). Read it with
GET /v1/jobs/job_8t2q to see the roll-up status and per-child state.Always
processing on submit. Terminal states are completed, partial, failed, canceled, expired.Response 200 OK (sync)
A sync call returns the parent id, its rollup status, and the full Envelope per child:
?view=flat the same call returns just { surface, status, text, markdown, sources }.
If a surface returns nothing, the job still completes:
provenance.surfacePresent is false, job.warnings carries a surface_absent warning, and answer is empty. An empty capture costs no credits.Per-surface alias
POST /v1/search/:surface targets one surface — sync by default like any single-surface call. It accepts two convenience fields:
prompt— alias ofquery.country— flat sugar forregions: [{ "country": "..." }].
Idempotency
IncludeidempotencyKey in the body to make retries safe.
| Scenario | Result |
|---|---|
| First request with a key | The job is created normally. |
| Same key + same body | 202 with header Idempotent-Replayed: true — the original job is replayed, no new work, no double billing. |
| Same key + different body | 409 IDEMPOTENCY_CONFLICT. |
Errors
Every error uses the flat shape{ "code", "error", "request_id", "docs_url", "details"? }. Validation messages name the offending field and the allowed values.
400
| Code | Status | When |
|---|---|---|
AUTH_INVALID | 401 | Missing, bad, or revoked API key. |
VALIDATION_FAILED | 400 | A field failed validation — the message names the field and allowed values. |
UNSUPPORTED_SURFACE | 400 | A value in surfaces (or the alias :surface) isn’t a surface. |
INSUFFICIENT_CREDITS | 402 | Not enough credits; nothing is spawned. |
IDEMPOTENCY_CONFLICT | 409 | Same idempotencyKey reused with a different body. |
UNSUPPORTED_METHOD_FOR_SURFACE | 422 | The pinned method has no v1 path on that surface — the message names what IS supported. |
TOO_MANY_REGIONS | 422 | More than 10 regions in one request. |
RATE_LIMIT_EXCEEDED | 429 | Account request rate exceeded. |
CONCURRENCY_LIMIT_EXCEEDED | 429 | Too many in-flight sync requests. |
QUEUE_CAPACITY_EXCEEDED | 429 | Async queue is full. |
Retry-After alongside X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. See Errors for the full catalog.
Response headers
| Header | On | Meaning |
|---|---|---|
X-Request-Id | every response | Correlation id; matches request_id in error bodies. Send your own to thread it through. |
X-AISearch-Version | every response | Envelope schema version that served the request. |
X-RateLimit-Limit / -Remaining / -Reset | submits + 429 | Live admission-bucket state for your key. |
Retry-After | 429 | Seconds to wait before retrying. |
X-Concurrency-Limit / -Running / -Queued | submits | Sync concurrency state for your key. |
Idempotent-Replayed | replays | true when an idempotent re-submit replayed the original job. |
Read a job
Poll a parent or fetch a single child’s Envelope.
The Envelope
Every field in the canonical per-surface result.