Skip to Content

Google

POST /api/v1/search/serp/google opens one Google search URL the way the client’s own browser would and returns the HTML of the results page. You send no code: the task is dat.ai’s own SERP script, run the same way as a Browsing API task, with the same key, the same billing and the same task_id in GET /api/v1/browsing/status.

A captcha, “unusual traffic”, “verifying your request”, a walk off Google, an empty page: 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 browser to return a full results page wins), then with the next round of attempts from browsers and IP addresses not yet tried. A page with only a few results or “did not match any documents”, which Google also shows a browser it is reluctant to serve, does not win at once: the other browsers of the same fanout get up to 20 seconds to answer, and the page with the most results is returned. By default the countries where Google refuses more often than it answers are excluded (RU always is). You pay only for the task that returned the page, once, however many browsers of its fanout answered.

Request

POST /api/v1/search/serp/google, JSON body:

FieldTypeDescription
urlstringRequired. A https://www.google.<tld>/search?q=... URL with all its parameters (hl, gl, uule, udm, …); it goes to the browser as is. Google no longer honours num: a results page holds about 10 results whatever it says. udm=50 asks for AI Mode. Other hosts and paths are rejected
localestringA language such as en-GB. It becomes the Accept-Language header, shaped the way Chrome shapes it, and navigator.languages: en-GB → en-GB,en-US;q=0.9,en;q=0.8, de-DE → de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7, fr → fr,en-US;q=0.9,en;q=0.8. Without it the browser speaks its own language
cookiesobjectCookies placed on the Google domain before the first request: {"NID": "..."}. A value is a string or a list of strings; a list is set in order and the browser keeps the last. The cookie consent (SOCS) is set by the server itself, so the consent wall is not your problem; a SOCS or CONSENT of your own replaces ours. On an AI Mode page the server sets none and clicks the wall through instead
headersobjectExtra headers on every request the page makes: {"Cache-Control": "max-age=0"}. A value is a string or a list (a list is joined with , ). Not allowed: User-Agent (a foreign UA is the surest way to a captcha), Cookie, Host, Connection, Content-Length, Sec-*, Proxy-*; Accept-Language only when there is no locale
timeout_secnumberThe budget of one attempt, default 90, maximum 1200
fanoutnumberHow many browsers run an attempt at once, 1–10, default 3. Only the browser that returns the page is billed
attemptsnumberHow many rounds are run in turn when a round produced no page, 1–5, default 3. Every round is a new task from browsers and IP addresses not used before
resolve_linksboolDefault false: the page comes back as Google rendered it. With true, Google’s redirect links in the page (/goto?url=..., /url?...&url=...) are replaced with the addresses they lead to. For these sessions Google renders every outbound link, organic results and AI Overview citations included, only as such tokens; the real URLs are not in the HTML at all. Resolution runs in the same session, one small request to google.com per link, in parallel; typically +3–8 s per task. Ask for it when you need the addresses rather than the text and positions
session_keystringAs in browsing: the rounds of one request never repeat a browser anyway; the key is for fitting the SERP into an existing session
filter.country_iso_includedstring[]The countries the search may run from, as in browsing. A country named here is not taken away by the default exclusions
filter.country_iso_excludedstring[]The countries it must not run from, added to the default exclusions. 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: the body is the page’s HTML, Content-Type: text/html; charset=utf-8.

HeaderMeaning
X-Task-IDThe task that returned the page (visible in GET /api/v1/browsing/status)
X-Serp-Final-URLWhere the browser ended up
X-Serp-LinksHow many redirect links now carry their real address out of how many were found (38/40); absent when the page had none or resolve_links was not set
X-Serp-AI-Overviewexpanded when the page had an AI Overview and it was opened (the HTML holds the full text rather than the collapsed third), present when it would not open; absent when the page had no such block
X-Serp-AI-ModeOn an AI Mode page: complete when the answer finished streaming before the page was read, partial when time ran out with some of it there

Errors: 400 (invalid request), 401, 402 (subscription or balance), 502 when no results page could be obtained (Google refused, a captcha and the like), 504 when no answer came in time. The error body is a JSON object with one field, error, as the server gives it:

// 400 — not a Google search URL, a header the browser must keep its own, a field the endpoint does not know { "error": "url must be a Google search page, e.g. https://www.google.com/search?q=..." } { "error": "headers: User-Agent cannot be set: the node's own browser identifies itself; a different User-Agent is what gets a node refused" } { "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" } // 502 — no browser tried returned a results page { "error": "no results page: Google refused the search (captcha)" } { "error": "no results page: Google's consent page could not be passed" } { "error": "no results page: Google returned an empty page" } { "error": "no results page: the search could not be completed" } // 504 — no answer within the attempts' time { "error": "no results page: no answer in time" }

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

AI Mode

Google’s AI Mode is a search page of its own: one answer that streams in, instead of a list of results. Ask for it with udm=50 in the URL, everything else unchanged:

https://www.google.com/search?q=how+does+photosynthesis+work&udm=50&hl=en&gl=us

The task waits for the answer to finish streaming before it reads the page, and X-Serp-AI-Mode says whether it did. The answer is in the [data-subtree="aimc"] element of the HTML. A browser Google will not give AI Mode to (“AI Mode is not currently available”) counts as a refusal like a captcha does, and the request moves on to another one within fanout and attempts. Google refuses AI Mode far more readily than a results page, so the default fanout of 3 matters here: keep it, or raise it. On this page a locale goes as the Accept-Language header only; navigator.languages stays the browser’s own.

Example

curl -sS -X POST "https://llm.dat.ai/api/v1/search/serp/google" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -o serp.html -D - \ -d '{ "url": "https://www.google.com/search?q=kids+soccer+pants&ie=utf-8&oe=utf-8&hl=en&gl=us&uule=w+CAIQIFISCQkhfJQWRMKJEYZ4AAR0XHaC&glp=1&gws_rd=cr&fg=1", "locale": "en-GB", "headers": {"Cache-Control": "max-age=0"}, "fanout": 2, "attempts": 3 }'

Response (200): the headers say which task answered and what the page holds, the body is the page (about 3 MB for a results page with an AI Overview), saved to serp.html here:

HTTP/2 200 content-type: text/html; charset=utf-8 x-task-id: web:f6fba073-a71e-4e74-87bf-d0c39c71dcf0 x-serp-final-url: https://www.google.com/search?q=kids+soccer+pants&hl=en&gl=us&sei=0QataoTVEbGN9u8PmLSz6Qg x-serp-ai-overview: expanded x-serp-links: 21/21 <!DOCTYPE html> <html itemscope="" itemtype="http://schema.org/SearchResultsPage" lang="en" ...><head><meta charset="UTF-8"> ...

x-serp-links is there because the request asked for resolve_links; on a page without an AI Overview x-serp-ai-overview is absent. A request that got no page answers with a JSON error instead, see Response.