Google search parameters: gl, hl, uule, num and more
Google’s results change with a handful of URL parameters: gl (country), hl (interface language), uule (location), num and start (depth and offset), tbs (time and other filters) and safe (SafeSearch). Device changes the page as much as these, though it is not a URL parameter.
Two request shapes
POST /v1/monitor/google (and the GOOGLE async task type) accepts one of two shapes. The request must contain either query with country, query with gl, or url.
Standard shape. You describe the search with fields:
{ "query": "running shoes", "country": "US", "pages": 2 }
Language (hl), city (location, uule) and device are optional fields of this shape.
URL shape. You pass a complete Google web search URL, and the API applies a fixed set of its parameters:
{ "url": "https://www.google.com/search?q=running+shoes&gl=us&num=20" }
The URL must be an absolute http(s) URL on a Google web search host (such as google.com, google.co.uk or www.google.de), with the path /search and a non-empty q. It may carry only q, gl, num, hl and uule; a URL with any other parameter (start, tbs, safe, lr and so on) is refused with a 400, since those are not served yet. Use the URL shape when you already store Google URLs. Date and SafeSearch filtering through the URL are coming.
Mapping table
| Google parameter | Meaning | Standard shape | URL shape |
|---|---|---|---|
q |
Query, operators included | query (1 to 10,000 characters) |
q in the URL |
gl |
Result country | country or gl (ISO 3166-1 alpha-2) |
gl in the URL, or country to override |
hl |
Interface language | hl |
hl in the URL, or hl to override |
uule |
Encoded location | uule, or location by name |
uule in the URL |
num |
Results per page | pages (1 to 10) |
num, read as depth in whole pages of ten, max 10 pages |
start |
Result offset | none; use pages |
refused |
tbs |
Time range and other filters | none | coming soon; refused today |
safe |
SafeSearch | none | coming soon; refused today |
tbm |
Vertical (news, images, …) | none | rejected; use POST /v1/monitor/google/news for news |
| (not a URL parameter) | Device | device: desktop, mobile, ios, android |
device as a field |
lr, cr, filter, others |
Various | none | refused |
gl and country
gl sets the country Google serves results for. In this API, country and gl take the same ISO 3166-1 alpha-2 codes, gl is accepted in either case (us or US), and the country must be one the endpoint supports:
curl https://api.answerline.dev/v1/countries?model=google \
-H "Authorization: Bearer $ANSWERLINE_API_KEY"
Send country or gl. Sending both with different values is a 400. In the URL shape, country is derived from the URL’s gl; pass country explicitly to override it or when the URL has no gl.
hl
hl is the interface language, as Google’s own codes: en, de, pt-BR. Setting it apart from the country captures, for example, English-language results pages in Germany. This endpoint takes hl; without it, the interface language is the country’s own, such as de for DE and pt-BR for BR. Google AI Mode and Google News take hl too. For multilingual AI answer monitoring, see multilingual AI answers.
Google’s Custom Search API documented a separate lr parameter to restrict documents to a language; a Google web search URL carrying lr is refused here.
uule and location
Country-level targeting is not enough when the results you care about depend on where the searcher is, such as the local pack. Google encodes a location in the uule parameter. This endpoint takes a city in two ways:
location: a Google canonical location name inCity,Region,Countryform, such as"New York,New York,United States". The valid names are those in Google Ads’ geotargets list, about 100,000 locations.uule: a pre-encoded UULE string, if you already build them.
They are mutually exclusive, with country alongside; Google AI Mode takes both the same way. For the local use case, see local rank tracking and state-level AI answers.
num, start and pages
Google’s num once set results per page, up to 100. Search Engine Roundtable reported in September 2025 that &num=100 had stopped working and that this broke most Google rank trackers. Results now come in pages of about ten.
This API models depth as pages:
- Standard shape:
pagesfrom 1 to 10 (default 1). Every organic result carriespage(1-indexed) andposition(1-indexed). - URL shape:
numis read as result depth, rounded up to whole pages of ten and capped at ten pages.num=20returns 2 pages;num=25returns 3;num=100returns 10.
start is the offset of the first result. According to Bright Data’s Google URL parameter reference (dated February 26, 2026; Google does not document it), start=0 is page 1, start=10 page 2 and start=20 page 3. This API refuses a url that carries start.
Ask for pages instead, up to the depth you need: one request returns the top N pages with page numbers already attached. See rank tracking with batches and Google rank tracking API.
device
Google serves separate desktop and mobile results pages, so capture the one your audience sees. Set device to desktop, mobile, ios or android.
tbs
tbs carries Google’s search tools filters, most often time ranges. Date filtering through tbs in the URL shape is coming; today a URL carrying tbs is refused with a 400. Google does not document its values. Bright Data’s reference lists:
tbs value |
Filter |
|---|---|
qdr:h |
Past hour |
qdr:d |
Past 24 hours |
qdr:w |
Past week |
qdr:m |
Past month |
qdr:y |
Past year |
cdr:1,cd_min:01/01/2025,cd_max:12/31/2025 |
Custom date range |
For date filtering today, use the documented alternative: Google’s Refine Google searches help page documents before: and after: operators, which go in query (or in the URL’s q). See Google search operators.
safe
safe controls SafeSearch filtering. Google’s Custom Search reference defined active (filter) and off (default) for its API, and Bright Data’s reference lists the same values for web search URLs. SafeSearch filtering on this endpoint is coming; today a URL carrying safe is refused with a 400.
Validation rules
The request schema allows no unknown fields. These cases are rejected:
| Request | Why |
|---|---|
Neither query + country/gl nor url |
One of the three shapes is required |
query and url together |
Mutually exclusive |
url with pages |
The URL owns that value |
location and uule together, or either with url |
Mutually exclusive |
country and gl with different values |
Conflicting geography |
A country not listed by GET /v1/countries?model=google |
Unsupported |
A url that is not a Google web search URL with path /search and a non-empty q |
Not a search URL |
A url containing tbm |
Verticals have their own endpoint |
A url with any parameter other than q, gl, num, hl and uule |
Not served yet |
pages outside 1 to 10, query longer than 10,000 characters |
Schema limits |
Any field not in the schema (for example num or start at the top level) |
additionalProperties is false |
A synchronous call returns HTTP 400 with the validation error shape from the OpenAPI document:
{
"success": false,
"error": "Request validation failed",
"details": [{ "field": "pages", "message": "..." }]
}
For async tasks, validation failures return 422. Retry logic should never retry a 400 or 422 unchanged; see API errors and retries.
Cost
At the Starter tier, a Google Search request costs $0.00103, each page after the first adds $0.00071, and include.aioverview adds $0.00071 once when Google shows an overview; include.paaAioverview is free. An accepted request reserves the maximum cost and is charged for the pages returned; a failed request is not charged.
| Request | Price |
|---|---|
query + country, 1 page |
$0.00103 |
pages: 3 |
$0.00245 |
pages: 10 |
$0.00742 |
1 page + include.aioverview |
$0.00174 |
pages: 3 + include.aioverview |
$0.00316 |
url with num=30 (3 pages) |
$0.00245 |
See /docs/credits and cost planning.
Examples
Python: one keyword across markets
This uses requests and async tasks.
import os
import requests
API = "https://api.answerline.dev"
HEADERS = {"Authorization": f"Bearer {os.environ['ANSWERLINE_API_KEY']}"}
countries = ["US", "DE", "GB"]
tasks = [
{
"taskType": "GOOGLE",
"payload": {"query": "project management software", "country": country, "pages": 2},
}
for country in countries
]
r = requests.post(f"{API}/v1/async/task/batch", json=tasks, headers=HEADERS, timeout=60)
r.raise_for_status()
for task, item in zip(tasks, r.json()["results"]):
print(task["payload"]["country"], item["success"])
Three tasks at $0.00174 each ($0.00103 + $0.00071 for the second page) cost $0.00522. Collect results with GET /v1/async/task/{taskId} or a webhook; see async tasks.
TypeScript: the URL shape with a date operator
const params = new URLSearchParams({ q: "\"acme analytics\" after:2026-09-01", gl: "gb", num: "20" });
const res = await fetch("https://api.answerline.dev/v1/monitor/google", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.ANSWERLINE_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ url: `https://www.google.co.uk/search?${params}` }),
});
if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
const { result } = await res.json();
for (const o of result.organicResults ?? []) console.log(o.page, o.position, o.date, o.link);
This is two pages: $0.00103 + $0.00071 for the second page = $0.00174.
Validate before sending
Catching shape errors client-side saves a round trip:
def check(body: dict) -> None:
if "url" in body:
clash = {"query", "pages", "location", "uule"} & body.keys()
if clash:
raise ValueError(f"url cannot be combined with {sorted(clash)}")
elif "query" not in body or not ({"country", "gl"} & body.keys()):
raise ValueError("send query with country or gl, or send url")
if "location" in body and "uule" in body:
raise ValueError("send location or uule, not both")
if "country" in body and "gl" in body and body["country"].upper() != body["gl"].upper():
raise ValueError("country and gl differ")
if not 1 <= body.get("pages", 1) <= 10:
raise ValueError("pages must be 1 to 10")
Pitfalls
- Sending
numorstartas top-level fields. At the top level they are unknown fields and fail validation;numbelongs insideurl, andstartis not served. - Assuming
hlsets the country. It sets the interface language; results followcountry/gl. - Planning city targeting around free text.
locationmust match a Google geotarget name; free text like “NYC” is not one. - Undocumented parameters as a long-term dependency.
startandnumare not documented by Google, andnum=100already changed once. Preferpagesand thebefore:/after:operators where they fit.
Related: Google Custom Search JSON API alternatives, Bing Search API alternatives and what is a SERP.
Full request and response fields are on the Google Search engine page.
Questions
What is the difference between gl and hl in Google search?
gl sets the country the results are for, and hl sets the interface language. In this API, country or gl sets the result geography with an ISO 3166-1 alpha-2 code. Google Search also takes hl; without it, the interface language is the country's own, such as de for DE.
Does Google's num=100 parameter still work?
Search Engine Roundtable reported in September 2025 that num=100 stopped working. This API reads num in a search URL as result depth, rounded up to whole pages of ten and capped at ten pages, and returns those pages; in the standard request shape you set pages from 1 to 10.
How do I target a city instead of a country?
Send location, a Google canonical name such as New York,New York,United States, or a pre-encoded uule, together with country.
Which parameter combinations return a 400 error?
query together with url; url together with pages; country and gl with different values; location together with uule; location or uule together with url; a url that is not a Google web search URL with a non-empty q, or that carries any parameter other than q, gl, num, hl and uule (start, tbs, safe, lr, tbm and the rest are not served yet); and any field not in the schema.
Which request fields change what a Google Search request costs?
Two of them. pages adds $0.00071 for each page after the first, and include.aioverview adds $0.00071 once when Google shows an overview; include.paaAioverview is free. country and gl change targeting at no extra cost, and