Rainbows form when sunlight is refracted, reflected, and dispersed inside water droplets ...
\n" }, "sources": [ { "url": "https://scijinks.gov/rainbow/", "title": "How Rainbows Form, NOAA SciJinks", "snippet": "...", "attribution": "NOAA SciJinks", "pub_date": 0 } ], "tool_data": { "shopping": [], "ads": [], "search_queries": [] }, "stream_bytes": 1422, "upstream_latency": "2.318s", "pow_inflight": 0 } ``` Message Envelope Fields [#message-envelope-fields] | Field | Type | Description | | ------------------------------------------ | ------ | --------------------------------------------------------------- | | `data.message.id` | string | Unique identifier for this assistant message | | `data.message.status` | string | Message status. `"finished_successfully"` for a complete reply | | `data.message.content.parts[0]` | string | The assistant's reply text with inline citation markers removed | | `data.message.metadata.model_slug` | string | Model that produced the reply (e.g., `"gpt-5"`) | | `data.message.metadata.finish_details` | object | Stop reason, e.g., `{ "type": "stop" }` | | `data.message.metadata.content_references` | array | Inline citations. Absent when the model did not cite anything | | `data.conversation_id` | string | Identifier for the conversation this reply belongs to | content_references[] [#content_references] | Field | Type | Description | | ----------- | --------- | -------------------------------------------------------- | | `type` | string | Reference type (e.g., `"webpage"`, `"image"`) | | `title` | string | Citation title as shown in the reply | | `safe_urls` | string\[] | Resolved URLs for the citation. Present on web citations | *** Render your own product cards [#render-your-own-product-cards] When ChatGPT returns shopping results, `output.markdown` renders inline products as links by default. If you render your own product cards, add `shopping_placeholders=1` and each inline product becomes a `{{shopping:
.png` |
| `flights[].travel_class` | string | `"Economy"` / `"Premium economy"` / `"Business"` / `"First"` |
| `flights[].flight_number` | string | IATA code + flight number (e.g. `B6 323`) |
| `flights[].legroom` | string | Human-readable legroom |
| `flights[].extensions` | string\[] | Amenity strings (`"Wi-Fi for a fee"`, `"In-seat power"`, …) |
| `flights[].overnight` | bool | `true` when the segment crosses midnight |
| `layovers[]` | array | Stops between segments |
| `layovers[].duration` | integer | Layover duration in minutes |
| `layovers[].name` / `.id` | string | Layover airport |
| `total_duration` | integer | End-to-end duration in minutes, including layovers |
| `carbon_emissions` | object | `{ this_flight, typical_for_this_route, difference_percent }` in grams |
| `price` | integer | Total price in the requested currency |
| `type` | string | `"One way"` or `"Round trip"` |
| `airline_logo` | string | Representative logo for the itinerary |
| `booking_token` | string | Opaque token identifying the upstream booking page |
| `departure_token` | string | Per-flight short token, when Google emits one |
`carbon_emissions.difference_percent` may be negative when a flight emits less than the route average. `this_flight` and `typical_for_this_route` are in grams.
price_insights [#price_insights]
```json
{
"lowest_price": 149,
"price_level": "typical",
"typical_price_range": [90, 205],
"price_history": [
[1771218000000, 169],
[1771304400000, 169]
]
}
```
| Field | Type | Description |
| --------------------- | ------- | ------------------------------------------------------------------ |
| `lowest_price` | integer | Cheapest fare in the response |
| `price_level` | string | `"low"`, `"typical"`, or `"high"` per Google's classification |
| `typical_price_range` | int\[2] | `[low, high]` for this route |
| `price_history` | array | `[unix_ms, price]` pairs. Empty when Google doesn't ship a history |
airports [#airports]
Top-level airport reference list. One entry per search (usually a single object containing `departure[]` and `arrival[]`).
```json
[
{
"departure": [
{
"airport": { "id": "JFK", "name": "John F. Kennedy International Airport" },
"city": "New York",
"country": "United States",
"country_code": "US",
"image": "https://encrypted-tbn1.gstatic.com/images?q=tbn:...",
"thumbnail": "https://encrypted-tbn1.gstatic.com/images?q=tbn:...",
"latitude": 40.6397,
"longitude": -73.7789
}
],
"arrival": [ { "airport": { "id": "LAX", "name": "Los Angeles International Airport" }, "city": "Los Angeles", "country": "United States", "country_code": "US", "image": "https://encrypted-tbn1.gstatic.com/images?q=tbn:...", "thumbnail": "https://encrypted-tbn1.gstatic.com/images?q=tbn:...", "latitude": 33.9425, "longitude": -118.4081 } ]
}
]
```
**Schema note:** each `departure[]` / `arrival[]` entry nests the IATA code and airport name under `airport` (`airport.id`, `airport.name`). City, country, `country_code`, coordinates, and the `image` / `thumbnail` URLs sit at the top level of the entry.
***
Sort Modes (sort_by) [#sort-modes-sort_by]
| Value | Meaning |
| ------------- | ----------------------------- |
| `1` (default) | Top. Google's default ranking |
| `2` | Price ascending |
| `3` | Departure time ascending |
| `4` | Arrival time ascending |
| `5` | Total duration ascending |
| `6` | Carbon emissions ascending |
```bash
# Lowest emissions first
curl "https://api.scrape.do/plugin/google/flights?departure_id=JFK&arrival_id=LAX&outbound_date=2026-12-15&sort_by=6&token=$TOKEN"
```
***
Stop Filters (stops) [#stop-filters-stops]
| Value | Meaning |
| ------------- | --------------- |
| `0` (default) | Any |
| `1` | Nonstop only |
| `2` | At most 1 stop |
| `3` | At most 2 stops |
***
Notes [#notes]
* **Listing data only.** `booking_token` to fare detail / baggage / seat map expansion is not yet exposed.
* **Dates must be today or in the future.** `outbound_date` and `return_date` for past days return `400`. If a date near today is rejected, move it forward one day.
* **Travel class changes the fares returned.** Business and first-class searches return their own cabin prices, not economy fares with a label changed afterward.
* **Sorting changes the result split.** With default `sort_by=1`, Google returns ranked `best_flights` plus `other_flights`. With `sort_by=2` through `6`, the ordered list arrives under `other_flights` and `best_flights` is empty.
* **Prices are integer units** in the requested currency; sub-unit precision is dropped.
* **Live prices drift.** Fares can change between requests; expect small variance on repeated calls.
* **Airline logos** are synthesised from the IATA code and may 404 for obscure carriers.
* **Multi-city** (`type=3`) returns `400` until support lands.
***
Error Handling [#error-handling]
```json
{ "error": "human readable error" }
```
Common Error Codes [#common-error-codes]
| Status | Error | Description |
| ------ | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| `400` | `token is required` | Missing authentication token |
| `400` | `departure_id is required (IATA or comma-separated IATAs)` | Missing origin |
| `400` | `arrival_id is required (IATA or comma-separated IATAs)` | Missing destination |
| `400` | `outbound_date is required (format: YYYY-MM-DD)` | Missing or malformed outbound date |
| `400` | `return_date is required for round-trip (format: YYYY-MM-DD)` | `type=1` without `return_date` |
| `400` | `outbound_date is in the past — it must be today or a future date (YYYY-MM-DD)` | `outbound_date` is a past date |
| `400` | `return_date is in the past — it must be today or a future date (YYYY-MM-DD)` | `return_date` is a past date |
| `400` | `return_date must be on or after outbound_date` | `return_date` earlier than `outbound_date` |
| `400` | `multi-city (type=3) is not yet supported — use one-way or round-trip` | `type=3` requested |
| `400` | `type must be 1 (round-trip), 2 (one-way), or 3 (multi-city)` | Invalid `type` |
| `400` | `travel_class must be 1 (economy), 2 (premium economy), 3 (business), or 4 (first)` | Invalid `travel_class` |
| `400` | `stops must be 0 (any), 1 (nonstop), 2 (≤1), or 3 (≤2)` | Invalid `stops` |
| `400` | `sort_by must be 1-6 (1=top, 2=price, 3=dep, 4=arr, 5=duration, 6=emissions)` | Invalid `sort_by` |
| `400` | `include_airlines and exclude_airlines are mutually exclusive` | Both supplied |
| `400` | `max_price is not yet supported` | `max_price`, `max_duration`, or `bags` passed |
| `400` | `invalid search parameters` | Google rejected the search, such as an unknown IATA code or a date it considers in the past |
| `502` | `request failed` | Transient upstream failure. Retry |
| `502` | `unexpected response` | Non-200 from Google. Retry |
| `502` | `upstream error` | Google returned an incomplete, empty, or unreadable response. Safe to retry |
| `500` | `internal server error` | Unexpected internal error. Retry; if it persists, contact support |
***
# Google Food API (/documentation/google-scraper-api/food)
import { Cards, Card } from 'fumadocs-ui/components/card';
The Google Food API returns the online-ordering providers shown on a restaurant's Google "Order online" page. One request returns both the pickup and delivery lists as structured JSON, so you do not need to render the page or make separate requests for each fulfillment mode.
**Credit Usage:** Each successful request costs **10 credits**. For bulk processing, use the [Async API with plugins](/documentation/async-api/plugins/).
Key Features [#key-features]
* **Pickup and delivery in one response**: `pickup[]` and `delivery[]` are returned together, in Google's display order.
* **Ordering deep links**: each provider includes its Google Food ordering URL when available.
* **Provider metadata**: names, logos, ETA labels, fee labels, and preferred-by-business flags are parsed into clean fields.
* **Google entity IDs**: pass a restaurant `mid` as `/g/...`, `/m/...`, or a bare Google entity ID such as `11x38c65wl`.
* **Localized availability**: use `gl`, `hl`, and `google_domain` to request regional provider lists.
* **No browser rendering**: Scrape.do fetches and parses the provider shelf directly.
***
Endpoint [#endpoint]
```
GET https://api.scrape.do/plugin/google/food
```
***
Request Parameters [#request-parameters]
Required [#required]
| Parameter | Type | Description |
| --------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| `token` | string | Your Scrape.do API authentication token |
| `mid` | string | Restaurant Google entity ID, for example `/g/11x38c65wl`. Bare IDs such as `11x38c65wl` are accepted and normalized to `/g/11x38c65wl` |
Localization [#localization]
| Parameter | Type | Default | Description |
| --------------- | ------ | ------------ | --------------------------------------------------- |
| `gl` | string | `us` | Country code, for example `us`, `gb`, `ca`, or `au` |
| `hl` | string | `en` | Language code, for example `en`, `es`, or `fr` |
| `google_domain` | string | `google.com` | Google domain to query, for example `google.co.uk` |
***
Example Usage [#example-usage]
Restaurant Providers [#restaurant-providers]
```bash
curl "https://api.scrape.do/plugin/google/food?mid=/g/11x38c65wl&gl=us&token=$TOKEN"
```
Bare Entity ID [#bare-entity-id]
```bash
curl "https://api.scrape.do/plugin/google/food?mid=11kskr5rtl&gl=us&token=$TOKEN"
```
***
Response [#response]
Top-Level Shape [#top-level-shape]
```json
{
"search_parameters": { ... },
"restaurant": { "name": "Fortuna NYC" },
"pickup": [ ... ],
"delivery": [ ... ]
}
```
`pickup` and `delivery` are always arrays. When Google does not show providers for a fulfillment mode, that field is returned as an empty array instead of `null`.
search_parameters [#search_parameters]
```json
{
"engine": "google_food",
"type": "choose_provider",
"mid": "/g/11x38c65wl",
"gl": "us",
"hl": "en"
}
```
restaurant [#restaurant]
Restaurant metadata exposed by Google for the order-online page.
```json
{ "name": "Fortuna NYC" }
```
| Field | Type | Description |
| ------ | ------ | -------------------------------------- |
| `name` | string | Restaurant name as displayed by Google |
pickup[] / delivery[] [#pickup--delivery]
Each entry is one ordering provider.
```json
{
"name": "Online Ordering by DoorDash",
"order_url": "https://order.online/store/-33601791/?delivery=true&hideModal=true&utm_source=gfo&rwg_token=AE37R_...",
"image": "https://lh3.googleusercontent.com/lHbUaJLjsUBai5bzcmTAVueYBjuXwDO_...",
"time": "Delivers in 29 min",
"fees": ["Service fee 10%", "Delivery fee $3.99"],
"preferred_by_business": false
}
```
| Field | Type | Description |
| ----------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | string | Provider name as displayed, for example `Uber Eats`, `Sauce`, or `Online Ordering by DoorDash` |
| `order_url` | string | Provider ordering deep link. Pickup entries usually include `pickup=true`; delivery entries usually include `delivery=true` or a delivery path |
| `image` | string | Provider logo URL. Omitted when Google does not supply one |
| `time` | string | ETA label, for example `Ready in 11 min` or `Delivers in 30-45 min`. Omitted when unavailable |
| `fees` | string\[] | Fee labels shown under the provider, for example `["No fee"]` or `["Delivery fee $1.99", "Service fee may apply"]`. Omitted when none are shown |
| `preferred_by_business` | boolean | `true` when Google marks the provider as preferred by the business. Omitted or `false` otherwise |
**Ordering links are short-lived.** `order_url` values can contain single-use Google Food tokens. Use them promptly instead of caching them long term.
***
Notes [#notes]
* One request returns both pickup and delivery provider lists.
* Provider order, ETAs, fees, and availability can vary by `gl`, `hl`, restaurant, and Google's current inventory.
* Empty provider lists usually mean Google does not show online ordering for that mode, the `mid` is invalid, or the restaurant is temporarily unavailable for ordering.
* `mid` must identify a Google entity with a `/g/` or `/m/` style ID after normalization.
***
Error Handling [#error-handling]
```json
{ "error": "error message" }
```
Common Errors [#common-errors]
| Status | Error | Description |
| ------ | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `400` | `token is required` | Missing API token |
| `400` | `mid is required (e.g. /g/11x38c65wl)` | Missing restaurant entity ID |
| `400` | `mid is not a valid Google entity id (e.g. /g/11x38c65wl)` | `mid` is not a `/g/` or `/m/` entity ID after normalization |
| `400` | `invalid google_domain` | Unrecognized Google domain |
| `502` | `request failed` | Upstream fetch failed. Retry the request |
| `502` | `failed to parse food providers` | Google returned no provider list for the supplied restaurant or page shape. Verify the `mid` and retry |
| `500` | `internal server error` | Transient server error. Retry the request |
***
# Gemini Scraper API (/documentation/google-scraper-api/gemini)
The Gemini API takes a single text prompt and returns Gemini's reply from `gemini.google.com` as structured JSON. One GET request, no Google account, no cookies, and no browser automation on your side.
```bash
curl "https://api.scrape.do/plugin/gemini/chat?token=$TOKEN&q=ping"
```
**Credit Usage:** Each successful request costs **25 credits**. Failed requests are not charged, and there is no render fee.
Key Features [#key-features]
* **One-shot prompt-to-reply**: send a prompt, get the full reply back in a single HTTP call.
* **Markdown-clean output**: `output.text` is the reply as Markdown — headings, tables, lists, and code blocks are preserved, while frontend-only placeholders (inline image widgets, card references) are stripped.
* **Grounding sources**: when Gemini grounds an answer on the web, the external links it used come back as a flat `sources[]` array.
* **Prompt language sets reply language**: Gemini answers in the language your prompt is written in, so ask in German to get a German reply.
* **Stateless from your side**: every call is an independent, fresh conversation. No login, no token refresh, nothing to keep between calls.
* **No render fee**: the per-call price covers everything; you do not pay a rendering surcharge.
***
Endpoint [#endpoint]
```
GET https://api.scrape.do/plugin/gemini/chat
```
Basic Example [#basic-example]
```bash
curl "https://api.scrape.do/plugin/gemini/chat?token=$TOKEN&q=Explain+quantum+entanglement+in+one+sentence"
```
Request Parameters [#request-parameters]
Required [#required]
| Parameter | Type | Description |
| --------- | ------ | ----------------------------------------------------------------------------------------- |
| `token` | string | Your Scrape.do API authentication token |
| `q` | string | Prompt text to send to Gemini. Maximum **4000 characters**. URL-encode multi-word prompts |
Parameter names are case-insensitive, so `Q` works the same as `q`.
Example Requests [#example-requests]
```bash
# Basic prompt
curl "https://api.scrape.do/plugin/gemini/chat?q=hello&token=$TOKEN"
# Multi-word prompt (URL-encode)
curl --get "https://api.scrape.do/plugin/gemini/chat" \
--data-urlencode "q=Write a haiku about the ocean" \
--data-urlencode "token=$TOKEN"
# German prompt gets a German reply
curl --get "https://api.scrape.do/plugin/gemini/chat" \
--data-urlencode "q=Was sind die wichtigsten Nachrichten heute" \
--data-urlencode "token=$TOKEN"
# Prompt that tends to produce grounded answers with sources
curl --get "https://api.scrape.do/plugin/gemini/chat" \
--data-urlencode "q=Latest developments in AI with sources" \
--data-urlencode "token=$TOKEN"
```
***
Response [#response]
Top-Level Structure [#top-level-structure]
```json
{
"prompt": "Latest developments in AI with sources",
"output": {
"text": "AI development has shifted toward autonomous agents...\n\n## 1. Agentic AI\n..."
},
"sources": [
"https://openai.com/index/...",
"https://www.whitehouse.gov/presidential-actions/2026/06/..."
],
"model": "3.5 Flash"
}
```
Fields [#fields]
| Field | Type | Description |
| ------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `prompt` | string | Echo of the prompt you submitted |
| `output.text` | string | The reply as Markdown — headings, tables, lists, and code blocks are preserved for rich answers (comparisons, recipes, product round-ups). Frontend-only placeholders such as inline image widgets and card references are removed |
| `sources` | string\[] | External links Gemini grounded the answer on, deduplicated and in the order they appear. Best-effort; omitted entirely when the answer was not grounded on external pages |
| `model` | string | Label of the model that produced the reply, such as `"3.5 Flash"`. Omitted when Gemini does not report it |
`prompt` and `output.text` are always present on a `200`. `sources` and `model` are omitted rather than returned empty, so treat both as optional in your parser.
Example [#example]
```json
{
"prompt": "Explain quantum entanglement in one sentence",
"output": {
"text": "Quantum entanglement is a phenomenon where two or more particles share a single quantum state, so measuring one instantly determines the corresponding property of the other, no matter how far apart they are."
},
"model": "3.5 Flash"
}
```
A grounded answer additionally carries `sources`:
```json
{
"prompt": "Who won the last Formula 1 race and where",
"output": {
"text": "## Latest Race Result\n\n| Position | Driver | Team |\n|---|---|---|\n| 1 | ... | ... |\n\nThe race was held at ..."
},
"sources": [
"https://www.formula1.com/en/results/...",
"https://www.autosport.com/f1/news/..."
],
"model": "3.5 Flash"
}
```
***
Notes [#notes]
* **Prompt limit is 4000 characters.** Longer prompts are rejected before the call runs, so no credits are spent on them.
* **No multi-turn state.** One call returns one reply; to follow up, include the prior context inside the new `q`.
* **Reply language follows the prompt.** The reply is written in the language of your prompt. Grounding sources may still be a globally mixed set, because Gemini decides which pages to cite.
* **Markdown, not HTML.** `output.text` is Markdown. Render it with any Markdown renderer; there is no separate HTML field.
* **`sources` is best-effort.** It collects the external links present in the reply payload and filters out platform infrastructure links, capped at 30 entries. An answer with no `sources` field is a normal answer that was not web-grounded, not a failure.
* **Response variance.** Gemini A/B-tests its answers, so the same prompt can return different wording, formatting, or a different `model` label across calls. Do not treat exact text as stable.
* **Typical latency** is a few seconds and depends on prompt complexity and reply length. The endpoint waits for the complete reply and does not stream partial output.
* **Per-call timeout is 45 seconds.** If you hit it, shorten unusually long prompts or retry.
* **Retry on `502`.** Both `502` bodies are transient conditions; a retry a few seconds later normally succeeds and you are not charged for the failed call.
***
Error Responses [#error-responses]
| Status | Body | Cause |
| ------ | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| `400` | `{ "error": "token is required" }` | Missing `token` parameter |
| `400` | `{ "error": "q (prompt) is required" }` | Missing or empty `q` parameter |
| `400` | `{ "error": "q is too long (max 4000 characters)", "message": "..." }` | Prompt exceeds the 4000-character limit |
| `502` | `{ "error": "no warm session available", "message": "..." }` | Capacity is being prepared; retry shortly |
| `502` | `{ "error": "failed to get a response from Gemini", "message": "..." }` | Upstream did not return a usable reply; retry, and contact support if it persists |
| `500` | `{ "error": "internal server error" }` | Unexpected internal failure |
# Google Hotels API (/documentation/google-scraper-api/hotels)
import { Cards, Card } from 'fumadocs-ui/components/card';
The Google Hotels API returns structured property data from Google Hotels in two steps:
1. **Listing** — search hotels and get property metadata (name, photos, GPS, ratings, amenities, per-night price) plus a `detail_token` for each property.
2. **Detail** — pass that `detail_token` to fetch the full per-vendor offer shelf (`Booking.com`, `Hotels.com`, `Agoda`, …) with live `rate_per_night` and `total_rate`.
**Credit Usage:** Each successful request costs **10 credits**. For bulk processing, use the [Async API with plugins](/documentation/async-api/plugins/).
Key Features [#key-features]
* **Two-step flow** — listing call returns properties + `detail_token`; detail call returns the vendor pricing shelf.
* **Structured JSON** — each property has name, description, GPS, hotel class, rating, reviews, amenities, images, per-night price, total rate, check-in/check-out times, nearby places, and a star-by-star ratings histogram.
* **Per-vendor pricing** — the detail endpoint returns `booking_sources[]` with sponsored ("Featured options") and organic ("All options") entries unified into one ranked list.
* **Currency localization** — per-request `currency` (`USD`, `EUR`, `GBP`, `TRY`, …). Combine with `hl` and `gl` for full regional search.
* **Sort & filter** — lowest price / highest rating / most reviewed; price range, minimum rating, hotel class, amenities, free cancellation, eco-certified, special offers.
* **Vacation rentals mode** — `property_types=12` switches to vacation rentals where available.
* **Pagination** — token-based with `next_page_token`.
* **No blocks or CAPTCHAs** — handled automatically.
***
Listing Endpoint [#listing-endpoint]
```
GET https://api.scrape.do/plugin/google/hotels
```
Basic Example [#basic-example]
```bash
curl "https://api.scrape.do/plugin/google/hotels?token=$TOKEN&q=Bali+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03"
```
**Occupancy parameters are not supported.** Results always reflect the default occupancy of **2 adults, 0 children**.
Query Guidelines [#query-guidelines]
Use simple location-based queries for best results:
| Recommended | Avoid |
| ------------------ | --------------------------------------- |
| `Paris hotels` | `Paris luxury hotels near Eiffel Tower` |
| `Tokyo hotels` | `Tokyo traditional ryokan hotels` |
| `Edinburgh hotels` | `Edinburgh Royal Mile hotels` |
Overly specific queries can return empty results. Simple `" hotels"` queries have the highest success rate.
Request Parameters [#request-parameters]
Required [#required]
| Parameter | Type | Description |
| ---------------- | ------ | ---------------------------------------------------------------------- |
| `token` | string | Your Scrape.do API authentication token |
| `q` | string | Hotel search query. Use simple `" hotels"` form for best results |
| `check_in_date` | string | Check-in date in `YYYY-MM-DD` |
| `check_out_date` | string | Check-out date in `YYYY-MM-DD`. Must be after `check_in_date` |
Localization [#localization]
| Parameter | Type | Default | Description |
| ---------- | ------ | ------- | -------------------------------------- |
| `hl` | string | `en` | Language code |
| `gl` | string | `us` | Country code |
| `currency` | string | `USD` | Currency code (`EUR`, `GBP`, `TRY`, …) |
Sorting [#sorting]
| Parameter | Type | Default | Description |
| --------- | ------- | --------------- | -------------------------------------------------------------- |
| `sort_by` | integer | `0` (relevance) | `3` = lowest price, `8` = highest rating, `13` = most reviewed |
Filters [#filters]
| Parameter | Type | Description |
| ------------------- | ------- | ---------------------------------------------------------------------------- |
| `min_price` | integer | Minimum nightly price (≥ 0) in the requested currency |
| `max_price` | integer | Maximum nightly price (≥ 0 and ≥ `min_price`) in the requested currency |
| `rating` | integer | Minimum rating: `7` (3.5+), `8` (4.0+), `9` (4.5+) |
| `hotel_class` | string | Comma-separated star ratings (`4,5` for 4★ and 5★). Each value 2–5 |
| `amenities` | string | Comma-separated amenity codes (see [Amenity Codes](#amenity-codes)) |
| `property_types` | string | Comma-separated property type codes. `12` = vacation rentals (switches mode) |
| `free_cancellation` | boolean | Only properties with free cancellation. Accepts `true` or `1` |
| `eco_certified` | boolean | Only eco-certified properties |
| `special_offers` | boolean | Only properties with special offers |
Pagination & Page Size [#pagination--page-size]
| Parameter | Type | Default | Description |
| ----------------- | ------- | ------- | ------------------------------------------------------------------------------ |
| `next_page_token` | string | — | From the previous response's `pagination.next_page_token`. Pass back unchanged |
| `limit` | integer | `20` | Maximum properties to return on the page. Range: 1–20 |
More Examples [#more-examples]
```bash
# Turkish locale + TRY currency
curl "https://api.scrape.do/plugin/google/hotels?q=Istanbul+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03&hl=tr&gl=tr¤cy=TRY&token=$TOKEN"
# Cheapest first within £100–£300
curl "https://api.scrape.do/plugin/google/hotels?q=London+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03&sort_by=3&min_price=100&max_price=300¤cy=GBP&token=$TOKEN"
# 4–5 star, free cancellation
curl "https://api.scrape.do/plugin/google/hotels?q=Dubai+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03&hotel_class=4,5&free_cancellation=true&token=$TOKEN"
# Eco-certified, 4.5+ rating, with pool
curl "https://api.scrape.do/plugin/google/hotels?q=Maldives+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03&rating=9&eco_certified=true&amenities=22&token=$TOKEN"
# Vacation rentals
curl "https://api.scrape.do/plugin/google/hotels?q=Santorini+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03&property_types=12&token=$TOKEN"
# Page 2 (token from a previous response)
curl "https://api.scrape.do/plugin/google/hotels?q=Paris+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03&next_page_token=CgsI...&token=$TOKEN"
```
Response [#response]
Top-Level Structure [#top-level-structure]
```json
{
"search_parameters": { ... },
"search_information": { "total_results": 18 },
"properties": [ ... ],
"ads": [ ... ],
"brands": [ ... ],
"pagination": { ... }
}
```
properties[] [#properties]
```json
{
"type": "hotel",
"name": "The Anvaya Beach Resort Bali",
"description": "Sophisticated resort offering 8 pools, 2 restaurants & a spa, plus a private beach & a kids' club.",
"gps_coordinates": { "latitude": -8.7322535, "longitude": 115.1659877 },
"hotel_class": "5-star hotel",
"extracted_hotel_class": 5,
"overall_rating": 4.7,
"reviews": 14342,
"amenities": [6, 29, 16, 22, 2, 8, 26, 5, 4, 23, 24, 14, 1, 31, 27, 7, 11],
"images": [
{ "thumbnail": "https://lh3.googleusercontent.com/...", "original_image": "https://lh3.googleusercontent.com/..." }
],
"detail_token": "Q2hrSXZfSHlpTktIcGY2eUFSb01MMmN2TVhvemRHSm5aM0J6RUFFfDB4MTRiZTZkN2IxYjkwM2VjMTowYzZkMjFhMzRiMDgwNGNkZQ",
"check_in_time": "3:00 PM",
"check_out_time": "12:00 PM",
"ratings": [
{ "stars": 5, "percent": 91, "count": 4546 },
{ "stars": 4, "percent": 7, "count": 363 },
{ "stars": 3, "percent": 1, "count": 96 }
],
"reviews_breakdown": [
{ "name": "Cleanliness", "sentiment": 0.42, "total_mentioned": 88, "positive": 71, "negative": 8, "neutral": 9 }
],
"price": { "display": "USD 187", "amount": 187.42, "currency": "USD" },
"total_rate": { "display": "USD 374", "amount": 374, "currency": "USD" },
"nearby_places": [
{ "name": "Beach", "transportations": [{ "type": 0, "duration": "10 min" }] }
]
}
```
| Field | Type | Always present | Description |
| ----------------------- | --------- | :------------: | ----------------------------------------------------------------------------------------- |
| `type` | string | yes | `"hotel"` or `"vacation rental"` |
| `name` | string | yes | Property name |
| `description` | string | | Short description |
| `gps_coordinates` | object | yes | `{ latitude, longitude }` |
| `hotel_class` | string | | Star rating label (e.g. `"5-star hotel"`) |
| `extracted_hotel_class` | int | | Numeric star rating |
| `overall_rating` | float | | Guest rating out of 5 |
| `reviews` | int | | Number of guest reviews |
| `amenities` | int\[] | yes | Amenity codes (see [Amenity Codes](#amenity-codes)). Empty array when none |
| `images` | object\[] | yes | `[{ thumbnail, original_image }]`. Empty array when none |
| `detail_token` | string | yes | Opaque token. Pass to the [detail endpoint](#detail-endpoint) to fetch per-vendor pricing |
| `check_in_time` | string | | Property check-in clock time (e.g. `"3:00 PM"`) |
| `check_out_time` | string | | Property check-out clock time |
| `ratings` | object\[] | | 5→1 star histogram entries: `{ stars, percent, count }` |
| `reviews_breakdown` | object\[] | | Guest sentiment by category (`Cleanliness`, `Service`, …) |
| `price` | object | | Per-night rate in the requested currency. See [Price](#price) |
| `total_rate` | object | | Total stay cost across the date range. See [Price](#price) |
| `nearby_places` | object\[] | | Points of interest with transit-mode durations |
Price [#price]
| Field | Type | Description |
| ---------- | ------ | -------------------------------------------------------------- |
| `display` | string | Formatted price as returned (e.g., `"USD 187"`, `"TRY 1,895"`) |
| `amount` | float | Numeric value (e.g., `187.42`) |
| `currency` | string | ISO 4217 code reflecting the request's `currency` |
ads[] [#ads]
Sponsored hotel results attached to the listing, when present. Each entry is flagged with `"sponsored": true`.
```json
{
"sponsored": true,
"name": "The LINQ - A Caesars Rewards Destination",
"source": "Booking.com",
"source_icon": "//www.gstatic.com/travel-hotels/branding/icon_184.png",
"click": "/aclk?sa=l&ai=...",
"gps_coordinates": { "latitude": 36.1206, "longitude": -115.1717 },
"hotel_class": 4,
"thumbnail": "//lh3.googleusercontent.com/proxy/...",
"reviews": 42322,
"amenities": [11, 23, 4, 24, 8, 29, 2],
"price": { "display": "$291", "amount": 290.81, "currency": "USD" },
"total_rate": { "display": "$582", "amount": 582, "currency": "USD" }
}
```
brands[] [#brands]
Hotel chain / brand index attached to the listing, when present.
```json
[
{ "id": 21, "name": "Ibis" },
{ "id": 61, "name": "Marriott Hotels & Resorts" }
]
```
pagination [#pagination]
Included when more pages exist. Omitted on the last page.
```json
{ "current_from": 1, "current_to": 20, "next_page_token": "CgsI..." }
```
Pages 2+ may overlap by up to 2 entries — dedupe across pages by `name` + `gps_coordinates`. The `detail_token` is **not** stable across pages, so the same property may receive a different `detail_token` on each page.
***
Detail Endpoint [#detail-endpoint]
```
GET https://api.scrape.do/plugin/google/hotels/detail
```
Returns the **per-vendor offer shelf** for a single property — each booking source's `rate_per_night`, `total_rate`, and quoted occupancy in the requested currency, with sponsored ("Featured options") and organic ("All options") entries unified into one ranked list.
Property metadata (name, photos, GPS, ratings, amenities) is **not** repeated in the detail response — it was already returned by the listing call. Merge client-side using the `detail_token` as the join key.
Two-Step Example [#two-step-example]
```bash
# Step 1 — listing
curl "https://api.scrape.do/plugin/google/hotels?token=$TOKEN&q=Bali+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03¤cy=USD"
# → response.properties[i].detail_token
# Step 2 — detail (per-vendor pricing for one property)
curl "https://api.scrape.do/plugin/google/hotels/detail?token=$TOKEN&detail_token=Q2hrSXZfSHlpTktIcGY2eUFSb01MMmN2TVhvemRHSm5aM0J6RUFFfDB4MTRiZTZkN2IxYjkwM2VjMTowYzZkMjFhMzRiMDgwNGNkZQ&check_in_date=2026-05-01&check_out_date=2026-05-03¤cy=USD"
```
Use the **same** `check_in_date`, `check_out_date`, and `currency` in both calls.
Detail Parameters [#detail-parameters]
Required [#required-1]
| Parameter | Type | Description |
| ---------------- | ------ | ----------------------------------------------------------------------------- |
| `token` | string | Your Scrape.do API token |
| `detail_token` | string | From a listing response's `properties[].detail_token`. Pass it back unchanged |
| `check_in_date` | string | `YYYY-MM-DD` |
| `check_out_date` | string | `YYYY-MM-DD`. Must be after `check_in_date` |
Optional [#optional]
| Parameter | Type | Default | Description |
| ---------- | ------ | ------- | ---------------------- |
| `currency` | string | `USD` | ISO 4217 currency code |
| `gl` | string | `us` | Country code |
| `hl` | string | `en` | Language code |
Detail Response [#detail-response]
```json
{
"search_parameters": {
"engine": "google_hotels_detail",
"detail_token": "Q2hrSXZfSHlp...",
"check_in_date": "2026-05-01",
"check_out_date": "2026-05-03",
"currency": "USD",
"gl": "us",
"hl": "en"
},
"property": {
"detail_token": "Q2hrSXZfSHlp...",
"booking_sources": [
{
"sponsored": true,
"name": "Booking.com",
"id": 184,
"click": "/aclk?...",
"icon": "//www.gstatic.com/travel-hotels/branding/icon_184.png",
"rate_per_night": { "display": "$221", "amount": 221.04, "currency": "USD" },
"total_rate": { "display": "$442", "amount": 442.08, "currency": "USD" },
"num_guests": 2
},
{
"name": "Hotels.com",
"id": 1162912808,
"click": "/aclk?...",
"icon": "//www.gstatic.com/travel-hotels/branding/...",
"rate_per_night": { "display": "$227", "amount": 227.33, "currency": "USD" },
"total_rate": { "display": "$455", "amount": 454.67, "currency": "USD" },
"num_guests": 2
}
]
}
}
```
booking_sources[] [#booking_sources]
Each entry is one vendor's quote for the requested dates and currency. Sponsored entries appear first and carry `"sponsored": true`; organic entries follow.
| Field | Type | Always present | Description |
| ---------------- | ------- | :------------: | ------------------------------------------------------------------------- |
| `sponsored` | boolean | | `true` on paid placements ("Featured options"); omitted on organic offers |
| `name` | string | yes | Vendor brand (e.g. `"Booking.com"`, `"Hotels.com"`, `"Agoda"`) |
| `id` | integer | | Internal source identifier |
| `click` | string | | Click-through URL that redirects to the vendor's listing |
| `icon` | string | | Vendor brand icon URL |
| `rate_per_night` | object | | Per-night rate. See [Price](#price). Omitted when not quoted |
| `total_rate` | object | | Total stay cost. See [Price](#price). Omitted when not quoted |
| `num_guests` | integer | | Occupancy the rate is quoted for — always `2` when present |
`rate_per_night.currency` and `total_rate.currency` reflect the request's `currency`. Numeric `amount` values are normalized — thousands separators are stripped before parsing.
***
Amenity Codes [#amenity-codes]
Raw integer codes from Google's classification. Common values:
| Code | Amenity | Code | Amenity |
| ---- | --------------- | ---- | --------------------- |
| 1 | Fitness center | 16 | Outdoor pool |
| 2 | Bar | 17 | Pet-friendly |
| 4 | Restaurant | 20 | Indoor pool |
| 5 | Room service | 22 | Pool |
| 6 | Free breakfast | 23 | Air-conditioned |
| 7 | Kids' club | 24 | Kitchen / kitchenette |
| 8 | Spa | 25 | Smoke-free |
| 9 | Business center | 26 | Full-service laundry |
| 10 | Child-friendly | 27 | Free Wi-Fi |
| 11 | Accessible | 29 | Wi-Fi |
| 14 | Beach access | 31 | Airport shuttle |
| 15 | Parking | | |
**Codes may change without notice.** Raw integer values are forwarded as-is from Google. The same codes are returned in `properties[].amenities[]`, so they round-trip safely.
***
Property Types [#property-types]
| Value | When returned |
| ------------------- | ------------------------------------------------------------------------------ |
| `"hotel"` | Traditional hotel, resort, inn, or hostel |
| `"vacation rental"` | Vacation rental, apartment, or villa. Returned when `property_types=12` is set |
**Mode switching depends on inventory.** Some destinations continue to return `type: "hotel"` even when `property_types=12` is set if no vacation rentals are listed.
***
Error Handling [#error-handling]
```json
{ "error": "error_code", "message": "Human readable error message" }
```
Common Error Codes [#common-error-codes]
| Status | Error | Description |
| ------ | ------------------------------------------------- | --------------------------------------------- |
| `400` | `token is required` | Missing token |
| `400` | `q (hotel search query) is required` | Missing `q` |
| `400` | `check_in_date is required (format: YYYY-MM-DD)` | Missing or malformed `check_in_date` |
| `400` | `check_out_date is required (format: YYYY-MM-DD)` | Missing or malformed `check_out_date` |
| `400` | `check_out_date must be after check_in_date` | Date range inverted |
| `400` | `sort_by must be 3, 8, or 13` | Invalid `sort_by` |
| `400` | `min_price must be a non-negative integer` | Invalid `min_price` |
| `400` | `max_price must be a non-negative integer` | Invalid `max_price` |
| `400` | `min_price must be <= max_price` | Range inverted |
| `400` | `rating must be 7, 8, or 9` | Invalid `rating` |
| `400` | `hotel_class values must be 2-5` | Invalid `hotel_class` |
| `400` | `detail_token is required` | Detail endpoint called without `detail_token` |
| `400` | `invalid detail_token: ...` | `detail_token` malformed or not base64url |
| `502` | `request failed` | Transient. Retry |
| `502` | `unexpected response` | Non-200 upstream |
| `500` | `decompression failed` / `parse failed` | Transient. Retry |
***
# Google Scraper API (/documentation/google-scraper-api)
import { Cards, Card } from 'fumadocs-ui/components/card';
The Google Scraper API is a unified suite of nine specialized plugins covering Google's biggest data surfaces. Each sub-API returns parsed, structured JSON, with no HTML parsing, no JavaScript rendering, and no protobuf decoding. They share authentication, share localization parameters, and chain identifiers cleanly across products (a `place_id` from Search to Maps Place to Maps Reviews; a `topic_token` from News across drivers; a `data_id` between Maps endpoints).
**Credit Usage:** Most endpoints cost **10 credits** per request. Single-credit exceptions are noted on each sub-API's page. For bulk processing, every endpoint is also available through the [Async API with plugins](/documentation/async-api/plugins/).
Sub-APIs [#sub-apis]
} title="Google Search API" href="/documentation/google-scraper-api/search">
SERP results, AI Overviews, AI Mode responses. 150+ languages, 240+ countries, 84 Google domains, strict and contextual filtering.
} title="Google Maps API" href="/documentation/google-scraper-api/maps">
Places, place details, paginated user reviews. Rating summaries, popular times, menus, owner responses, third-party hotel sources.
} title="Google Shopping API" href="/documentation/google-scraper-api/shopping">
Product listings with prices, retailers, and 18+ filter groups, plus full product pages with every seller offer, variants, and reviews.
} title="Google Flights API" href="/documentation/google-scraper-api/flights">
Fares, layovers, durations, airlines, carbon emissions. Multi-airport origin and destination, six sort modes, price insights with history.
} title="Google Hotels API" href="/documentation/google-scraper-api/hotels">
Property listings with ratings, hotel class, amenities, GPS, images. Filters for price, rating, brand, eco-cert, free cancellation.
} title="Google Play Store API" href="/documentation/google-scraper-api/play-store">
Android app search, product details, and paginated user reviews with developer responses.
} title="Google News API" href="/documentation/google-scraper-api/news">
Articles with direct publisher URLs, ISO dates, bylines, thumbnails. Six driver parameters for keyword, topic, story, publisher, or entity navigation.
} title="Google Trends API" href="/documentation/google-scraper-api/trends">
Interest over time, by region, related queries, related topics, and real-time trending searches.
} title="Gemini Scraper API" href="/documentation/google-scraper-api/gemini">
Send a prompt to Google Gemini and get the Markdown reply, grounding sources, and model label as one JSON response.
***
Shared References [#shared-references]
Every sub-API uses the same localization vocabulary (`hl`, `gl`, `google_domain`) and shares the same transient-error semantics. Consolidated reference pages cover the full code lists once instead of repeating them per sub-API.
Complete `hl` (150+ languages), `gl` (240+ countries), `google_domain` (84 domains), `lr` / `cr` strict filters, `location` / `uule`, `ll`, `device`, `time_period`. Applicable-to badges per parameter.
`currency` codes used by Flights, Hotels, and Shopping. Independent of `gl`, so you can request EUR pricing from a US perspective for unified multi-region comparison.
Why `502 no results` happens, when to retry, and the retry-once pattern that recovers \~99% of transient empties.
***
Authentication [#authentication]
All requests use your Scrape.do API token via the `token` query parameter:
```
https://api.scrape.do/plugin/google/?token=YOUR_TOKEN&...
```
***
Common Response Shape [#common-response-shape]
Every Google plugin returns JSON with the same broad structure:
```json
{
"search_parameters": { ... }, // echo of request parameters
"search_information": { ... }, // metadata (where applicable)
"": [ ... ], // local_results, shopping_results, news_results, properties, best_flights, ...
"pagination": { ... } // when applicable
}
```
Array fields always return `[]` when empty (never `null`). Object fields return `null` when absent.
***
Error Handling [#error-handling]
Every endpoint returns errors in a consistent JSON format:
```json
{
"error": "error_code",
"message": "Human readable error message"
}
```
`400` responses indicate a client error (missing or invalid parameter). `502` responses indicate a transient upstream failure. See [Transient Errors](/documentation/google-scraper-api/transient-errors) for retry guidance. `500` responses indicate a parser-level issue and are usually transient.
***
Async Bulk Processing [#async-bulk-processing]
Every Google plugin endpoint is also available as an async plugin via the Async API. Submit batches of requests and collect results as they complete. See [Async API with plugins](/documentation/async-api/plugins/) for the full flow.
# Localization (/documentation/google-scraper-api/localization)
This is the master localization reference for the entire Google Scraper API suite. Every parameter listed here is documented once with an **applicable-to** badge so you know which sub-APIs accept it. The full code lists (150+ languages, 240+ countries, 84 domains) live on this page; every other doc just links here.
For currency parameters used by the pricing-aware sub-APIs, see [Currency](/documentation/google-scraper-api/currency).
***
Language Parameter (hl) [#language-parameter-hl]
**Applicable to:** Search, Maps, Shopping, Flights, Hotels, News. (Trends uses `geo` instead.)
The `hl` (Host Language) parameter controls the language of the Google UI (buttons, labels, and interface text). It uses ISO 639-1 language codes. All 150+ Google interface languages are supported.
On Maps and Reviews, `hl` also localizes day names in `operating_hours`, guided review `details` keys (`cuisine` instead of `food` for `hl=fr`), and triggers inline review translations (`extracted_snippet.translated`). On Hotels and Flights, it affects label text but not retailer/airline names. On News, it changes the menu strip and related topics.
Common Languages [#common-languages]
| | |
| :--------------------- | :---------------------------- |
| `en` English (default) | `ja` Japanese |
| `tr` Turkish | `ko` Korean |
| `de` German | `ar` Arabic |
| `fr` French | `ru` Russian |
| `es` Spanish | `zh-cn` Chinese (Simplified) |
| `it` Italian | `zh-tw` Chinese (Traditional) |
| `pt` Portuguese | `pl` Polish |
| `nl` Dutch | `hi` Hindi |
| `sv` Swedish | `th` Thai |
All Supported hl Values [#all-supported-hl-values]
| | | |
| :---------------------------- | :------------------------------- | :---------------------------- |
| `ach` Luo | `af` Afrikaans | `ak` Akan |
| `am` Amharic | `ar` Arabic | `az` Azerbaijani |
| `be` Belarusian | `bem` Bemba | `bh` Bihari |
| `bn` Bengali | `br` Breton | `bs` Bosnian |
| `bt` Bhutanese | `bg` Bulgarian | `ca` Catalan |
| `chr` Cherokee | `ckb` Kurdish (Sorani) | `co` Corsican |
| `crs` Seychellois Creole | `cs` Czech | `cy` Welsh |
| `da` Danish | `de` German | `ee` Ewe |
| `el` Greek | `en` English | `eo` Esperanto |
| `es` Spanish | `es-419` Spanish (Latin America) | `et` Estonian |
| `eu` Basque | `fa` Persian | `fi` Finnish |
| `fo` Faroese | `fr` French | `fy` Frisian |
| `ga` Irish | `gaa` Ga | `gd` Scots Gaelic |
| `gl` Galician | `gn` Guarani | `gu` Gujarati |
| `ha` Hausa | `haw` Hawaiian | `he` Hebrew |
| `hi` Hindi | `hr` Croatian | `ht` Haitian Creole |
| `hu` Hungarian | `hy` Armenian | `ia` Interlingua |
| `id` Indonesian | `ig` Igbo | `is` Icelandic |
| `it` Italian | `iw` Hebrew (legacy) | `ja` Japanese |
| `jw` Javanese | `ka` Georgian | `kg` Kongo |
| `kk` Kazakh | `kl` Greenlandic | `km` Cambodian |
| `kn` Kannada | `ko` Korean | `kri` Krio |
| `ku` Kurdish | `ky` Kyrgyz | `la` Latin |
| `lg` Luganda | `ln` Lingala | `lo` Laothian |
| `loz` Lozi | `lt` Lithuanian | `lua` Tshiluba |
| `lv` Latvian | `mfe` Mauritian Creole | `mg` Malagasy |
| `mi` Maori | `mk` Macedonian | `ml` Malayalam |
| `mn` Mongolian | `mo` Moldavian | `mr` Marathi |
| `ms` Malay | `mt` Maltese | `mv` Maldives |
| `my` Myanmar (Burmese) | `ne` Nepali | `nl` Dutch |
| `nn` Norwegian (Nynorsk) | `no` Norwegian | `nso` Northern Sotho |
| `ny` Chichewa | `nyn` Runyakitara | `oc` Occitan |
| `om` Oromo | `or` Oriya | `pa` Punjabi |
| `pcm` Nigerian Pidgin | `pl` Polish | `ps` Pashto |
| `pt` Portuguese | `pt-br` Portuguese (Brazil) | `pt-pt` Portuguese (Portugal) |
| `qu` Quechua | `rm` Romansh | `rn` Kirundi |
| `ro` Romanian | `ru` Russian | `rw` Kinyarwanda |
| `sd` Sindhi | `sh` Serbo-Croatian | `si` Sinhalese |
| `sk` Slovak | `sl` Slovenian | `sn` Shona |
| `so` Somali | `sq` Albanian | `sr` Serbian |
| `sr-me` Montenegrin | `st` Sesotho | `su` Sundanese |
| `sv` Swedish | `sw` Swahili | `ta` Tamil |
| `te` Telugu | `tg` Tajik | `th` Thai |
| `ti` Tigrinya | `tk` Turkmen | `tl` Filipino |
| `tn` Setswana | `to` Tonga | `tr` Turkish |
| `tt` Tatar | `tum` Tumbuka | `tw` Twi |
| `ug` Uighur | `uk` Ukrainian | `ur` Urdu |
| `uz` Uzbek | `vi` Vietnamese | `vu` Vanuatu |
| `wo` Wolof | `ws` Samoa | `xh` Xhosa |
| `yi` Yiddish | `yo` Yoruba | `zh-cn` Chinese (Simplified) |
| `zh-tw` Chinese (Traditional) | `zu` Zulu | |
`hl` sets the search *context*. Google treats it as a hint, so results may still include other languages. Use `lr` for strict language filtering.
***
Country Parameter (gl) [#country-parameter-gl]
**Applicable to:** Search, Maps, Shopping, Flights, Hotels, News. (Trends uses `geo` instead.)
The `gl` (Geo Location) parameter determines from which country's perspective results are ranked and returned. It uses ISO 3166-1 alpha-2 country codes. All 240+ Google geo location codes are supported.
Common Countries [#common-countries]
| | |
| :--------------------------- | :--------------- |
| `us` United States (default) | `de` Germany |
| `gb` United Kingdom | `fr` France |
| `tr` Turkey | `es` Spain |
| `ca` Canada | `it` Italy |
| `au` Australia | `nl` Netherlands |
| `jp` Japan | `br` Brazil |
| `kr` South Korea | `in` India |
| `mx` Mexico | |
All Supported gl Values [#all-supported-gl-values]
Americas [#americas]
| | |
| :---------------------- | :----------------------------- |
| `us` United States | `gy` Guyana |
| `ca` Canada | `ht` Haiti |
| `mx` Mexico | `hn` Honduras |
| `ar` Argentina | `jm` Jamaica |
| `bo` Bolivia | `mq` Martinique |
| `br` Brazil | `ni` Nicaragua |
| `cl` Chile | `pa` Panama |
| `co` Colombia | `py` Paraguay |
| `cr` Costa Rica | `pe` Peru |
| `cu` Cuba | `pr` Puerto Rico |
| `dm` Dominica | `pm` Saint Pierre and Miquelon |
| `do` Dominican Republic | `vc` Saint Vincent |
| `ec` Ecuador | `sr` Suriname |
| `sv` El Salvador | `tt` Trinidad and Tobago |
| `fk` Falkland Islands | `um` US Minor Outlying Islands |
| `gf` French Guiana | `uy` Uruguay |
| `gd` Grenada | `ve` Venezuela |
| `gp` Guadeloupe | `vg` British Virgin Islands |
| `gt` Guatemala | `vi` US Virgin Islands |
Europe [#europe]
| | |
| :-------------------------- | :------------------- |
| `gb` United Kingdom | `lt` Lithuania |
| `uk` United Kingdom (alt) | `lu` Luxembourg |
| `al` Albania | `lv` Latvia |
| `ad` Andorra | `mc` Monaco |
| `at` Austria | `md` Moldova |
| `ba` Bosnia and Herzegovina | `me` Montenegro |
| `be` Belgium | `mk` North Macedonia |
| `bg` Bulgaria | `mt` Malta |
| `by` Belarus | `nl` Netherlands |
| `ch` Switzerland | `no` Norway |
| `cz` Czech Republic | `pl` Poland |
| `de` Germany | `pt` Portugal |
| `dk` Denmark | `ro` Romania |
| `ee` Estonia | `rs` Serbia |
| `es` Spain | `ru` Russia |
| `fi` Finland | `se` Sweden |
| `fo` Faroe Islands | `si` Slovenia |
| `fr` France | `sk` Slovakia |
| `ge` Georgia | `sm` San Marino |
| `gi` Gibraltar | `sj` Svalbard |
| `gg` Guernsey | `ua` Ukraine |
| `gl` Greenland | `va` Vatican City |
| `gr` Greece | `im` Isle of Man |
| `hr` Croatia | `je` Jersey |
| `hu` Hungary | `is` Iceland |
| `ie` Ireland | `li` Liechtenstein |
| `it` Italy | |
Asia Pacific [#asia-pacific]
| | |
| :---------------------------- | :-------------------- |
| `af` Afghanistan | `mv` Maldives |
| `au` Australia | `my` Malaysia |
| `az` Azerbaijan | `mm` Myanmar |
| `bd` Bangladesh | `mn` Mongolia |
| `bn` Brunei | `nr` Nauru |
| `bt` Bhutan | `np` Nepal |
| `cc` Cocos Islands | `nz` New Zealand |
| `ck` Cook Islands | `nf` Norfolk Island |
| `cn` China | `nu` Niue |
| `cx` Christmas Island | `pk` Pakistan |
| `fj` Fiji | `pg` Papua New Guinea |
| `fm` Micronesia | `ph` Philippines |
| `gu` Guam | `pn` Pitcairn |
| `hk` Hong Kong | `pw` Palau |
| `id` Indonesia | `sb` Solomon Islands |
| `in` India | `sg` Singapore |
| `jp` Japan | `th` Thailand |
| `kh` Cambodia | `tl` Timor-Leste |
| `ki` Kiribati | `tk` Tokelau |
| `kp` North Korea | `to` Tonga |
| `kr` South Korea | `tv` Tuvalu |
| `kz` Kazakhstan | `tw` Taiwan |
| `kg` Kyrgyzstan | `uz` Uzbekistan |
| `la` Laos | `vn` Vietnam |
| `lk` Sri Lanka | `vu` Vanuatu |
| `mh` Marshall Islands | `ws` Samoa |
| `mo` Macao | `as` American Samoa |
| `mp` Northern Mariana Islands | `pf` French Polynesia |
Middle East & Africa [#middle-east--africa]
| | |
| :---------------------------- | :------------------------- |
| `ae` United Arab Emirates | `mg` Madagascar |
| `bh` Bahrain | `ml` Mali |
| `bj` Benin | `mr` Mauritania |
| `bf` Burkina Faso | `mu` Mauritius |
| `bi` Burundi | `mw` Malawi |
| `bw` Botswana | `mz` Mozambique |
| `cd` Congo (DRC) | `na` Namibia |
| `cf` Central African Republic | `ne` Niger |
| `cg` Congo | `ng` Nigeria |
| `ci` Cote D'Ivoire | `om` Oman |
| `cm` Cameroon | `ps` Palestinian Territory |
| `cv` Cape Verde | `qa` Qatar |
| `dj` Djibouti | `re` Reunion |
| `dz` Algeria | `rw` Rwanda |
| `eg` Egypt | `sa` Saudi Arabia |
| `eh` Western Sahara | `sc` Seychelles |
| `er` Eritrea | `sd` Sudan |
| `et` Ethiopia | `sh` Saint Helena |
| `ga` Gabon | `sl` Sierra Leone |
| `gh` Ghana | `sn` Senegal |
| `gm` Gambia | `so` Somalia |
| `gn` Guinea | `st` Sao Tome and Principe |
| `gq` Equatorial Guinea | `sz` Swaziland |
| `gw` Guinea-Bissau | `td` Chad |
| `il` Israel | `tg` Togo |
| `iq` Iraq | `tn` Tunisia |
| `ir` Iran | `tr` Turkey |
| `jo` Jordan | `tz` Tanzania |
| `ke` Kenya | `ug` Uganda |
| `km` Comoros | `ye` Yemen |
| `kn` Saint Kitts and Nevis | `yt` Mayotte |
| `kw` Kuwait | `za` South Africa |
| `lb` Lebanon | `zm` Zambia |
| `lc` Saint Lucia | `zw` Zimbabwe |
| `lr` Liberia | `ls` Lesotho |
| `ly` Libya | `ma` Morocco |
Other Territories [#other-territories]
| | |
| :---------------------------------- | :------------------------------- |
| `ai` Anguilla | `ky` Cayman Islands |
| `ag` Antigua and Barbuda | `ms` Montserrat |
| `aq` Antarctica | `nc` New Caledonia |
| `aw` Aruba | `sy` Syria |
| `bb` Barbados | `tc` Turks and Caicos |
| `bm` Bermuda | `tf` French Southern Territories |
| `bs` Bahamas | `tj` Tajikistan |
| `bv` Bouvet Island | `tm` Turkmenistan |
| `bz` Belize | `wf` Wallis and Futuna |
| `gs` South Georgia | `hm` Heard Island |
| `io` British Indian Ocean Territory | |
`gl` sets the search *context*. Google treats it as a hint, so results may still include global results. Use `cr` for strict country filtering.
***
Country Restrict Parameter (cr) [#country-restrict-parameter-cr]
**Applicable to:** Search only.
The `cr` (Country Restrict) parameter applies **strict country filtering**. Only results originating from the specified country are returned. Format: `countryXX` where `XX` is the ISO 3166-1 alpha-2 country code in uppercase.
Common Values [#common-values]
| | |
| :------------------------- | :---------------------- |
| `countryUS` United States | `countryDE` Germany |
| `countryUK` United Kingdom | `countryFR` France |
| `countryTR` Turkey | `countryES` Spain |
| `countryCA` Canada | `countryIT` Italy |
| `countryAU` Australia | `countryNL` Netherlands |
| `countryJP` Japan | `countryBR` Brazil |
| `countryKR` South Korea | `countryIN` India |
| `countryMX` Mexico | |
All Supported cr Values [#all-supported-cr-values]
We support 240+ `cr` values. The full list mirrors the `gl` country codes in `countryXX` format. Every country code supported in `gl` is also supported in `cr`.
Americas [#americas-1]
| | |
| :----------------------------- | :--------------------------------- |
| `countryUS` United States | `countryGY` Guyana |
| `countryCA` Canada | `countryHT` Haiti |
| `countryMX` Mexico | `countryHN` Honduras |
| `countryAR` Argentina | `countryJM` Jamaica |
| `countryBO` Bolivia | `countryNI` Nicaragua |
| `countryBR` Brazil | `countryPA` Panama |
| `countryCL` Chile | `countryPY` Paraguay |
| `countryCO` Colombia | `countryPE` Peru |
| `countryCR` Costa Rica | `countryPR` Puerto Rico |
| `countryCU` Cuba | `countrySR` Suriname |
| `countryDM` Dominica | `countryTT` Trinidad and Tobago |
| `countryDO` Dominican Republic | `countryUY` Uruguay |
| `countryEC` Ecuador | `countryVE` Venezuela |
| `countrySV` El Salvador | `countryVI` US Virgin Islands |
| `countryGT` Guatemala | `countryVG` British Virgin Islands |
Europe [#europe-1]
| | |
| :--------------------------------- | :-------------------------- |
| `countryUK` United Kingdom | `countryLT` Lithuania |
| `countryAL` Albania | `countryLU` Luxembourg |
| `countryAT` Austria | `countryLV` Latvia |
| `countryBA` Bosnia and Herzegovina | `countryMC` Monaco |
| `countryBE` Belgium | `countryMD` Moldova |
| `countryBG` Bulgaria | `countryMK` North Macedonia |
| `countryBY` Belarus | `countryMT` Malta |
| `countryCH` Switzerland | `countryNL` Netherlands |
| `countryCZ` Czech Republic | `countryNO` Norway |
| `countryDE` Germany | `countryPL` Poland |
| `countryDK` Denmark | `countryPT` Portugal |
| `countryEE` Estonia | `countryRO` Romania |
| `countryES` Spain | `countryRU` Russia |
| `countryEU` European Union | `countrySE` Sweden |
| `countryFI` Finland | `countrySI` Slovenia |
| `countryFR` France | `countrySK` Slovakia |
| `countryGE` Georgia | `countryUA` Ukraine |
| `countryGR` Greece | `countryVA` Vatican City |
| `countryHR` Croatia | `countryIS` Iceland |
| `countryHU` Hungary | `countryLI` Liechtenstein |
| `countryIE` Ireland | `countrySM` San Marino |
| `countryIT` Italy | |
Asia Pacific [#asia-pacific-1]
| | |
| :---------------------- | :---------------------- |
| `countryAU` Australia | `countryMY` Malaysia |
| `countryBD` Bangladesh | `countryMM` Myanmar |
| `countryCN` China | `countryNP` Nepal |
| `countryHK` Hong Kong | `countryNZ` New Zealand |
| `countryID` Indonesia | `countryPH` Philippines |
| `countryIN` India | `countryPK` Pakistan |
| `countryJP` Japan | `countrySG` Singapore |
| `countryKH` Cambodia | `countryTH` Thailand |
| `countryKP` North Korea | `countryTW` Taiwan |
| `countryKR` South Korea | `countryVN` Vietnam |
| `countryKZ` Kazakhstan | `countryMN` Mongolia |
| `countryLA` Laos | `countryLK` Sri Lanka |
| `countryMO` Macao | `countryKG` Kyrgyzstan |
Middle East & Africa [#middle-east--africa-1]
| | |
| :------------------------------- | :-------------------------------- |
| `countryAE` United Arab Emirates | `countryMA` Morocco |
| `countryBH` Bahrain | `countryNG` Nigeria |
| `countryDZ` Algeria | `countryOM` Oman |
| `countryEG` Egypt | `countryPS` Palestinian Territory |
| `countryET` Ethiopia | `countryQA` Qatar |
| `countryGH` Ghana | `countrySA` Saudi Arabia |
| `countryIL` Israel | `countrySD` Sudan |
| `countryIQ` Iraq | `countryTN` Tunisia |
| `countryIR` Iran | `countryTR` Turkey |
| `countryJO` Jordan | `countryTZ` Tanzania |
| `countryKE` Kenya | `countryUG` Uganda |
| `countryKW` Kuwait | `countryZA` South Africa |
| `countryLB` Lebanon | `countryZM` Zambia |
| `countryLY` Libya | `countryZW` Zimbabwe |
You can also combine `cr` values with boolean operators for complex filtering. For example, `countryUS.countryUK` restricts results to pages from either the US or UK.
***
Language Restrict Parameter (lr) [#language-restrict-parameter-lr]
**Applicable to:** Search only.
The `lr` (Language Restrict) parameter applies **strict language filtering**. Only results written in the specified language are returned. Format: `lang_XX`.
We support all 35 Google language restrict values:
| | | |
| :------------------ | :------------------- | :--------------------------------- |
| `lang_ar` Arabic | `lang_hu` Hungarian | `lang_pl` Polish |
| `lang_bg` Bulgarian | `lang_id` Indonesian | `lang_pt` Portuguese |
| `lang_ca` Catalan | `lang_is` Icelandic | `lang_ro` Romanian |
| `lang_cs` Czech | `lang_it` Italian | `lang_ru` Russian |
| `lang_da` Danish | `lang_iw` Hebrew | `lang_sk` Slovak |
| `lang_de` German | `lang_ja` Japanese | `lang_sl` Slovenian |
| `lang_el` Greek | `lang_ko` Korean | `lang_sr` Serbian |
| `lang_en` English | `lang_lt` Lithuanian | `lang_sv` Swedish |
| `lang_es` Spanish | `lang_lv` Latvian | `lang_tr` Turkish |
| `lang_et` Estonian | `lang_nl` Dutch | `lang_zh-CN` Chinese (Simplified) |
| `lang_fi` Finnish | `lang_no` Norwegian | `lang_zh-TW` Chinese (Traditional) |
| `lang_fr` French | `lang_hr` Croatian | |
***
Location & UULE [#location--uule]
**Applicable to:** Search, Shopping. (Maps uses `ll` instead. See below.)
For precise geographic targeting beyond country-level, use the `location` or `uule` parameter.
location [#location]
The `location` parameter accepts a location name in Google's canonical format. It is automatically encoded to a UULE string internally.
**Format:** `City,State/Region,Country`
**Examples:**
* `Istanbul,Istanbul,Turkey`
* `New York,New York,United States`
* `London,England,United Kingdom`
* `Tokyo,Tokyo,Japan`
* `Berlin,Berlin,Germany`
uule [#uule]
The `uule` parameter accepts a pre-encoded Google UULE location string. This is auto-generated from `location` when not provided.
If both `location` and `uule` are sent, `uule` takes priority. For most use cases, `location` is sufficient. Use `uule` only when you need to pass a pre-encoded value.
***
hl vs lr / gl vs cr [#hl-vs-lr--gl-vs-cr]
The Google Search API has two levels of language and country control:
Context (Hint) vs Strict Filter [#context-hint-vs-strict-filter]
| | Context (Hint) | Strict Filter |
| ------------ | -------------------------------------------------------------------- | ------------------------------------------------------- |
| **Language** | `hl=tr`: Google UI in Turkish. Results may include other languages. | `lr=lang_tr`: Only Turkish-language pages are returned. |
| **Country** | `gl=tr`: Results from Turkey datacenter. May include global results. | `cr=countryTR`: Only pages originating from Turkey. |
When to Use What [#when-to-use-what]
* **`hl` + `gl`**: Best for general localization. You want results relevant to a market but don't mind mixed languages.
* **`lr` + `cr`**: Best for strict filtering. You need only pages in a specific language or from a specific country.
* **Combine both**: Use `hl`/`gl` for context and add `lr`/`cr` when you need strict filtering on top.
**Example: Turkish results from Turkey**
```
/plugin/google/search?token=TOKEN&q=pizza&hl=tr&gl=tr&google_domain=google.com.tr
```
**Example: Strictly Turkish-language pages from Turkey**
```
/plugin/google/search?token=TOKEN&q=pizza&hl=tr&gl=tr&lr=lang_tr&cr=countryTR&google_domain=google.com.tr
```
***
Time-Based Filtering [#time-based-filtering]
**Applicable to:** Search only.
The `time_period` parameter limits results to a specific recency window:
| Value | Description |
| ------------ | ------------------------------- |
| `last_hour` | Results from the last hour |
| `last_day` | Results from the last 24 hours |
| `last_week` | Results from the last 7 days |
| `last_month` | Results from the last 30 days |
| `last_year` | Results from the last 12 months |
***
Supported Google Domains [#supported-google-domains]
**Applicable to:** Search, Maps, Shopping. (Flights, Hotels, News, and Trends use a global Google origin; locale comes from `gl`, `hl`, and `currency`.)
The `google_domain` parameter accepts the following 84 domains. Prefixes (`https://`, `http://`, `www.`) are automatically stripped.
Americas [#americas-2]
| Domain | Country |
| --------------- | ------------- |
| `google.com` | United States |
| `google.ca` | Canada |
| `google.com.br` | Brazil |
| `google.com.mx` | Mexico |
| `google.com.ar` | Argentina |
| `google.cl` | Chile |
| `google.com.co` | Colombia |
| `google.com.pe` | Peru |
Europe [#europe-2]
| Domain | Country |
| --------------- | -------------- |
| `google.co.uk` | United Kingdom |
| `google.de` | Germany |
| `google.fr` | France |
| `google.es` | Spain |
| `google.it` | Italy |
| `google.nl` | Netherlands |
| `google.be` | Belgium |
| `google.at` | Austria |
| `google.ch` | Switzerland |
| `google.se` | Sweden |
| `google.no` | Norway |
| `google.dk` | Denmark |
| `google.fi` | Finland |
| `google.pl` | Poland |
| `google.pt` | Portugal |
| `google.com.tr` | Turkey |
| `google.ie` | Ireland |
| `google.com.ua` | Ukraine |
| `google.ro` | Romania |
| `google.gr` | Greece |
| `google.hu` | Hungary |
| `google.cz` | Czech Republic |
| `google.sk` | Slovakia |
| `google.bg` | Bulgaria |
| `google.hr` | Croatia |
| `google.rs` | Serbia |
| `google.si` | Slovenia |
| `google.lt` | Lithuania |
| `google.lv` | Latvia |
| `google.ee` | Estonia |
| `google.ru` | Russia |
Asia Pacific [#asia-pacific-2]
| Domain | Country |
| --------------- | ----------- |
| `google.co.jp` | Japan |
| `google.co.kr` | South Korea |
| `google.com.au` | Australia |
| `google.co.nz` | New Zealand |
| `google.co.in` | India |
| `google.co.id` | Indonesia |
| `google.co.th` | Thailand |
| `google.com.sg` | Singapore |
| `google.com.my` | Malaysia |
| `google.com.ph` | Philippines |
| `google.com.vn` | Vietnam |
| `google.com.tw` | Taiwan |
| `google.com.hk` | Hong Kong |
| `google.com.pk` | Pakistan |
| `google.com.bd` | Bangladesh |
| `google.com.np` | Nepal |
| `google.lk` | Sri Lanka |
| `google.com.mm` | Myanmar |
| `google.com.kh` | Cambodia |
| `google.la` | Laos |
Middle East & Africa [#middle-east--africa-2]
| Domain | Country |
| --------------- | ------------ |
| `google.co.za` | South Africa |
| `google.com.eg` | Egypt |
| `google.com.sa` | Saudi Arabia |
| `google.ae` | UAE |
| `google.co.il` | Israel |
| `google.com.ng` | Nigeria |
| `google.co.ke` | Kenya |
| `google.com.gh` | Ghana |
| `google.com.et` | Ethiopia |
| `google.co.tz` | Tanzania |
| `google.co.ug` | Uganda |
| `google.com.ly` | Libya |
| `google.dz` | Algeria |
| `google.co.ma` | Morocco |
| `google.tn` | Tunisia |
| `google.com.qa` | Qatar |
| `google.com.kw` | Kuwait |
| `google.com.bh` | Bahrain |
| `google.com.om` | Oman |
| `google.jo` | Jordan |
| `google.com.lb` | Lebanon |
| `google.iq` | Iraq |
Central Asia & Caucasus [#central-asia--caucasus]
| Domain | Country |
| ----------- | ---------- |
| `google.az` | Azerbaijan |
| `google.kz` | Kazakhstan |
| `google.ge` | Georgia |
***
Latitude/Longitude Pinning (ll) [#latitudelongitude-pinning-ll]
**Applicable to:** Maps Search only.
The `ll` parameter pins the Maps search to a specific latitude, longitude, and zoom level. It applies to the [Maps Search endpoint](/documentation/google-scraper-api/maps/search) only. Without `ll`, Google uses IP-based geolocation of the proxy network.
Format [#format]
```
ll=@LAT,LNG,ZOOMz
```
* `LAT`, `LNG`: decimal coordinates (e.g., `40.7128,-74.0060`).
* `ZOOMz`: integer zoom from 1 (world) to 21 (building level), followed by the literal `z` (e.g., `13z`).
Examples [#examples]
| Target | `ll` value |
| ------------------------------------- | ------------------------ |
| Times Square, New York (neighborhood) | `@40.7580,-73.9855,15z` |
| Manhattan (borough) | `@40.7128,-74.0060,13z` |
| London Bridge area | `@51.5074,-0.0878,14z` |
| Shibuya, Tokyo | `@35.6595,139.7005,15z` |
| San Francisco Ferry Building | `@37.7955,-122.3937,14z` |
Zoom guide [#zoom-guide]
| Zoom | Typical coverage |
| ----------- | ------------------ |
| `10z`–`12z` | Metro area / city |
| `13z`–`14z` | Borough / district |
| `15z`–`16z` | Neighborhood |
| `17z`–`19z` | Block / street |
Invalid `ll` values return `400 invalid ll parameter`.
***
Device Emulation (device) [#device-emulation-device]
**Applicable to:** Search, Shopping.
| Value | Description |
| ------------------- | --------------------------------------------------------------------------------------------------------- |
| `desktop` (default) | Desktop layout |
| `mobile` | Mobile layout, with slightly different filter set, smaller thumbnails, and occasionally different ranking |
Invalid values return `400 device must be one of: desktop, mobile`.
```
/plugin/google/search?token=TOKEN&q=laptop&device=mobile
/plugin/google/shopping?token=TOKEN&q=laptop&device=mobile
```
Useful for tracking how listings appear across device types for the same query.
***
Trends Geo Parameter (geo) [#trends-geo-parameter-geo]
**Applicable to:** Trends only.
The Google Trends API uses its own `geo` parameter instead of `gl`. Format is the ISO 3166-1 alpha-2 country code (uppercase) optionally followed by a region subdivision:
| Value | Meaning |
| ------- | -------------- |
| `US` | United States |
| `US-CA` | California, US |
| `GB` | United Kingdom |
| `TR` | Turkey |
| `DE` | Germany |
| `JP` | Japan |
| `IN` | India |
See [Trends](/documentation/google-scraper-api/trends) for full details. `hl` works the same as on other sub-APIs.
***
import { Cards, Card } from 'fumadocs-ui/components/card';
# Google News API (/documentation/google-scraper-api/news)
import { Cards, Card } from 'fumadocs-ui/components/card';
The Google News API is a specialized plugin that returns Google News results (articles, topic clusters, menu navigation, related topics) as clean JSON. One HTTP call per request. Deeper browsing happens by chaining tokens (`topic_token`, `section_token`, `story_token`, `publication_token`) returned inside any response.
**Credit Usage:** Each successful request costs **10 credits**. For bulk processing, use the [Async API with plugins](/documentation/async-api/plugins/).
Key Features [#key-features]
* **Six Driver Parameters**: Search by keyword (`q`), browse a topic (`topic_token`), drill into a section (`section_token`), expand a story cluster (`story_token`), view a publication's page (`publication_token`), or pivot through a Knowledge Graph entity (`kgmid`).
* **Direct Publisher URLs**: `news_results[].link` is the **publisher URL** directly. No Google redirect to follow, no AMP wrapper to unwrap.
* **RSS Link Resolution**: Standard scrape requests can resolve `news.google.com/rss/articles/...` wrapper links to the real publisher article page.
* **Per-Article Metadata**: Bylines (`source.authors`), publisher icons, relative date (`"3 hours ago"`), ISO 8601 timestamps, and thumbnails (full + small).
* **Story Clusters**: Multi-outlet coverage of the same news story comes back as a cluster object with a `stories[]` array, same schema as flat articles.
* **Sort by Relevance or Date**: `so=0` / `so=1` controls keyword and entity searches. Other drivers use Google's editorial ordering.
* **Localized Menus & Topics**: `menu_links[]` (top nav: U.S., World, Business, Technology, …) and `related_topics[]` are returned localized for `hl` / `gl`.
* **No Pagination Quirks**: A single response returns the first page (\~100 articles). Deeper browsing is done by chaining tokens, with no manual offset arithmetic.
* **No Blocks or CAPTCHAs**: All anti-bot measures are handled automatically by Scrape.do.
***
Endpoint [#endpoint]
```
GET https://api.scrape.do/plugin/google/news
```
***
Request Parameters [#request-parameters]
Required [#required]
| Parameter | Type | Description |
| --------- | ------ | --------------------------------------- |
| `token` | string | Your Scrape.do API authentication token |
Plus **exactly one** driver:
| Driver | Type | Fetches |
| ------------------- | ------ | ----------------------------------------------------------------- |
| `q` | string | Keyword search (e.g., `q=openai`) |
| `topic_token` | string | A topic stream (U.S., World, Business, Technology, …) |
| `section_token` | string | A section within a topic (Latest, For You, Opinion, …) |
| `story_token` | string | Full-coverage page for a single news story |
| `publication_token` | string | A publisher's page (CNN, BBC, Reuters, …) |
| `kgmid` | string | A Knowledge Graph entity ID (e.g., `/m/02_286` for New York City) |
Sending no driver returns `400 one of q, topic_token, section_token, story_token, publication_token, kgmid is required`. Sending two returns `400 exactly one of ... may be set`.
Tokens are returned inside `news_results[]`, `menu_links[]`, `sub_menu_links[]`, `related_topics[]`, and `related_publications[]` on every response. Chain them to navigate.
**Tokens rotate occasionally.** When a token stops working (`502 unexpected response`), fetch a fresh one from a recent response and retry.
Localization [#localization]
| Parameter | Type | Default | Description |
| --------------- | ------ | ------------ | ------------------------------------------------------------------------------------- |
| `hl` | string | `en` | Language code (e.g., `en`, `tr`, `de`, `fr`, `ja`, `pt-br`) |
| `gl` | string | `us` | Country code (e.g., `us`, `gb`, `de`, `tr`, `jp`, `br`) |
| `google_domain` | string | `google.com` | Echoed back; Google News uses one global origin and the locale comes from `hl` / `gl` |
Sort (search mode only) [#sort-search-mode-only]
| Parameter | Values | Description |
| --------- | ---------- | --------------------------------------------------------------------------- |
| `so` | `0` or `1` | `0` = by relevance (default), `1` = by date. Only valid with `q` or `kgmid` |
***
Example Usage [#example-usage]
Keyword Search [#keyword-search]
```bash
curl "https://api.scrape.do/plugin/google/news?q=openai&hl=en&gl=us&token=$TOKEN"
```
Localized Keyword Search [#localized-keyword-search]
```bash
curl "https://api.scrape.do/plugin/google/news?q=bundesliga&hl=de&gl=de&token=$TOKEN"
```
Sort by Date [#sort-by-date]
```bash
curl "https://api.scrape.do/plugin/google/news?q=openai&so=1&token=$TOKEN"
```
Topic Stream [#topic-stream]
```bash
curl "https://api.scrape.do/plugin/google/news?topic_token=CAAqJggKIiBDQkFTRWdvSUwyMHZNRGRqTVhZU0FtVnVHZ0pWVXlnQVAB&token=$TOKEN"
```
Story Cluster [#story-cluster]
```bash
curl "https://api.scrape.do/plugin/google/news?story_token=CAAqNggKIjBDQklTSGpvSmMzUnZjbmt0TXpZd1NoRUtEd2lLOWRfNUVCRllrZlFnbTlaN3F5Z0FQAQ&token=$TOKEN"
```
Knowledge Graph Entity [#knowledge-graph-entity]
```bash
# /m/02_286 = New York City
curl "https://api.scrape.do/plugin/google/news?kgmid=/m/02_286&so=1&token=$TOKEN"
```
***
Response [#response]
Top-Level Shape [#top-level-shape]
```json
{
"search_parameters": { ... },
"title": "U.S.",
"news_results": [ ... ],
"menu_links": [ ... ],
"sub_menu_links": [ ... ],
"related_topics": [ ... ],
"related_publications": [ ... ]
}
```
`news_results` is always an array (empty array when no results, never `null`). Other fields are present contextually:
* `title`: populated on topic and publication responses (e.g. `"Technology"`, `"CNN"`).
* `menu_links`: top navigation strip; same on every page (localized by `hl` / `gl`).
* `sub_menu_links`: sections within the current topic / publication.
* `related_topics`: populated on keyword searches when the query resolves to a known entity.
* `related_publications`: populated on publication pages.
search_parameters [#search_parameters]
```json
{
"engine": "google_news",
"q": "openai",
"google_domain": "google.com",
"hl": "en",
"gl": "us"
}
```
Optional fields (`topic_token`, `section_token`, `story_token`, `publication_token`, `kgmid`, `so`) appear when set.
news_results[] [#news_results]
Each entry is either a **flat article** or a **cluster** (a single story covered by multiple outlets).
Flat article [#flat-article]
```json
{
"position": 2,
"title": "OpenAI Takes Aim at Google with New Image Model",
"link": "https://www.theinformation.com/newsletters/ai-agenda/openai-takes-aim-google-new-image-model",
"source": {
"name": "The Information",
"title": "The Information",
"icon": "https://encrypted-tbn3.gstatic.com/faviconV2?...",
"authors": ["Stephanie Palazzolo"]
},
"date": "20 hours ago",
"iso_date": "2026-04-20T14:00:00Z",
"thumbnail": "https://tii.imgix.net/production/articles/16959/46bed976.png",
"thumbnail_small": "https://tii.imgix.net/production/articles/16959/46bed976.png",
"topic_token": "CAAqKAgKIiJDQkFTRXdvTkwyY3ZNVEZuWW1oeGNqaHhlaElDWlc0b0FBUAE",
"publication_token": "CAAqLggKIihDQklTR0FnTWFoUUtFblJvWldsdVptOXliV0YwYVc5dUxtTnZiU2dBUAE"
}
```
Cluster [#cluster]
The outer entry holds only the cluster headline; individual articles live in `stories[]`.
```json
{
"position": 1,
"title": "Federal Reserve signals rate cut ahead",
"stories": [
{
"position": 1,
"title": "Federal Reserve signals rate cut ahead",
"link": "https://www.reuters.com/markets/us/federal-reserve-signals-rate-cut-2026-04-21/",
"source": { "name": "Reuters", "icon": "..." },
"date": "2 hours ago",
"iso_date": "2026-04-21T12:10:50Z",
"thumbnail": "..."
},
{
"position": 2,
"title": "Wall Street rallies on Fed signal",
"link": "https://www.wsj.com/...",
"source": { "name": "The Wall Street Journal" },
"date": "3 hours ago",
"iso_date": "2026-04-21T11:00:00Z"
}
]
}
```
Field reference [#field-reference]
| Field | Type | Description |
| ------------------- | --------- | -------------------------------------------------------------- |
| `position` | int | 1-based position |
| `title` | string | Article (or cluster) headline |
| `link` | string | **Direct publisher URL** with no redirect to follow |
| `source.name` | string | Publisher name |
| `source.title` | string | Publisher display title |
| `source.icon` | string | Publisher favicon URL |
| `source.authors` | string\[] | Bylines when Google exposes them |
| `date` | string | Relative time (`"3 hours ago"`, `"2 days ago"`) |
| `iso_date` | string | RFC 3339 publication timestamp |
| `thumbnail` | string | Article image URL when available |
| `thumbnail_small` | string | Small-variant image URL |
| `topic_token` | string | Token for the parent topic. Pass back as `topic_token` |
| `story_token` | string | Token for the story cluster. Pass back as `story_token` |
| `publication_token` | string | Token for the publisher. Pass back as `publication_token` |
| `section_token` | string | Token for the section. Pass back as `section_token` |
| `stories` | array | Cluster entries only: related articles covering the same story |
menu_links[] [#menu_links]
The top navigation strip; same on every page (localized by `hl` / `gl`).
```json
[
{ "position": 1, "title": "U.S.", "topic_token": "CAAq..." },
{ "position": 2, "title": "World", "topic_token": "CAAq..." },
{ "position": 3, "title": "Business", "topic_token": "CAAq..." },
{ "position": 4, "title": "Technology","topic_token": "CAAq..." }
]
```
sub_menu_links[] [#sub_menu_links]
Present on topic and publication pages: sections within the current scope (Latest, For You, Opinion, …), each with a `section_token`.
related_topics[] [#related_topics]
Populated on keyword searches when the query resolves to a known entity (person, place, organization). Each item carries a `topic_token` you can use to pivot to that entity's topic stream.
related_publications[] [#related_publications]
Populated on publication pages: adjacent publishers Google suggests.
***
Notes [#notes]
* **No redirects.** `link` is the publisher URL directly.
* **Tokens are opaque.** Pass them back exactly as returned. Don't parse, construct, or cache them across days.
* **Tokens rotate.** When a token stops working (`502 unexpected response`), fetch a fresh one from a recent search response and retry.
* **No pagination.** A single response is the first page (\~100 articles for keyword searches). Deeper browsing happens by chaining tokens.
***
Resolving Google News RSS Article Links [#resolving-google-news-rss-article-links]
Google News RSS feeds can return wrapper URLs like this:
```text
https://news.google.com/rss/articles/?oc=5
```
Those URLs do not redirect to the publisher in a plain HTTP fetch. Google returns a JavaScript shell page instead: status `200`, no article text, and no publisher URL in the raw response body.
Pass the RSS article URL to the standard Scrape.do endpoint and Scrape.do resolves the wrapper before returning the response:
```bash
curl "https://api.scrape.do/?token=$TOKEN&url=https%3A%2F%2Fnews.google.com%2Frss%2Farticles%2FARTICLE_ID%3Foc%3D5"
```
The response body is the publisher's article page, not the Google wrapper page.
Important details:
* `gl` / `hl` parameters on the wrapper URL are honored when Google honors that combination.
* Resolution costs **10 credits**, the same as a `/plugin/google/news` request.
* If the wrapper cannot be resolved, the request returns `502` and is **not charged**.
* A `200` response means Scrape.do returned real publisher article content, not the Google shell page.
You do not need this step for `/plugin/google/news` responses. The `news_results[].link` field is already the direct publisher URL.
***
Navigating with Tokens [#navigating-with-tokens]
Google News doesn't paginate. Instead, it exposes a graph of topics, sections, stories, and publications. You navigate by chaining the opaque `*_token` values returned in any response.
Token Types [#token-types]
| Token | Where you find it | What it fetches |
| ------------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| `topic_token` | `news_results[].topic_token`, `menu_links[].topic_token`, `related_topics[].topic_token` | A topic stream (U.S., Business, Technology, Sports, …) |
| `section_token` | `sub_menu_links[].section_token` | A section within a topic (Latest, For You, Opinion, …) |
| `story_token` | `news_results[].story_token` (on cluster entries) | Full coverage for a single news story across multiple outlets |
| `publication_token` | `news_results[].publication_token`, `related_publications[].publication_token` | A publisher's page (CNN, BBC, Reuters, …) |
You also have:
* `q`: keyword search (not a token; a string).
* `kgmid`: a Knowledge Graph entity ID (e.g., `/m/02_286` for New York City). Stable across responses.
**Tokens are opaque.** Pass them back exactly as returned. Don't parse, construct, or cache them across days. **Tokens rotate** occasionally; when one stops working (`502 unexpected response`), fetch a fresh one from a recent response.
Workflow Patterns [#workflow-patterns]
Search → Topic Pivot [#search--topic-pivot]
A keyword search response includes `related_topics[]` when the query resolves to an entity. Pivot into the topic to get the editorial feed for that entity instead of search relevance.
```bash
# Step 1: search
curl -s "https://api.scrape.do/plugin/google/news?q=apple&token=$TOKEN" \
| jq '.related_topics'
# Output:
# [ { "title": "Apple Inc.", "topic_token": "CAAq..." } ]
# Step 2: pivot into the topic
TOPIC=$(curl -s "https://api.scrape.do/plugin/google/news?q=apple&token=$TOKEN" \
| jq -r '.related_topics[0].topic_token')
curl "https://api.scrape.do/plugin/google/news?topic_token=$TOPIC&token=$TOKEN"
```
Topic → Section [#topic--section]
A topic response includes `sub_menu_links[]` with sections like Latest, For You, Opinion, etc. Drill into one for that section's feed.
```bash
SECTION=$(curl -s "https://api.scrape.do/plugin/google/news?topic_token=$TOPIC&token=$TOKEN" \
| jq -r '.sub_menu_links[] | select(.title=="Latest") | .section_token')
curl "https://api.scrape.do/plugin/google/news?section_token=$SECTION&token=$TOKEN"
```
Article → Story Cluster [#article--story-cluster]
Search and topic results sometimes return cluster entries: entries with a `stories[]` array and a `story_token`. Pass `story_token` as the driver to get the full cluster page (often more outlets than the inline `stories[]` snapshot).
```bash
STORY=$(curl -s "https://api.scrape.do/plugin/google/news?q=fed+rate+cut&token=$TOKEN" \
| jq -r '.news_results[] | select(.story_token) | .story_token' \
| head -n 1)
curl "https://api.scrape.do/plugin/google/news?story_token=$STORY&token=$TOKEN"
```
Article → Publication [#article--publication]
Each article carries a `publication_token`. Pivot to the publisher's page to see their recent coverage.
```bash
PUB=$(curl -s "https://api.scrape.do/plugin/google/news?q=openai&token=$TOKEN" \
| jq -r '.news_results[0].publication_token')
curl "https://api.scrape.do/plugin/google/news?publication_token=$PUB&token=$TOKEN"
```
Top-Level Menu [#top-level-menu]
Every response includes `menu_links[]`, Google News's top navigation strip (U.S., World, Business, Technology, Sports, Entertainment, Science, Health). Each entry exposes a `topic_token`. Use it to navigate without first searching.
```bash
TECH=$(curl -s "https://api.scrape.do/plugin/google/news?q=anything&token=$TOKEN" \
| jq -r '.menu_links[] | select(.title=="Technology") | .topic_token')
curl "https://api.scrape.do/plugin/google/news?topic_token=$TECH&token=$TOKEN"
```
Knowledge Graph Pivot [#knowledge-graph-pivot]
`kgmid` is a stable identifier for a Knowledge Graph entity (a person, place, organization). Unlike the `*_token` values, it doesn't rotate, so you can store it.
```bash
# /m/02_286 = New York City, stable across responses
curl "https://api.scrape.do/plugin/google/news?kgmid=/m/02_286&so=1&token=$TOKEN"
# /m/0k8z = Apple Inc.
curl "https://api.scrape.do/plugin/google/news?kgmid=/m/0k8z&token=$TOKEN"
```
`kgmid` accepts the `so` (sort) parameter; use `so=1` for date-sorted entity feeds.
Sort Order Across Drivers [#sort-order-across-drivers]
| Driver | `so` accepted | Default ordering |
| ------------------- | :-----------: | ---------------------------------------------- |
| `q` | ✅ | Relevance (`so=0`); switch with `so=1` |
| `kgmid` | ✅ | Same as `q` |
| `topic_token` | ❌ | Google's editorial ordering (recency-weighted) |
| `section_token` | ❌ | Same |
| `story_token` | ❌ | Same |
| `publication_token` | ❌ | Same |
Sending `so` with a non-search driver returns `400 so is only valid with q or kgmid (search mode)`.
Handling Stale Tokens [#handling-stale-tokens]
Tokens rotate occasionally. Symptoms:
* `502 unexpected response` from the API.
* A previously-working token suddenly returns no results.
**Recovery:** fetch a fresh token from a recent response and retry. Don't store tokens long-term; treat them as ephemeral cursors. The two stable identifiers are `q` (keyword strings) and `kgmid` (entity IDs).
```bash
# Pseudocode for a resilient navigator
fresh_response=$(curl -s "https://api.scrape.do/plugin/google/news?q=$KEYWORD&token=$TOKEN")
fresh_topic=$(echo "$fresh_response" | jq -r '.related_topics[0].topic_token // empty')
# Use the fresh token even if you had one cached
curl "https://api.scrape.do/plugin/google/news?topic_token=$fresh_topic&token=$TOKEN"
```
***
Error Handling [#error-handling]
```json
{ "error": "error_code", "message": "Human readable error message" }
```
Common Error Codes [#common-error-codes]
| Status | Error | Description |
| ------ | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |
| `400` | `token is required` | Missing API token |
| `400` | `one of q, topic_token, section_token, story_token, publication_token, kgmid is required` | No driver parameter set |
| `400` | `exactly one of q, topic_token, section_token, story_token, publication_token, kgmid may be set` | More than one driver parameter set |
| `400` | `invalid google_domain` | Unrecognized Google domain |
| `400` | `so must be 0 (relevance) or 1 (date)` | Invalid `so` value |
| `400` | `so is only valid with q or kgmid (search mode)` | `so` passed with a non-search driver |
| `502` | `request failed` | Transient. Retry |
| `502` | `unexpected response` | Upstream returned an unexpected page (often a stale `*_token`). **Fetch a fresh token from a recent response and retry** |
| `500` | `decompression failed` / `failed to parse news results` | Transient. Retry |
***
# Google Play Store API (/documentation/google-scraper-api/play-store)
The Google Play Store API returns structured data from the Play Store across three endpoints:
1. **Search**: find apps by query, category, or top-chart and get titles, ratings, prices, and package identifiers.
2. **App Details**: fetch the full details page for a single app (description, version, downloads, screenshots, release notes, developer info).
3. **Reviews**: pull paginated user reviews with ratings, dates, app version, and developer responses.
**Credit Usage:** Each successful request costs **10 credits**. For bulk processing, use the [Async API with plugins](/documentation/async-api/plugins/).
Key Features [#key-features]
* **Three endpoints, one schema family**: search, product, and reviews all return structured JSON with consistent field naming.
* **Search by query, category, or chart**: keyword search, Play Store category browsing (`GAME_PUZZLE`, `MEDICAL`, …), or top-chart listings (`topselling_free`, `topselling_paid`, `topgrossing`).
* **Device filters**: restrict search results to `phone`, `tablet`, `tv`, `chromebook`, `watch`, or `car`.
* **Family / age filters**: when browsing the `FAMILY` category, filter by age bucket.
* **Localization**: per-request `hl` (language) and `gl` (country) for region-specific results.
* **Paginated reviews**: 20 reviews per page, unlimited depth via `next_page_token`.
* **Developer responses**: review payloads include the developer's reply when present.
***
Search Endpoint [#search-endpoint]
Returns a list of apps matching your query, category, or chart.
```
GET https://api.scrape.do/plugin/google/play-store
```
Basic Example [#basic-example]
```bash
curl "https://api.scrape.do/plugin/google/play-store?token=$TOKEN&q=weather"
```
You must provide at least one of `q`, `apps_category`, or `chart`.
Request Parameters [#request-parameters]
Required [#required]
| Parameter | Type | Description |
| --------- | ------ | --------------------------------------- |
| `token` | string | Your Scrape.do API authentication token |
Search (provide at least one) [#search-provide-at-least-one]
| Parameter | Type | Description |
| --------------- | ------ | ---------------------------------------------------------------------- |
| `q` | string | Search query (e.g., `weather`, `fitness tracker`) |
| `apps_category` | string | Category identifier (e.g., `ART_AND_DESIGN`, `MEDICAL`, `GAME_PUZZLE`) |
| `chart` | string | Top charts: `topselling_free`, `topselling_paid`, `topgrossing` |
Localization [#localization]
| Parameter | Type | Default | Description |
| --------- | ------ | ------- | -------------------------------------- |
| `hl` | string | `en` | Language code (e.g., `en`, `es`, `fr`) |
| `gl` | string | `us` | Country code (e.g., `us`, `gb`, `de`) |
Pagination [#pagination]
| Parameter | Type | Default | Description |
| ----------------- | ------- | ------- | ------------------------------------------------------------------------------ |
| `num` | integer | `20` | Number of results per page (1–100) |
| `next_page_token` | string | | From the previous response's `pagination.next_page_token`. Pass back unchanged |
Filters [#filters]
| Parameter | Type | Description |
| -------------- | ------ | -------------------------------------------------------------------------------------------------------------------- |
| `store_device` | string | Device type: `phone`, `tablet`, `tv`, `chromebook`, `watch`, `car` |
| `age` | string | Age filter (only valid with `apps_category=FAMILY`): `AGE_RANGE1` (up to 5), `AGE_RANGE2` (6–8), `AGE_RANGE3` (9–12) |
More Examples [#more-examples]
```bash
# Search apps
curl "https://api.scrape.do/plugin/google/play-store?token=$TOKEN&q=coffee"
# Search with localization
curl "https://api.scrape.do/plugin/google/play-store?token=$TOKEN&q=fitness&gl=de&hl=de"
# Browse by category
curl "https://api.scrape.do/plugin/google/play-store?token=$TOKEN&apps_category=GAME_PUZZLE"
# Top free charts
curl "https://api.scrape.do/plugin/google/play-store?token=$TOKEN&chart=topselling_free"
# Paginate with 10 results per page
curl "https://api.scrape.do/plugin/google/play-store?token=$TOKEN&q=coffee&num=10"
```
Response [#response]
```json
{
"search_parameters": {
"q": "coffee",
"hl": "en",
"gl": "us",
"store": "apps"
},
"organic_results": [
{
"items": [
{
"title": "Good Coffee, Great Coffee",
"link": "https://play.google.com/store/apps/details?id=com.tapblaze.coffeebusiness",
"product_id": "com.tapblaze.coffeebusiness",
"rating": 4.4,
"author": "TapBlaze",
"video": "https://www.youtube.com/embed/RFUNyVrGCzE?...",
"thumbnail": "https://play-lh.googleusercontent.com/..."
},
{
"title": "Coffee Roaster",
"link": "https://play.google.com/store/apps/details?id=de.brettspielwelt.coffee_roaster",
"product_id": "de.brettspielwelt.coffee_roaster",
"rating": 3.8,
"author": "Brettspielwelt GmbH",
"price": "$3.99",
"extracted_price": 3.99,
"thumbnail": "https://play-lh.googleusercontent.com/...",
"feature_image": "https://play-lh.googleusercontent.com/..."
}
]
}
],
"pagination": {
"next_page_token": "eyJvIjoyMH0"
}
}
```
organic_results[].items[] [#organic_resultsitems]
| Field | Type | Description |
| ----------------- | ------ | --------------------------------------------------------- |
| `title` | string | App name |
| `link` | string | Google Play Store URL |
| `product_id` | string | Package identifier (e.g., `com.example.app`) |
| `rating` | float | Rating 0–5. Omitted if unrated |
| `author` | string | Developer name |
| `price` | string | Price string (e.g., `$3.99`). Omitted for free apps |
| `extracted_price` | float | Numeric price. Omitted for free apps |
| `video` | string | YouTube embed URL. Present when the app has a promo video |
| `thumbnail` | string | App icon URL |
| `feature_image` | string | Feature banner or screenshot URL. Present when available |
pagination [#pagination-1]
| Field | Type | Description |
| ----------------- | ------ | -------------------------------------------------------------------------------------- |
| `next_page_token` | string | Pass this value as `next_page_token` to get the next page. Absent when no more results |
***
App Details Endpoint [#app-details-endpoint]
Returns the full details page for one app: title, description, developer, version, downloads, screenshots, release notes, and more.
```
GET https://api.scrape.do/plugin/google/play-store/product
```
Basic Example [#basic-example-1]
```bash
curl "https://api.scrape.do/plugin/google/play-store/product?token=$TOKEN&product_id=com.discord"
```
Request Parameters [#request-parameters-1]
Required [#required-1]
| Parameter | Type | Description |
| ------------ | ------ | ------------------------------------------------------------ |
| `token` | string | Your Scrape.do API authentication token |
| `product_id` | string | App package identifier (e.g., `com.discord`, `com.whatsapp`) |
Localization [#localization-1]
| Parameter | Type | Default | Description |
| --------- | ------ | ------- | ------------- |
| `hl` | string | `en` | Language code |
| `gl` | string | `us` | Country code |
More Examples [#more-examples-1]
```bash
# Default (US / English)
curl "https://api.scrape.do/plugin/google/play-store/product?token=$TOKEN&product_id=com.discord"
# Localized to Germany
curl "https://api.scrape.do/plugin/google/play-store/product?token=$TOKEN&product_id=com.duolingo&gl=de&hl=de"
```
Response [#response-1]
```json
{
"search_parameters": {
"product_id": "com.discord",
"hl": "en",
"gl": "us"
},
"title": "Discord - Talk, Play, Hang Out",
"product_id": "com.discord",
"description": "Discord is designed for gaming and great for just chilling with friends ...",
"category": "Communication",
"category_id": "COMMUNICATION",
"content_rating": "Teen",
"rating": 4.28,
"rating_count": 6769967,
"free": true,
"downloads": "500,000,000+",
"in_app_purchases": "$1.99 - $274.94 per item",
"version": "327.12 - Stable",
"min_android": "7.0",
"updated": "May 8, 2026",
"release_notes": "We've been hard at work making Discord better for you. ...",
"icon": "https://play-lh.googleusercontent.com/...",
"header_image": "https://play-lh.googleusercontent.com/...",
"screenshots": [
"https://play-lh.googleusercontent.com/...",
"https://play-lh.googleusercontent.com/..."
],
"developer": {
"name": "Discord Inc.",
"url": "https://discord.com/",
"email": "support@discord.com"
}
}
```
Top-Level Fields [#top-level-fields]
| Field | Type | Description |
| ------------------ | --------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `title` | string | App display name |
| `product_id` | string | Package identifier |
| `description` | string | Long-form description |
| `category` | string | Display category (e.g., `"Communication"`) |
| `category_id` | string | Category identifier (e.g., `"COMMUNICATION"`) |
| `content_rating` | string | `"Everyone"`, `"Teen"`, `"Mature 17+"`, etc. |
| `rating` | float | Average rating, 1–5 |
| `rating_count` | int | Total number of ratings |
| `price` | string | Price + currency. Omitted on free apps |
| `free` | bool | `true` if the app is free |
| `downloads` | string | Download bucket (e.g., `"500,000,000+"`) |
| `in_app_purchases` | string | Price range for IAPs. Omitted if none |
| `version` | string | Current published version. Absent when the developer ships device-targeted bundles ("Varies with device" in the Play Store UI) |
| `min_android` | string | Minimum Android version (e.g., `"7.0"`) |
| `updated` | string | Last-updated date (e.g., `"May 8, 2026"`) |
| `release_notes` | string | What's new text. Omitted if not published |
| `icon` | string | App icon URL |
| `header_image` | string | Feature graphic / header image URL |
| `screenshots` | string\[] | Screenshot URLs |
developer [#developer]
| Field | Type | Description |
| --------- | ------ | ------------------------------------------ |
| `name` | string | Developer / publisher name |
| `url` | string | Developer website |
| `email` | string | Developer contact email |
| `address` | string | Physical address. Omitted if not published |
***
Reviews Endpoint [#reviews-endpoint]
Returns user reviews for a specific app, with pagination support.
```
GET https://api.scrape.do/plugin/google/play-store/reviews
```
Basic Example [#basic-example-2]
```bash
curl "https://api.scrape.do/plugin/google/play-store/reviews?token=$TOKEN&product_id=com.tapblaze.coffeebusiness"
```
Request Parameters [#request-parameters-2]
Required [#required-2]
| Parameter | Type | Description |
| ------------ | ------ | ------------------------------------------------------------ |
| `token` | string | Your Scrape.do API authentication token |
| `product_id` | string | App package identifier (e.g., `com.tapblaze.coffeebusiness`) |
Localization [#localization-2]
| Parameter | Type | Default | Description |
| --------- | ------ | ------- | ------------- |
| `hl` | string | `en` | Language code |
| `gl` | string | `us` | Country code |
Sorting & Pagination [#sorting--pagination]
| Parameter | Type | Default | Description |
| ----------------- | ------- | ------- | ------------------------------------------------------------------------------ |
| `sort_by` | integer | `1` | Sort order. Currently only `1` (most relevant) is supported |
| `next_page_token` | string | | From the previous response's `pagination.next_page_token`. Pass back unchanged |
More Examples [#more-examples-2]
```bash
# First page of reviews
curl "https://api.scrape.do/plugin/google/play-store/reviews?token=$TOKEN&product_id=com.tapblaze.coffeebusiness"
# Next page using the returned token
curl "https://api.scrape.do/plugin/google/play-store/reviews?token=$TOKEN&product_id=com.tapblaze.coffeebusiness&next_page_token=CrkBIrAB..."
```
Response [#response-2]
```json
{
"search_parameters": {
"product_id": "com.tapblaze.coffeebusiness",
"hl": "en",
"gl": "us",
"store": "apps",
"sort_by": 1,
"num": 20
},
"reviews": [
{
"id": "707d91c4-0510-455d-9288-71eddf9784be",
"title": "Rebecca Saurer",
"avatar": "https://play-lh.googleusercontent.com/a-/ALV-...",
"rating": 4,
"snippet": "generally speaking, this games great! ...",
"likes": 52,
"date": "February 16, 2026",
"iso_date": "2026-02-16T02:26:14Z",
"app_version": "1.18.0",
"response": {
"title": "TapBlaze",
"snippet": "Hello! Thank you so much for your feedback ...",
"date": "February 16, 2026",
"iso_date": "2026-02-16T23:30:38Z"
}
}
],
"pagination": {
"next_page_token": "CrkBIrABAYw1..."
}
}
```
reviews[] [#reviews]
| Field | Type | Description |
| ------------- | ------ | -------------------------------------------------------------------------- |
| `id` | string | Unique review identifier |
| `title` | string | Reviewer name |
| `avatar` | string | Reviewer avatar URL |
| `rating` | float | Rating 1–5 |
| `snippet` | string | Review text |
| `likes` | int | Number of people who found this review helpful |
| `date` | string | Human-readable date (e.g., `"February 16, 2026"`) |
| `iso_date` | string | ISO 8601 date |
| `app_version` | string | App version installed when the review was written. Absent on older reviews |
| `response` | object | Developer response. Absent if the developer hasn't replied |
reviews[].response [#reviewsresponse]
| Field | Type | Description |
| ---------- | ------ | ------------------- |
| `title` | string | Developer name |
| `snippet` | string | Response text |
| `date` | string | Human-readable date |
| `iso_date` | string | ISO 8601 date |
pagination [#pagination-2]
| Field | Type | Description |
| ----------------- | ------ | -------------------------------------------------------------------------------------- |
| `next_page_token` | string | Pass this value as `next_page_token` to get the next page. Absent when no more results |
***
Notes [#notes]
* Search returns up to \~30 apps total. With the default `num=20`, you get two pages.
* Reviews return 20 per page with unlimited pagination depth.
* The `rating` field is omitted for apps and reviews that have no rating.
* The `response` field on reviews is only present when the developer has replied.
* On the product endpoint, `version` is absent for apps that publish device-targeted bundles (these show as "Varies with device" in the Play Store UI). For per-review installed versions, use `app_version` on each review.
**Known limitation:** `apps_category=MEDICAL` currently returns `500 failed to parse play store results`. Other categories (`GAME_PUZZLE`, `FAMILY`, `COMMUNICATION`, etc.) work normally. Browsing the medical app catalog via `q=medical` returns matching apps.
# Transient Errors & Retry Guidance (/documentation/google-scraper-api/transient-errors)
Google's data surfaces occasionally return an empty response shell even when the underlying data exists. Every Google Scraper sub-API surfaces this as either a `502 no results` error or, less commonly, a `200` response with an empty results array. **A single retry recovers the data \~99% of the time.** This page documents the pattern so your crawlers handle it gracefully.
***
When You'll See It [#when-youll-see-it]
| Symptom | Plugins where it appears | Typical cause |
| ------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `502 {"error":"no results"}` | Search · Maps · Maps Reviews · Flights · Hotels · News (with stale token) | Google returned an empty data shell; plugin parses zero results |
| `200 { "shopping_results": [] }` | Shopping (deeper paginated pages) | Same upstream cause; Shopping plugin returns 200 since the response was structurally valid |
| `502 {"error":"unexpected response"}` | News only | Stale `*_token`; fetch a fresh one from a recent search response |
| `410 {"error":"session expired"}` | Shopping Product | `immersive_product_page_token` aged out (typically several hours after mint). Re-mint via `/plugin/google/shopping` |
| `502 {"error":"request failed"}` | All | Generic upstream failure; safe to retry |
***
Empirical Picture [#empirical-picture]
Based on 260 stress requests across the Google Scraper sub-APIs (excluding outlier query patterns):
| Plugin family | First-try success | After one retry |
| --------------------------------------------- | ----------------: | --------------: |
| Most queries | **99%** | **\~100%** |
| Edge cases (deep pagination on dense queries) | 60–80% | 96–99% |
The takeaway: **build retry-once into your client by default.** A single retry takes the worst-case cell from 60% to 96% effective success.
***
Retry-Once Pattern (Python) [#retry-once-pattern-python]
```python
import time
import requests
def fetch_with_retry(url, max_attempts=2):
for attempt in range(1, max_attempts + 1):
r = requests.get(url, timeout=120)
try:
data = r.json()
except Exception:
data = None
# Success: 200 with non-empty primary results
if r.status_code == 200 and data and any(
(data.get(k) or []) for k in
("local_results", "shopping_results", "news_results",
"properties", "best_flights", "reviews")
):
return data
# Transient empty/502 — retry
if attempt < max_attempts:
time.sleep(0.5)
continue
# Final attempt failed — return whatever we got
r.raise_for_status()
return data
# Usage
data = fetch_with_retry(
"https://api.scrape.do/plugin/google/maps/search?q=pizza+new+york&token=YOUR_TOKEN"
)
```
***
What NOT to Treat as Transient [#what-not-to-treat-as-transient]
| Symptom | Treat as |
| ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- |
| `400` with a clear validation message | Permanent. Fix the request |
| `200` with `error` field present (validation that returned 200 in some legacy paths) | Permanent. Fix the request |
| `502 no results` after 2–3 retries with the same parameters | Likely a real empty result (e.g., `start=200` past the end of the result set) |
| `500` with a parser error message | Possibly a new upstream HTML/JSON shape. Log it and report to support |
***
News-Specific: Stale Tokens [#news-specific-stale-tokens]
The Google News API uses opaque tokens (`topic_token`, `section_token`, `story_token`, `publication_token`) that **rotate occasionally**. When a stored token stops working, the API returns:
```json
{
"error": "unexpected response",
"message": "Unexpected response. Please try again, and if the issue continues, contact support."
}
```
Recovery: fetch a fresh token from a recent `q=` response and retry. Don't store tokens long-term; treat them as ephemeral cursors. The two stable identifiers are `q` (keyword strings) and `kgmid` (Knowledge Graph entity IDs).
See [News navigation](/documentation/google-scraper-api/news) for the full chaining playbook.
***
Session Expiry on Immersive Product Tokens [#session-expiry-on-immersive-product-tokens]
The Google Shopping Product API uses an opaque `immersive_product_page_token` minted by the search endpoint. Each token is tied to an upstream Google product-viewer session that **ages out after several hours**. When a stored token is past its lifetime, `/plugin/google/shopping/product` returns HTTP `410 Gone`:
```json
{
"error": "session expired",
"message": "The immersive_product_page_token is older than 6h0m0s and is past Google's product-viewer session lifetime. Fetch /plugin/google/shopping again to mint a fresh token, then retry."
}
```
(The exact lifetime in the message may vary; the recovery is always the same.)
Recovery: re-fetch `/plugin/google/shopping` with the same query, grab a fresh `immersive_product_page_token` from a `shopping_results[]` entry, then retry the product call with the new token. Don't store tokens long-term; treat them as ephemeral cursors. The stable identifier across re-mints is the search query (`q`), which always resolves to the same product cluster (`gpcid`) in the result set.
See [Google Shopping Product](/documentation/google-scraper-api/shopping/product#handling-session-expiry) for the per-endpoint details.
***
import { Cards, Card } from 'fumadocs-ui/components/card';
# YouTube Video API (/documentation/google-scraper-api/youtube-video)
The YouTube Video API returns structured details for one video, including its channel, description, chapters, related and end-screen videos, comments, and pagination tokens. It does not require JavaScript rendering or add a render fee.
**Credit Usage:** Each request costs **10 credits**, including continuation requests. For bulk processing, use the [Async API with plugins](/documentation/async-api/plugins/).
Key Features [#key-features]
* **Machine-readable publication time**: `published_at` is an RFC 3339 timestamp in UTC, suitable for sorting and video-age calculations.
* **Stable channel identifiers**: `channel.channel_id` and `comments[].author.channel_id` remain stable when a channel changes its display name or handle.
* **Exact numeric counts**: `extracted_views`, `extracted_likes`, and `extracted_subscribers` complement YouTube's formatted display strings.
* **Complete video metadata**: Retrieve the description and links, chapters, related videos, end-screen videos, and transcript availability.
* **Comments and replies**: Use continuation tokens to load comments, sort by newest or top, and fetch replies.
* **Localization**: Control display strings with `hl` and influence related-video ranking with `gl`.
***
Endpoint [#endpoint]
```text
GET https://api.scrape.do/plugin/google/youtube/video
```
Request Parameters [#request-parameters]
Required [#required]
| Parameter | Type | Description |
| --------- | ------ | ---------------------------------------------------------------------------------------------------------- |
| `token` | string | Your Scrape.do API authentication token |
| `v` | string | The 11-character YouTube video ID from a `watch?v=...` URL. Required for initial and continuation requests |
Localization [#localization]
| Parameter | Type | Default | Description |
| --------- | ------ | ------- | ----------------------------------------------------------------------------------------------------------------- |
| `hl` | string | `en` | Interface language, such as `en`, `de`, `fr`, `ja`, `tr`, or `pt-br`. Controls localized dates and count suffixes |
| `gl` | string | `us` | Two-letter country code, such as `us`, `gb`, `de`, `fr`, or `jp`. Influences related-video ranking |
Pagination [#pagination]
| Parameter | Type | Description |
| ----------------- | ------ | ---------------------------------------------------------------------------------------------------------------- |
| `next_page_token` | string | A continuation token from a previous response. Accepts related-video, comment, comment-sorting, and reply tokens |
The endpoint detects the token type automatically and returns the corresponding collection.
***
Example Requests [#example-requests]
Video Details [#video-details]
```bash
curl "https://api.scrape.do/plugin/google/youtube/video?v=dQw4w9WgXcQ&token=$TOKEN"
```
Localized Details [#localized-details]
```bash
curl "https://api.scrape.do/plugin/google/youtube/video?v=dQw4w9WgXcQ&hl=fr&gl=fr&token=$TOKEN"
```
More Related Videos [#more-related-videos]
Pass `related_videos_next_page_token` from the previous response:
```bash
curl "https://api.scrape.do/plugin/google/youtube/video?v=dQw4w9WgXcQ&next_page_token=CBQSDRIL...&token=$TOKEN"
```
Comments or Replies [#comments-or-replies]
Pass `comments_next_page_token`, a token from `comments_sorting_token`, or a comment's `replies_next_page_token`:
```bash
curl "https://api.scrape.do/plugin/google/youtube/video?v=dQw4w9WgXcQ&next_page_token=Eg0SC2RR...&token=$TOKEN"
```
***
Response Modes [#response-modes]
The endpoint uses one response schema with three modes. Fields that do not apply to the current mode are omitted.
| Mode | Trigger | Populated fields |
| ------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| Video details | No `next_page_token` | Video metadata, channel, description, chapters, related videos, end-screen videos, comment tokens, and transcript token |
| Related videos | `related_videos_next_page_token` | `related_videos` and a new `related_videos_next_page_token` |
| Comments or replies | Comment, sorting, or reply token | `comments` and, when available, `comments_next_page_token` |
Video Details Response [#video-details-response]
```json
{
"search_parameters": {
"engine": "google_youtube_video",
"v": "dQw4w9WgXcQ",
"gl": "us",
"hl": "en"
},
"title": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)",
"thumbnail": "https://i.ytimg.com/vi_webp/dQw4w9WgXcQ/maxresdefault.webp",
"channel": {
"name": "Rick Astley",
"channel_id": "UCuAXFkgsw1L7xaCfnd5JJOw",
"link": "https://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw",
"thumbnail": "https://yt3.ggpht.com/...",
"subscribers": "4.51M subscribers",
"extracted_subscribers": 4510000,
"verified": true
},
"views": "1,790,053,431 views",
"extracted_views": 1790053431,
"likes": "19M",
"extracted_likes": 19222757,
"published_date": "Oct 24, 2009",
"published_at": "2009-10-25T06:57:33Z",
"description": { "content": "The official video...", "links": [] },
"chapters": [],
"related_videos": [],
"related_videos_next_page_token": "CBQSDRILZFF3NHc5V2dYY1E...",
"end_screen_videos": [],
"comments_next_page_token": "Eg0SC2RRdzR3OVdnWGNR...",
"comments_sorting_token": [],
"transcript": { "token": "CgtkUXc0dzlXZ1hjUQ..." }
}
```
Publication Fields [#publication-fields]
| Field | Type | Description |
| ---------------- | ------ | ----------------------------------------------------------------------------------------------- |
| `published_date` | string | YouTube's day-precision display date, localized by `hl`, such as `Oct 24, 2009` or `3 Nis 2016` |
| `published_at` | string | RFC 3339 publication timestamp, always in UTC, such as `2009-10-25T06:57:33Z` |
Use `published_at` for chronological sorting, precise video age, and rate-per-hour calculations. Use `published_date` when you need the text shown for the requested locale. On rare videos where YouTube does not provide an exact timestamp, `published_at` is omitted while `published_date` can still be present.
channel [#channel]
| Field | Type | Description |
| ----------------------- | ------- | ----------------------------------------------------------------------------------- |
| `name` | string | Channel display name |
| `channel_id` | string | Stable `UC...` channel identifier |
| `link` | string | Absolute channel URL. YouTube may return either an `@handle` or `/channel/` URL |
| `thumbnail` | string | Channel avatar URL |
| `subscribers` | string | Formatted subscriber count, localized by `hl` |
| `extracted_subscribers` | number | Parsed numeric subscriber count |
| `verified` | boolean | `true` for verified channels and official artist channels. Omitted when `false` |
Use `channel_id` as the durable identifier instead of parsing `link`, because channel handles and display names can change.
Views and Likes [#views-and-likes]
| Field | Type | Description |
| ----------------- | ------ | ----------------------------------------------------------------- |
| `views` | string | Formatted view count as displayed by YouTube |
| `extracted_views` | number | Exact numeric view count |
| `likes` | string | Compact like count as displayed on the like button, such as `19M` |
| `extracted_likes` | number | Exact numeric like count |
description [#description]
```json
{
"content": "The official video for Never Gonna Give You Up...",
"links": [
{
"start_index": 180,
"length": 16,
"text": "#rickastleynever",
"url": "https://www.youtube.com/hashtag/rickastleynever"
}
]
}
```
`links[].start_index` and `links[].length` use UTF-16 code units, matching JavaScript string indexing. External targets use YouTube's redirect URL when that is what appears on the page.
chapters [#chapters]
Present only when the creator has defined chapters.
```json
[
{
"title": "Introduction",
"thumbnail": "https://i.ytimg.com/vi/...",
"time_start": 0
},
{
"title": "Variables",
"thumbnail": "https://i.ytimg.com/vi/...",
"time_start": 208
}
]
```
`time_start` is the chapter's start offset in seconds.
related_videos [#related_videos]
The initial response usually includes about 20 sidebar suggestions. Use `related_videos_next_page_token` to retrieve more.
```json
[
{
"video_id": "u_cLVh73u_o",
"link": "https://www.youtube.com/watch?v=u_cLVh73u_o",
"thumbnail": {
"static": "https://i.ytimg.com/vi/u_cLVh73u_o/hqdefault.jpg",
"rich": "https://i.ytimg.com/an_webp/u_cLVh73u_o/mqdefault_6s.webp"
},
"title": "80'S MUSIC ON THE ROAD",
"published_date": "6 days ago",
"views": "421K views",
"extracted_views": 421000,
"length": "2:16:49",
"channel": {
"name": "Every Song Is A Scar",
"link": "https://www.youtube.com/@everysongisascar",
"verified": true
}
}
]
```
`thumbnail.rich` is an animated preview and is omitted when unavailable. A live suggestion has `live: true` and a watcher count, so `views`, `extracted_views`, and `published_date` are omitted. Shorts shelves between suggestions are not included.
end_screen_videos [#end_screen_videos]
End-screen cards include fields such as `video_id`, `link`, `title`, `thumbnail`, `views`, `extracted_views`, and `length`. YouTube does not display a publication date or channel on these cards, so those fields are absent.
comments_sorting_token [#comments_sorting_token]
```json
[
{ "title": "Top", "token": "Eg0SC2RRdzR3OVdnWGNR..." },
{ "title": "Newest", "token": "Eg0SC2RRdzR3OVdnWGNR..." }
]
```
Pass either token as `next_page_token` to load comments in that order. The initial `comments_next_page_token` uses the default Top ordering.
transcript [#transcript]
```json
{ "token": "CgtkUXc0dzlXZ1hjUQ..." }
```
This opaque token indicates that the video has a transcript. It is reserved for future use; this endpoint does not return transcript text.
***
Comments Response [#comments-response]
Comment pages contain 20 items per batch. Reply pages contain 10 items per batch.
```json
{
"search_parameters": {
"engine": "google_youtube_video",
"v": "dQw4w9WgXcQ",
"gl": "us",
"hl": "en",
"next_page_token": "Eg0SC2RR..."
},
"comments": [
{
"comment_id": "UgzB3ZO9YOWY0PJqmZR4AaABAg",
"content": "can confirm: he never gave us up",
"published_date": "1 year ago",
"likes": "265K",
"extracted_likes": 265000,
"replies_count": 961,
"pinned": true,
"author": {
"name": "@YouTube",
"link": "https://www.youtube.com/@YouTube",
"channel_id": "UCBR8-60-B28hp2BmDPdntcQ",
"thumbnail": "https://yt3.ggpht.com/...",
"verified": true
},
"replies_next_page_token": "Eg0SC2RRdzR3OVdnWGNR..."
}
],
"comments_next_page_token": "Eg0SC2RRdzR3OVdnWGNR..."
}
```
| Field | Type | Description |
| ------------------------- | ------- | ----------------------------------------------------------------------- |
| `comment_id` | string | Stable comment identifier |
| `content` | string | Comment text |
| `published_date` | string | Relative date localized by `hl`; may include an `(edited)` suffix |
| `likes` | string | Formatted like count |
| `extracted_likes` | number | Parsed numeric like count |
| `replies_count` | number | Number of replies. Omitted when `0` |
| `pinned` | boolean | `true` for a creator-pinned comment. Omitted when `false` |
| `author.channel_id` | string | Stable identifier for the comment author's channel |
| `author.is_creator` | boolean | `true` when the video's creator wrote the comment. Omitted when `false` |
| `replies_next_page_token` | string | Pass as `next_page_token` to fetch replies |
Reply batches use the same `comments` item shape. They omit `replies_next_page_token` and use `comments_next_page_token` for additional replies in the same thread.
***
Pagination Guidelines [#pagination-guidelines]
* Treat every continuation token as opaque and pass it back unchanged.
* Tokens belong to a specific video, collection, and position. Do not reuse them across videos.
* Stop when the response no longer includes the relevant next-page token.
* Keep `v` on every continuation request. The continuation token selects the underlying video content.
* Tokens containing percent sequences can be passed as returned or URL-encoded with `curl -G --data-urlencode`.
***
Error Handling [#error-handling]
| Status | Error | Cause |
| ------ | -------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| `400` | `token is required` | Missing API token |
| `400` | `v is required` | Missing video ID |
| `400` | `v must be an 11-character YouTube video id` | Malformed video ID |
| `400` | `gl must be a 2-letter country code` | Invalid country code |
| `400` | `hl must be a valid language code (e.g., en, de, pt-br)` | Invalid language code |
| `401` | Gateway authorization error | Invalid or inactive API token |
| `429` | Gateway rate-limit error | Concurrent-request or monthly quota limit reached |
| `502` | `upstream returned truncated page` | YouTube returned a partial page. Retry the request |
| `502` | `failed to parse video results` | Invalid or expired continuation token, unavailable video, or unexpected upstream response |
| `502` | `unexpected response` | YouTube returned a non-200 status. Retry the request |
| `502` | `request failed` | Network-level failure. Retry the request |
| `500` | `decompression failed` | Response decompression failed |
| `500` | `internal server error` | Unhandled server error. Report it if persistent |
Notes [#notes]
* Private, removed, age-gated, or region-blocked videos can return `502 failed to parse video results` and are not charged.
* Optional fields are omitted when YouTube does not provide them; one unavailable field does not fail an otherwise valid response.
* Display strings follow `hl`, while `extracted_*` fields are normalized to integers when the value can be determined safely.
* Match `hl` and `gl`, such as `hl=fr&gl=fr`, for consistent localized metadata and regional ranking.
# Custom Headers (/documentation/headers-cookies/custom-headers)
You can interfere with all headers sent to the target website via our service. Scrape.do sends User-Agent, Accept, Cookies, etc. from you. It takes all header parameters and forwards them. You must set `customHeaders=True` to use the feature.
If you leave the User-Agent information blank in the header field of the requests sent via programming languages, it can be filled with a default value like 'python/httpclient'. Therefore, we strongly recommend that you use a real User-Agent with this feature.
**Result**
```json
{
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "en-US,en;q=0.9",
"Authority": "httpbin.co",
"Cache-Control": "no-cache,no-cache",
"Host": "httpbin.co",
"Referer": "https://httpbin.co",
"Sec-Ch-Ua": "\"Google Chrome\";v=\"107\", \"Chromium\";v=\"107\", \"Not=A?Brand\";v=\"24\"",
"Sec-Ch-Ua-Mobile": "?0",
"Sec-Ch-Ua-Platform": "\"macOS\"",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
"Test-Header-Key": "TestValue",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "PostmanRuntime/7.29.2",
"X-Amzn-Trace-Id": "Root=1-63748965-50104cf629bc898d03188f57"
}
}
```
# Extra Headers (/documentation/headers-cookies/extra-headers)
It is a feature that you can use only when there is one or more header parameters that you want to use for the needs of the target website, without breaking the requests we have created for the target website.
After setting the query parameter as `extraHeaders=true`, the header values you will send with the `Sd-**` prefix will be sent to the target website.
**Example Result**
```json
{
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "en-US,en;q=0.9",
"Authority": "httpbin.co",
"Cache-Control": "no-cache",
"Host": "httpbin.co",
"Referer": "https://httpbin.co",
"Sec-Ch-Ua": "\"Google Chrome\";v=\"107\", \"Chromium\";v=\"107\", \"Not=A?Brand\";v=\"24\"",
"Sec-Ch-Ua-Mobile": "?0",
"Sec-Ch-Ua-Platform": "\"macOS\"",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
"Test": "TestValue",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36",
"X-Amzn-Trace-Id": "Root=1-63748c46-2e20cdb45d2d89455b4e4fd0"
}
}
```
# Forward Headers (/documentation/headers-cookies/forward-headers)
You may need to forward your own headers to the target web page you want to scrape without auto generated headers. In this case you can use `forwardHeaders=True` query parameter.
**Result**
```json
{
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br",
"Host": "httpbin.co",
"Test-Header-Key": "TestValue",
"User-Agent": "curl/7.85.0",
"X-Amzn-Trace-Id": "Root=1-63f5d502-4fa0a57019562b1b55fed4f6"
}
}
```
# Headers & Cookies (/documentation/headers-cookies)
Overview [#overview]
HTTP headers and cookies are critical components for successful web scraping. Modern websites use headers to identify legitimate traffic and cookies to maintain session state. Scrape.do provides three distinct approaches to header management, each suited for different use cases.
Header Management Strategies [#header-management-strategies]
* **[Custom Headers](/documentation/headers-cookies/custom-headers)** — Gives you complete control over all headers sent to the target website. Use this when you need to replicate specific browser behavior or handle complex authentication flows.
* **[Extra Headers](/documentation/headers-cookies/extra-headers)** — Allows you to add specific headers on top of Scrape.do's automatically generated ones. This is ideal when you need to include one or two custom headers (like API keys or tracking IDs) without disrupting the optimized header set.
* **[Forward Headers](/documentation/headers-cookies/forward-headers)** — Strips away all auto-generated headers and forwards only what you explicitly provide. Perfect for scenarios where you're working with APIs or need minimal header overhead.
Cookie Management [#cookie-management]
* **[Set Cookies](/documentation/headers-cookies/set-cookies)** — Maintain session state across multiple requests. Use the `setCookies` parameter to pass authentication cookies, session tokens, or any other cookie-based data required by your target website.
# Set Cookies (/documentation/headers-cookies/set-cookies)
{/* {{< parameter-snippet "setCookies" "url encoded cookies" "false">}} */}
You can pass cookies to the target website using `setCookies=cookie1=value1; cookie2=value2`, and our system will send the cookies you have specified to the target website.
You don't need to use **extraHeaders** or **customHeaders** parameters to send cookies. Simply use the **setCookies** parameter instead.
You should URL-encode your cookie values. For example: `setCookies=cookie1%3Dvalue1%3B`. You can see more details about URL encoding [here](/documentation/#encode-your-target-url).
**Example Result:**
```json
{
"cookies": {
"cookie1": "value1",
"cookie2": "value2",
"cookie3": "value3"
}
}
```
# Block Resources (/documentation/headless-browser/block-resources)
{/* {{< parameter-snippet "blockResources" "boolean" "true" >}} */}
By default, this feature is enabled so that results are loaded faster and returned to you more quickly when you navigate to a website through the headless browser. CSS files, images, and fonts are blocked to improve performance and reduce bandwidth usage.
If you want to turn this feature off, simply pass the `blockResources=false` parameter.
This feature may affect success rates for some target websites. Therefore, setting **false** on problematic web pages may increase your success rate if the site requires images or CSS to load properly.
# Browser Interactions (/documentation/headless-browser/browser-interactions)
Scrape.do supports basic JavaScript instructions to interact with the web page you want to scrape. You can create a list of instructions and send it to the system. The system will execute the instructions in the order you send them and return the result to you.
You should URL-encode the `playWithBrowser` query parameter value. You can see more details about URL encoding [**here**](/documentation/#encode-your-target-url).
Example of a script with multiple instructions:
```json
[
{ "Action": "Click", "Selector": "#button_id" },
{ "Action": "Wait", "Timeout": 5000 }
]
```
You can add multiple instructions to the script. Below are examples of all the different instructions you can use.
When you also pass `returnJSON=true`, the response includes an `actionResults` array reporting `success`, and any `error`, for each action in your script, in order. A failed selector lookup is reported there while the request itself still returns `200`.
Actions [#actions]
Click [#click]
To click a button, you will need to use this feature. Use it with the CSS selector of the button you want to click. The scraper will click on the #buttonId button and then return the HTML of the target page.
```json
[{ "Action": "Click", "Selector": "#button_id"}]
```
Wait [#wait]
Use the wait command with the time in ms you want to **"wait"** for when you need to wait for a fixed amount of time.
```json
[{ "Action": "Wait", "Timeout": 1000 }]
```
Wait Selector [#wait-selector]
If there is no expected content as a result of a successful return, you can wait for the element by specifying the desired time in ms with the **"wait-selector"** property.
```json
[{ "Action": "WaitSelector", "WaitSelector": "#btn", "Timeout": 1000 }]
```
Scroll Horizontally [#scroll-horizontally]
Use the **"scroll-x"** attribute with the number of pixels you want to scroll to scroll the target website page horizontally.
```json
[{ "Action": "ScrollX", "Value": 100 }]
```
Scroll Vertically [#scroll-vertically]
To scroll the target website page vertically, use the **"scroll-y"** attribute with the number of pixels you want to scroll.
```json
[{ "Action": "ScrollY", "Value": 100 }]
```
Scroll To [#scroll-to]
With the **"scroll-to"** property, you can scroll for x - y on a per-pixel basis.
```json
[{ "Action": "ScrollTo", "Selector": "#btn" }]
```
Fill [#fill]
Use the **"fill"** attribute to fill an entry on the target website page. Use it with the CSS selector of the input you want to fill and the value you want to fill it with.
```json
[{ "Action": "Fill", "Selector": "#input", "Value": "test" }]
```
Execute [#execute]
JavaScript code is executed. If you need to run custom JavaScript, you should use the **"execute"** property.
```json
[{ "Action": "Execute", "Execute": "window.document.cookie" }]
```
Screenshot [#screenshot]
The `returnJSON=true` parameter needs to be used.
```json
[{ "Action": "ScreenShot" }]
[{ "Action": "ScreenShot", "fullScreenShot": "true" }]
[{ "Action": "ScreenShot", "particularScreenShot": "#elementSelector" }]
```
WaitForRequestCompletion [#waitforrequestcompletion]
**"WaitForRequestCompletion"** waits for any network request matching the given urlPattern to complete or until the specified timeout is reached. Useful for waiting on dynamically loaded content such as APIs, images, scripts, or other resources.
```json
[{ "Action": "WaitForRequestCompletion", "UrlPattern": "*example.com/image*", "Timeout": 10000 }]
```
RealClick [#realclick]
Selects a random coordinate inside the clickable element and clicks it with operating system specific human behavior, instead of dispatching a synthetic click at the element center. Use it when a plain **"Click"** gets detected or ignored by the target site.
```json
[{ "Action": "RealClick", "Selector": "#buy-button" }]
```
If the click triggers a page navigation, use **ClickAndWait** instead so the navigation is tracked and completed before the result is returned.
RealAction [#realaction]
Simulates human behavior on the page for the given time period, specific to the operating system. On desktop it performs mouse movements and scroll wheel scrolling. On mobile it interacts with the page through touch events. Useful for warming up sessions on sites that score user behavior.
```json
[{ "Action": "RealAction", "Timeout": 10000 }]
```
Focus [#focus]
Triggers the focus event on an element, the same as a user tabbing or clicking into it. Useful before typing into inputs that only initialize on focus.
```json
[{ "Action": "Focus", "Selector": "#search-input" }]
```
Hover [#hover]
Triggers the hover event on an element. Useful for opening hover-only menus or lazy-loading content that appears on mouseover.
```json
[{ "Action": "Hover", "Selector": ".dropdown-menu" }]
```
Select [#select]
Changes the value of a `