Skip to Content

The AI Search endpoints put one prompt to an answer engine in a real browser and return the reply: its text, the same text as Markdown and as the page’s HTML, the pages it cites, and the stream the page received while the reply was arriving. You send no code and no account: the task is dat.ai’s own script, run as a Browsing API task with the same key, the same billing and the same task_id in GET /api/v1/browsing/status.

An address the engine will not serve, a challenge page, a page with no composer, a reply that never came: none of these is an answer. Such an attempt counts as a failure and the server tries again from elsewhere: first within fanout (the first reply wins), then with the next round of attempts from browsers and IP addresses not yet tried. You pay only for the task that returned the reply.

EngineEndpointThe pageStream
ChatGPTPOST /api/v1/search/ai/chatgptchatgpt.com, the lightweight guest appThe engine’s streamed HTML patches
GeminiPOST /api/v1/search/ai/geminigemini.google.com, as a guestGoogle’s batchexecute frames

Other engines follow under the same prefix with the same request and reply. Claude is not among them: claude.ai has no guest mode, a browser that is not signed in is sent to the login page.

Request

POST /api/v1/search/ai/<engine>, JSON body, the same for every engine:

FieldTypeDescription
promptstringRequired. The message, up to 8000 characters. It goes into a fresh conversation as typed
localestringA language such as en-GB. It becomes the Accept-Language header, shaped the way Chrome shapes it; the page is drawn in that language. The reply follows the language of the prompt
raw_streamboolDefault true: the reply comes with the stream the page received (see Response). false leaves it out
timeout_secnumberThe budget of one attempt, default 150, maximum 1200
fanoutnumberHow many browsers put the prompt at once, 1–10, default 2. Only the browser that returns the reply is billed
attemptsnumberHow many rounds are run in turn when a round produced no reply, 1–5, default 3. Every round is a new task from browsers and IP addresses not used before
session_keystringAs in browsing: the rounds of one request never repeat a browser anyway; the key is for fitting the request into an existing session
filter.country_iso_includedstring[]The countries the request may run from, as in browsing
filter.country_iso_excludedstring[]The countries it must not run from. Not together with country_iso_included
ttl_daysnumberHow long the task body is kept, as in browsing

Unknown fields are rejected with 400.

Response

200: a JSON object, the same shape for every engine. X-Task-ID in the headers names the task that returned it.

{ "task_id": "web:1d1f7e6e-...", "provider": "chatgpt", "prompt": "What is the capital of Australia and how big is its population? Cite sources.", "final_url": "https://chatgpt.com/uc/6a9e...", "status": "complete", "answer": { "text": "The capital of Australia is Canberra ...", "markdown": "The capital of **Australia is Canberra** ...\n\n- **Canberra’s population:** about **484,630 people** ... [1]\n", "html": "<p>The capital of <strong>Australia is Canberra</strong> ...</p>" }, "web_search": true, "sources": [ { "url": "https://www.abs.gov.au/statistics/people/population/regional-population/2024-25", "title": "Regional population, 2024-25 financial year | Australian Bureau of Statistics", "domain": "abs.gov.au", "attribution": "Australian Bureau of Statistics" } ], "citations": [ { "url": "https://www.abs.gov.au/statistics/people/population/regional-population/2024-25", "title": "Regional population, 2024-25 financial year | Australian Bureau of Statistics", "source": 1, "text": "Canberra’s population: about 484,630 people as of 30 June 2025, according to the Australian Bureau of Statistics (ABS).", "supporting": false } ], "raw_stream": "..." }
FieldMeaning
providerThe engine, as in the path
statuscomplete when the reply finished streaming before it was read, partial when the attempt’s time ran out with some of it there
answer.textThe reply as the page shows it, plain text
answer.markdownThe same reply as Markdown: headings, lists, emphasis, code, tables and links. A citation in the text is [n], n being the source’s place in sources. Empty when the engine’s page could not give it
answer.htmlThe reply’s HTML as the page rendered it
web_searchWhether the engine searched the web for this reply, that is, whether it cites anything
sourcesEvery page the reply draws on, in the order the citations count, with the title and the attribution the engine shows; an engine that says more about a page says it here. The engine’s own click tags (utm_source) are removed from the addresses
citationsOne entry per place in the reply that points at a page: the address, its number in sources, and the sentence or list item it stands in. supporting marks the pages an engine lists as additional to a citation
raw_streamWhat the page received while the reply arrived, verbatim; what that is depends on the engine, see its page. Absent with raw_stream: false or when the page got no stream

Errors: 400 (invalid request), 401, 402 (subscription or balance), 404 (an engine that does not exist), 502 when no reply could be obtained (the engine refused every browser tried), 504 when no answer came in time. The error body is a JSON object with one field, error, as the server gives it:

// 400 — no prompt, a value out of range, a field the endpoint does not know { "error": "prompt is required" } { "error": "attempts must be between 1 and 5" } { "error": "locale must be a language tag such as en-GB or de" } { "error": "Invalid input data" } // 401 — no key, or a wrong one { "error": "Authentication required: missing Authorization header. Please provide Authorization header with Bearer token format: 'Authorization: Bearer <your-api-key>'" } // 402 — a quota at zero and no balance to cover it (see Account & Balance) { "error": "Insufficient balance: the browser_seconds quota is used up and there is no USD balance to cover overage; top up in the dashboard (https://dash.dat.ai) or check GET /api/v1/account/balance", "code": "insufficient_balance", "exhausted": "browser_seconds" } // 404 — an engine that does not exist { "error": "unknown answer engine" } // 502 — every browser tried was refused, or none produced a reply { "error": "no reply: the engine refused this browser" } { "error": "no reply: the engine gave no reply" } { "error": "no reply: the request could not be completed" } // 504 — no answer within the attempts' time { "error": "no reply: no answer in time" }

The whole request takes up to attempts × (timeout_sec + ~45 s); with long budgets keep attempts small.