Query a real consumer AI search engine and get the answer users actually see, as JSON, with provenance on how it was captured. This guide gets you from an API key to a parsed Envelope in one call.
Integrating with an AI agent or coding assistant? The For AI agents page is a single self-contained spec — auth, submit, poll, parse, errors, budgets — with explicit URLs and full JSON shapes. Or hand it the whole corpus at /llms-full.txt.
1

Get an API key

Grab a key from the dashboard, then export it so the examples below just work.
  1. Open the dashboard and go to Settings → API Keys.
  2. Create a key and copy it (you only see the full value once).
  3. Export it in your shell:
Verify connectivity with the unauthenticated health check:
Response
New accounts start with 500 free credits, no card required. A capture is charged only on success — an empty or failed capture costs nothing. See pricing.
2

Make your first search — one call, one answer

Submit a query and one surface. A single-surface request with no webhook runs synchronously by default: the API holds the connection while the capture runs and returns the finished result in one 200. Add ?view=flat for the simplest possible shape.
200 OK (?view=flat)
Drop ?view=flat and the same call returns the full result: the parent id, its rollup status, and the Envelope per child:
200 OK (default)
Shape the payload with include. include: { markdown: false } drops answer.markdown (answer.text always stays); include: { html: true } adds an opt-in proof-of-page html URL for consumer-UI (scrape) surfaces. Unknown include keys return 400 VALIDATION_FAILED.
Sync is the default only for the hello-world case: one surface, no webhook. Multi-surface requests, requests with a webhook, or ?mode=async return 202 and run durably (next step). You can also force the inline path explicitly with ?mode=sync or the header Prefer: wait=30.
3

Fan out asynchronously

For more surfaces or regions, use the durable path: it returns 202 immediately with a parent job and one child per surface × region. A child id has three dot-separated segments — <jobId>.<surface>.<regionKey>.
202 Accepted
4

Poll for the result

Fetch a job with GET /v1/jobs/:id. A parent id (no dots) returns the rollup; a child id (dotted) returns the canonical Envelope. While a capture is still running a child returns { "job": { "id": ..., "status": "running" }, "children": [] }. Poll until job.status reaches a terminal state — completed, partial, failed, canceled, or expired — and never past it.
Polling is fine for a single surface. For fan-out across many surfaces and regions, prefer webhooks — you get a POST on each child’s terminal state instead of polling N jobs.
5

Read the Envelope

A completed child returns job, provenance, answer, evidence, and credits (plus an optional html proof-of-page URL when you opt in with include.html: true).
GET /v1/jobs/job_8t2q.chatgpt.us

Key fields to read first

When a surface returns nothing, the job still reaches completed with provenance.surfacePresent: false, an empty answer, and a surface_absent entry in job.warnings. Absence is a valid result — the capture costs no credits.

Safe retries with an idempotency key

Pass idempotencyKey in the body (it is not a header). Re-submitting the same key with the same body replays the original job — 202 with the header Idempotent-Replayed: true, no new work, no double billing. The same key with a different body returns 409 IDEMPOTENCY_CONFLICT.

Per-surface alias

POST /v1/search/:surface targets one surface (sync by default, like any single-surface call) and accepts prompt (an alias of query) plus a flat country:
Sync mode resolves one surface at a time. For more than one surface — or when you hit 429 CONCURRENCY_LIMIT_EXCEEDED — use ?mode=async with polling or webhooks.

Fan out across surfaces & regions

Add more surfaces and regions to a single search. The API creates one child per surface × region, and the parent tracks them all. The region key in each child id is the lowercased country[:city][:language].
202 Accepted
Read the parent id (no dots) to see the roll-up:
The parent is completed when all children complete, failed when all fail, and partial on a mix — processing while any child is still running.

When something goes wrong

Every error, on every endpoint, is the same flat shape — a stable code, an actionable error message, the request’s correlation id, and a deep link into the error catalog:
400 Bad Request
The request_id always matches the X-Request-Id response header (send your own X-Request-Id to thread a correlation id through).

Next steps

Webhooks

Get a signed POST on each child’s terminal state instead of polling.

Batch

Submit up to 500 searches in one request.

Errors

Codes, statuses, and the rate-limit headers to respect.

The Envelope

The canonical response shape, field by field.