AI Search
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.
| Engine | Endpoint | The page | Stream |
|---|---|---|---|
| ChatGPT | POST /api/v1/search/ai/chatgpt | chatgpt.com, the lightweight guest app | The engine’s streamed HTML patches |
| Gemini | POST /api/v1/search/ai/gemini | gemini.google.com, as a guest | Google’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:
| Field | Type | Description |
|---|---|---|
prompt | string | Required. The message, up to 8000 characters. It goes into a fresh conversation as typed |
locale | string | A 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_stream | bool | Default true: the reply comes with the stream the page received (see Response). false leaves it out |
timeout_sec | number | The budget of one attempt, default 150, maximum 1200 |
fanout | number | How many browsers put the prompt at once, 1–10, default 2. Only the browser that returns the reply is billed |
attempts | number | How 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_key | string | As 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_included | string[] | The countries the request may run from, as in browsing |
filter.country_iso_excluded | string[] | The countries it must not run from. Not together with country_iso_included |
ttl_days | number | How 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": "..."
}| Field | Meaning |
|---|---|
provider | The engine, as in the path |
status | complete when the reply finished streaming before it was read, partial when the attempt’s time ran out with some of it there |
answer.text | The reply as the page shows it, plain text |
answer.markdown | The 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.html | The reply’s HTML as the page rendered it |
web_search | Whether the engine searched the web for this reply, that is, whether it cites anything |
sources | Every 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 |
citations | One 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_stream | What 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.