SERP API
View as MarkdownSearch Google and get structured SERP data as JSON
The Search endpoint performs a Google search for any query and returns fully parsed, structured JSON data. You get organic results, ads, knowledge graphs, local packs, video results, related questions, and more. All extracted and organized without any HTML parsing on your end.
Endpoint
GET https://api.scrape.do/plugin/google/searchRequest Parameters
Required
| Parameter | Type | Description |
|---|---|---|
token | string | Your Scrape.do API authentication token |
q | string | Search query. URL-encode spaces and special characters. pizza+new+york or pizza%20new%20york |
General
| Parameter | Type | Default | Description |
|---|---|---|---|
device | string | desktop | Device type for SERP layout. Accepted values: desktop, mobile |
start | integer | 0 | Result offset for pagination. 0 = 1st page, 10 = 2nd page, 20 = 3rd page, etc. Maximum 100. When paginating, send it from the first request onward — see Pagination |
cache | boolean | true | Reuses a successful identical search for up to 6 hours. Set false to fetch a fresh page from Google instead. See Caching |
include_html | boolean | false | When true, the raw Google HTML is included in the response html field. Useful for debugging and custom parsing. If this query's AI Overview turns out to be deferred, passing include_html=true here also lets the follow-up endpoint return one combined page with the AI Overview content inserted into it |
resolveGoto | boolean | false | When true, Google redirect links left in the response (https://www.google.com/goto?url=…) are replaced with the destination URL each one points to, at no extra cost. See Result links |
Localization & Geo-targeting
| Parameter | Type | Default | Description |
|---|---|---|---|
hl | string | en | Host Language. Controls the language of the Google UI (buttons, labels, interface text). Supports 150+ languages. ISO 639-1 codes. Examples: tr, de, fr, ja, pt. Full list → |
gl | string | us | Geo Location (datacenter country). Determines from which country's perspective results are ranked and returned. Supports 240+ countries. ISO 3166-1 alpha-2 codes. Examples: tr, de, gb, jp. Full list → |
google_domain | string | google.com | Google domain to query. Supports 84 regional domains. Prefixes https://, http://, and www. are automatically stripped. Examples: google.com.tr, google.de, google.co.uk. Full list → |
location | string | - | Location name in Google's canonical format. Automatically encoded to UULE internally. Examples: Istanbul,Istanbul,Turkey, New York,New York,United States. Send gl together with it — when gl and location disagree, the country code wins, so location on its own returns US results |
uule | string | - | Google UULE-encoded location string. Auto-generated from location when not provided. If both location and uule are sent, uule takes priority. location is sufficient for most use cases |
Result Filtering
| Parameter | Type | Default | Description |
|---|---|---|---|
lr | string | - | Language Restrict. Filters results to pages written in a specific language only. Supports 35 languages. Format: lang_XX. Examples: lang_tr (Turkish), lang_en (English), lang_de (German). Full list → |
cr | string | - | Country Restrict. Filters results to pages originating from a specific country only. Supports 240+ countries. Format: countryXX. Examples: countryTR (Turkey), countryUS, countryDE. Full list → |
safe | string | - | SafeSearch. Send active to filter adult content from results |
nfpr | boolean | false | When true, disables Google's automatic spelling correction. The "Did you mean..." suggestion will not alter results |
filter | string | - | Controls "Similar Results" and "Omitted Results" filters. Send 0 to disable and show all results. Default (not sent or 1) = filters active |
time_period | string | - | Time-based filter. Accepted values: last_hour, last_day, last_week, last_month, last_year |
hl vs lr / gl vs cr: hl and gl set the search context. Google treats them as hints, so results may still include other languages or countries. lr and cr apply strict filtering. Only results matching the specified language or country are returned.
Location matching: search_parameters echoes back the location and uule you sent, but that confirms acceptance — not localization. Check local_results or the domains in organic_results to confirm. The location string must match Google's own name for the place; if it does not, the location is ignored silently and results fall back to the country in gl. Country names follow Google's current spelling — send Turkey and it is submitted as Turkiye, the name Google recognises.
Example Usage
Step 1: Define Your Search Query
Decide what you want to search for on Google. This can be:
- General queries:
pizza new york,best laptop 2026 - Local searches:
restaurants near me,hotels in istanbul - Informational queries:
how to learn python,what is web scraping
For this example, we'll search for "pizza new york":
Step 2: Send the API Request
curl --location --request GET 'https://api.scrape.do/plugin/google/search?token=<SDO-token>&q=pizza+new+york'import requests
import json
token = "<SDO-token>"
query = "pizza+new+york"
url = f"https://api.scrape.do/plugin/google/search?token={token}&q={query}"
response = requests.request("GET", url)
print(json.dumps(response.json(), indent=2))const axios = require('axios');
const token = "<SDO-token>";
const query = "pizza+new+york";
const url = `https://api.scrape.do/plugin/google/search?token=${token}&q=${query}`;
axios.get(url)
.then(response => {
console.log(JSON.stringify(response.data, null, 2));
})
.catch(error => {
console.error(error);
});package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
token := "<SDO-token>"
query := "pizza+new+york"
url := fmt.Sprintf(
"https://api.scrape.do/plugin/google/search?token=%s&q=%s",
token, query,
)
resp, err := http.Get(url)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}require 'net/http'
require 'json'
token = "<SDO-token>"
query = "pizza+new+york"
url = URI("https://api.scrape.do/plugin/google/search?token=#{token}&q=#{query}")
response = Net::HTTP.get(url)
puts JSON.pretty_generate(JSON.parse(response))import java.net.HttpURLConnection;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class GoogleSearch {
public static void main(String[] args) throws Exception {
String token = "<SDO-token>";
String query = "pizza+new+york";
String url = String.format(
"https://api.scrape.do/plugin/google/search?token=%s&q=%s",
token, query
);
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestMethod("GET");
BufferedReader reader = new BufferedReader(
new InputStreamReader(conn.getInputStream())
);
String line;
StringBuilder response = new StringBuilder();
while ((line = reader.readLine()) != null) {
response.append(line);
}
reader.close();
System.out.println(response.toString());
}
}using System;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
string token = "<SDO-token>";
string query = "pizza+new+york";
string url = $"https://api.scrape.do/plugin/google/search?token={token}&q={query}";
using HttpClient client = new HttpClient();
string response = await client.GetStringAsync(url);
Console.WriteLine(response);
}
}<?php
$token = "<SDO-token>";
$query = "pizza+new+york";
$url = "https://api.scrape.do/plugin/google/search?token={$token}&q={$query}";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo json_encode(json_decode($response), JSON_PRETTY_PRINT);
?>Step 3: Receive Structured Results
The API returns a JSON object with all SERP elements parsed and categorized:
{
"search_parameters": {
"q": "pizza new york",
"hl": "en",
"gl": "us",
"device": "desktop",
"start": 0,
"google_domain": "google.com"
},
"search_information": {
"page_title": "pizza new york - Google Search",
"query_displayed": "pizza new york",
"total_results": 349000000,
"time_taken_displayed": 0.52,
"organic_results_state": "Results for exact spelling",
"results_for": "",
"country": "",
"city": ""
},
"organic_results": [
{
"position": 1,
"title": "The Best Pizzerias of New York City",
"link": "https://ny.eater.com/maps/best-pizza-new-york-city-nyc-pizzerias",
"displayed_link": "https://ny.eater.com › maps › best-pizza-new-york-city-...",
"snippet": "Where to find the best pizza in New York at the best pizzerias in NYC serving the best pies and slices including L&B Spumoni Gardens, L'Industrie, ...",
"snippet_highlighted_words": ["best"],
"source": "Eater New York",
"sitelinks": {
"expanded": [
{ "title": "L&B Spumoni Gardens", "link": "https://ny.eater.com/maps/best-pizza-new-york-city-nyc-pizzerias#lb-spumoni-gardens" },
{ "title": "L'Industrie Pizzeria", "link": "https://ny.eater.com/maps/best-pizza-new-york-city-nyc-pizzerias#lindustrie" }
]
}
},
{
"position": 2,
"title": "Ny Pizza New York, NY - Last Updated February 2026",
"link": "https://www.yelp.com/search",
"displayed_link": "https://www.yelp.com › Restaurants",
"snippet": "Top 10 Best Ny Pizza in New York, NY - Yelp - NY Pizza Suprema, Juliana's, L'industrie Pizzeria, Joe's Pizza, Angelo's Coal Oven ...",
"snippet_highlighted_words": ["NY Pizza Suprema"],
"source": "yelp.com",
"rich_snippet": {
"bottom": {
"extensions": ["(41,361)"]
}
}
},
{
"position": 3,
"title": "NYC: Best Pizza",
"link": "https://www.the-carboholic.com/nyc-guides/nyc-best-pizza",
"displayed_link": "https://www.the-carboholic.com › nyc-guides › nyc-best-...",
"snippet": "Nov 23, 2025 — The best (and one of the only) I've found is from Emmett's. You can build your own deep dish pie here, choosing from a list of 20+ toppings.",
"snippet_highlighted_words": ["Emmett's"],
"source": "The Carboholic"
}
],
"related_questions": [
{ "question": "What is the most famous pizza in New York?" },
{ "question": "What is the best pizza in NYC?" },
{ "question": "Why is Joe's pizza so famous?" }
],
"related_searches": [
{ "query": "best pizza in new york", "link": "https://www.google.com/search?q=best+pizza+in+new+york" },
{ "query": "pizza delivery new york", "link": "https://www.google.com/search?q=pizza+delivery+new+york" }
],
"pagination": {
"current": 0,
"next": "https://www.google.com/search?q=pizza+new+york&start=10",
"other_pages": {
"2": "...&start=10",
"3": "...&start=20"
}
}
}Pagination
To get additional pages of results, increment the start parameter by 10. Offset can go up to start=100; a higher value is rejected with 400 because no results exist that deep. On the last servable page, pagination.next is omitted.
The start parameter uses zero-based offset: 0 = first page, 10 = second page, 20 = third page, and so on.
Send start from the very first request of a sweep — begin at start=0 and send it on every page. Pages of the same search (identical parameters except start) are then served from one browsing session for a short window (about 2 minutes), so results stay coherent across start=0,10,20,… instead of reshuffling or repeating between pages. There is nothing to enable: sending start is the signal.
/plugin/google/search?token=...&q=pizza+new+york&start=0
/plugin/google/search?token=...&q=pizza+new+york&start=10
/plugin/google/search?token=...&q=pizza+new+york&start=20- Omitting
starton the first request and adding it only from the second page onward breaks that coherence: the first request is treated as a standalone search and can return a different set of results than the rest of the sweep. - Send pages one at a time: request the next page only after the previous one has returned. While a page of the same search is still in progress, another page is rejected with
429and is not charged. Pages from different searches can run in parallel. This limit applies per account, so using another API token from the same account does not bypass it. - Allow at least 60 seconds per request. If your client stops waiting sooner, the request may still be running on our side, and an immediate retry can receive
429until it finishes. - Changing any other parameter that affects results (
q,gl,hl,location,device, …) starts a separate search.cache,include_htmlandresolveGotodo not, so a page sent withcache=falsestill belongs to the same sweep. - A sweep that takes longer than the window simply continues on a new session.
- A single search sent without
startis a one-off and is not tied to any sweep. - Pages after the first (
start > 0) contain web results:organic_results,pagination, and, when Google shows them,top_adsandbottom_adsare filled as usual.related_questions,top_stories,local_results, andlocal_mapare empty on these pages, whilerelated_searchesandvideo_resultscan still appear. The first page (start=0) carries every section. - With
device=mobile, Google does not return numbered pagination links, sopagination.nextis omitted andpagination.other_pagesis empty. Manual pagination still works: sendstart=0,10,20,…yourself rather than followingpagination.next.
Caching
Successful searches are cached for 6 hours. Repeating an identical search during that time returns the cached page, so the results and total_results remain consistent. Cached responses cost the same credits as fresh responses.
A cached page is reused only when every result-affecting parameter matches. include_html and resolveGoto do not affect cache matching. Paginated searches are cached page by page, and pages in the same cached sweep stay consistent with each other. To refresh a complete paginated search, request the first page with start=0&cache=false; later pages then follow that fresh sweep.
Set cache=false to skip the cache and fetch a fresh page. The fresh page replaces the cached version for later requests. The previous no_cache=true parameter remains supported as an alias and has the same effect.
Pages without organic results and pages with a deferred AI Overview are not cached.
Response Structure
Top-Level Fields
| Field | Type | When Empty | Description |
|---|---|---|---|
search_parameters | object | always present | Echo of the request parameters sent by the client |
search_information | object | always present | Search metadata: total results, timing, detected location |
organic_results | array | [] | Standard web search results |
top_ads | array | [] | Sponsored results above organic results |
bottom_ads | array | [] | Sponsored results below organic results |
top_stories | array | [] | News and stories carousel |
related_questions | array | [] | "People Also Ask" expandable questions |
related_searches | array | [] | Related search query suggestions |
ai_overview | object | null | AI-generated overview with text blocks and references. See ai_overview |
navigation | array | [] | Search type tabs (All, Images, News, etc.) |
video_results | array | [] | Video results section |
discussions_and_forums | array | [] | Forum and Reddit-style discussion results |
dmca_messages | object | omitted | Filtered-result notices (removed results, copyright or legal removals). Only present when Google removed results on this page. See dmca_messages |
local_map | object | null | Map widget with link and thumbnail image |
local_results | object | null | Local business listings (map pack) |
knowledge_graph | object | null | Knowledge panel for entities |
pagination | object | null | Pagination links and metadata |
html | string | omitted | Raw Google HTML. Only present when include_html=true |
Array fields always return [] when empty (never null). Object fields return null when absent. The html field is completely omitted unless include_html=true.
search_parameters
Echoes the request parameters. Always-present fields are shown below; optional fields appear only when sent by the client.
{
"q": "pizza new york",
"hl": "en",
"gl": "us",
"device": "desktop",
"start": 0,
"google_domain": "google.com",
"location": "New York,New York,United States",
"uule": "w+CAIQICIfTmV3IFlvcmssTmV3...",
"nfpr": true,
"lr": "lang_en",
"cr": "countryUS",
"safe": "active",
"time_period": "last_week",
"filter": "0"
}search_information
Search metadata. All fields are always present (empty string or zero when no data).
| Field | Type | Description |
|---|---|---|
page_title | string | HTML page title returned by Google |
query_displayed | string | The query as displayed by Google on the results page |
total_results | integer | Approximate total number of results reported by Google |
time_taken_displayed | float | Search time in seconds as displayed by Google |
organic_results_state | string | State of results (e.g., "Results for exact spelling") |
results_for | string | Corrected query if Google auto-corrected the search term |
country | string | Detected country from results context |
city | string | Detected city from results context |
With device=mobile, total_results is 0 because Google does not display a result count on mobile SERPs. Use device=desktop if you need the reported count.
organic_results[]
Standard web search results.
| Field | Type | Description |
|---|---|---|
position | integer | Result position on the page (1-indexed). Standard web results only — ads, local pack and top stories never consume an organic position, so position reflects the true organic rank. Restarts at 1 on each page |
title | string | Page title |
link | string | Absolute URL to the page. See Result links for when a Google redirect URL is returned |
displayed_link | string | URL as shown in the SERP (breadcrumb format) |
snippet | string | Result description/snippet text |
snippet_highlighted_words | string[] | Bold/highlighted words within the snippet |
source | string | Source website name (e.g., "Eater New York", "Reddit · r/FoodNYC") |
date | string | Publication date (e.g., "3 days ago", "Nov 23, 2025") |
sitelinks | object | Sitelinks for the result. inline are the small links shown directly beneath a result; expanded holds the stacked deep links Google groups when a single site dominates the query ("More results from this site"). Shape: { "inline": [{ "title", "link", "description" }], "expanded": [{ "title", "link" }] }. Either array is omitted when empty. |
rich_snippet | object | Rich snippet data: { "bottom": { "extensions": ["(41,361)", ...] } } |
top_ads[] & bottom_ads[]
Sponsored results. top_ads appear above organic results, bottom_ads appear below.
| Field | Type | Description |
|---|---|---|
position | integer | Ad position within its group (1-indexed) |
title | string | Ad headline |
url | string | Ad destination URL |
displayed_url | string | URL shown in the ad |
description | string | Ad description text |
phone | string | Phone number (if displayed in the ad) |
knowledge_graph
Rich knowledge panel. Fields vary by entity type (person, business, place, etc.).
| Field | Type | Description |
|---|---|---|
title | string | Entity name |
type | string | Entity type (e.g., "Restaurant", "Person", "Movie") |
knowledge_graph_type | string | Internal Knowledge Graph type identifier |
kgmid | string | Knowledge Graph Machine ID |
description | string | Entity description text |
source | object | { "name": "Wikipedia", "link": "..." } |
header_images | array | [{ "image": "...", "source": "..." }] |
facts | array | [{ "label": "Origin", "value": "Italy" }] |
rating | float | Rating score |
review_count | integer | Number of reviews |
address | string | Physical address |
phone | string | Phone number |
website | string | Official website URL |
open_hours | array | [{ "name": "Monday", "value": "10AM-11PM" }] |
local_map | object | { "link", "gps_coordinates": { "latitude", "longitude" } } |
social_media | array | [{ "platform": "Twitter", "url": "..." }] |
related_topics | array | [{ "title": "Chicago-style pizza" }] |
user_reviews | array | [{ "review", "rating", "user": { "name", "link" } }] |
Additional entity-dependent fields may include: age, born, born_location, spouse, children, education, tv_shows, price_range, service_options, directions, menu, reservations, popular_times, web_reviews, merchant_description, profiles, images, ludocid.
local_results
Local business results (Google Maps pack). Contains a places array and an optional link to more results.
{
"places": [
{ "position": 1, "title": "Joe's Pizza Broadway", "rating": 4.5, "price": "$10–20" },
{ "position": 2, "title": "NY Pizza Suprema", "rating": 4.6, "price": "$10–20" }
],
"more_locations_link": "https://..."
}| Field | Type | Description |
|---|---|---|
places[].position | integer | Position in the local pack (1-indexed) |
places[].title | string | Business name |
places[].rating | float | Star rating (e.g., 4.5) |
places[].price | string | Price range (e.g., "$10–20", "$$") |
more_locations_link | string | URL to see more local results |
local_map
Map widget associated with local results.
{
"link": "https://...",
"image": "data:image/png;base64,..."
}| Field | Type | Description |
|---|---|---|
link | string | URL to the map or top local result |
image | string | Base64-encoded map thumbnail image |
related_questions[]
"People Also Ask" expandable question boxes.
| Field | Type | Description |
|---|---|---|
question | string | The question text |
video_results[]
| Field | Type | Description |
|---|---|---|
position | integer | Position in the video section |
title | string | Video title |
url | string | Video URL |
source | string | Platform name and channel (e.g., "YouTube · Bon Appétit") |
duration | string | Video duration (e.g., "16:00") |
top_stories[]
News and stories carousel. Contains sections with an array of story items.
[{
"items": [
{ "position": 1, "title": "...", "link": "..." },
{ "position": 2, "title": "...", "link": "..." }
]
}]discussions_and_forums[]
| Field | Type | Description |
|---|---|---|
position | integer | Position in the discussions section |
title | string | Thread title |
link | string | Thread URL |
source | string | Platform name (e.g., "Reddit", "Stack Overflow") |
date | string | Post date |
community | string | Community name (e.g., "r/FoodNYC") |
related_searches[]
| Field | Type | Description |
|---|---|---|
query | string | Related search query text |
link | string | Google search URL for this query |
ai_overview
When Google returns an AI Overview for the query, this field is included in the first page (start=0). Pages after the first return web results without an AI Overview. When no AI Overview is present, the field is null.
AI Overview uses these response states:
| State | Description |
|---|---|
complete | Full AI Overview content returned inline |
deferred | Content not yet available — use the async endpoint with the returned session_key |
unavailable | Returned by the follow-up endpoint when Google did not produce an answer for the session |
null | No AI Overview for this query |
Complete example:
{
"ai_overview": {
"state": "complete",
"text_blocks": [
{
"type": "paragraph",
"snippet": "To print a document, open the file, select File > Print or press Ctrl+P (Windows) / Cmd+P (Mac)...",
"reference_indexes": [0, 1, 2]
},
{
"type": "list",
"list": [
{
"snippet": "Open the File: Open the document you wish to print."
},
{
"snippet": "Access Print Menu: Click \"File\" then select \"Print.\""
},
{
"snippet": "Select Printer & Settings: Choose the correct printer.",
"list": [
{ "snippet": "Printer: Choose the correct printer from the dropdown menu." },
{ "snippet": "Pages: Select \"All Pages\" or enter specific page numbers." }
]
}
],
"reference_indexes": [3, 6]
}
],
"references": [
{
"title": "How to Print Documents from Computer",
"link": "https://www.youtube.com/watch?v=example",
"snippet": "To print from a laptop, ensure both the printer and laptop are connected...",
"source": "YouTube"
}
]
}
}Deferred example:
{
"ai_overview": {
"state": "deferred",
"session_key": "ce5a60050323b4beee15b60e1fcc9018"
}
}AI Overview Schema
AIOverview Object
| Field | Type | Description |
|---|---|---|
state | string | "complete", "deferred", or "unavailable" |
text_blocks | array | Content blocks (only when state=complete) |
references | array | Cited sources (only when state=complete) |
session_key | string | Session key for the follow-up endpoint (only when state=deferred). Any references already available are also carried in the deferred object |
TextBlock Object
| Field | Type | Description |
|---|---|---|
type | string | "paragraph", "heading", "list" or "table" |
snippet | string | Text content (for paragraphs and heading titles) |
snippet_links | array | Inline links within the snippet (optional) |
list | array | List items when type=list (optional) |
table_headers | array | Column headers as strings, when type=table |
table_rows | array | Table rows, each an array of strings, when type=table |
reference_indexes | array of int | Indexes into the references array (optional) |
ListItem Object
| Field | Type | Description |
|---|---|---|
snippet | string | Item text |
snippet_links | array | Inline links (optional) |
list | array | Nested sub-items (optional, recursive) |
Reference Object
| Field | Type | Description |
|---|---|---|
title | string | Page title |
link | string | URL |
snippet | string | Description excerpt |
source | string | Domain or site name |
SnippetLink Object
| Field | Type | Description |
|---|---|---|
text | string | Link text |
link | string | URL |
Fetching a Deferred AI Overview
When you receive state: "deferred", make a follow-up request to fetch the complete AI Overview:
GET https://api.scrape.do/plugin/google/search/ai-overview?token=TOKEN&session_key=SESSION_KEY| Parameter | Required | Description |
|---|---|---|
token | Yes | Your API token |
session_key | Yes | Session key from the deferred response |
include_html | No | When true, HTML is included in the response (see below) |
Credit cost: 5 credits per async AI Overview request.
This endpoint does not accept resolveGoto: a reference link whose destination the page does not carry stays Google's redirect URL.
The response returns the same AI Overview structure with state: "complete" or state: "unavailable":
{
"state": "complete",
"text_blocks": [
{
"type": "paragraph",
"snippet": "mRNA vaccines work by instructing cells to produce a harmless piece of the target virus's spike protein...",
"reference_indexes": [0, 1]
}
],
"references": [
{
"title": "Understanding mRNA COVID-19 Vaccines",
"link": "https://www.cdc.gov/...",
"snippet": "mRNA vaccines teach our cells how to make a protein...",
"source": "CDC"
}
]
}Session keys expire after 90 seconds and are one-time use (deleted after successful fetch). If expired or already used, the endpoint returns 404 with {"error": "session not found"} — repeat the search request for a fresh one.
Included HTML in the follow-up response
By default, html is just the HTML the AI Overview was extracted from. If you also passed include_html=true on the original search request that produced this session_key, html is instead one combined page with the AI Overview content already inserted into the original page — so you don't have to stitch the two responses together yourself. If the original page's HTML isn't available (for example, include_html=true wasn't passed on the original request, or too much time passed between the two requests), html falls back to just the AI Overview fragment.
In both cases html is ready-to-render markup: the AI Overview arrives from Google in an incremental transfer format, and it is assembled into finished HTML for you, with the answer in its normal place on the page. You can save the response to a file and open it in a browser as-is.
Result links
link is an absolute URL you can request directly — no host to prepend, no path to resolve.
- Results pointing at one of Google's own surfaces (Flights, Books, Arts, Finance) keep that host, for example
https://www.google.com/travel/flights/flights-to-tokyo.html. They are ordinary results and count towardposition. - Google serves result links as its own redirect,
https://www.google.com/goto?url=…, in place of the destination URL. When the page carries the destination as well,linkis resolved back to it at no extra cost, so it is the publisher's URL. - For links whose destination the page does not carry — commonly AI Overview references, image-led cards and the complaint links in dmca_messages —
linkis Google's redirect on the domain you searched. It lands on the destination when you follow it, or passresolveGoto=trueto get the destination in the response.
ai_overview.references[].link follows the same rules.
Resolving redirect links (resolveGoto)
With resolveGoto=true, every Google redirect link still in the response is replaced with the destination URL it points to before the response is returned:
/plugin/google/search?token=...&q=what+is+seo&gl=us&hl=en&resolveGoto=true- All redirects in the response are resolved at the same time. A link that cannot be resolved keeps its redirect URL, and the request still succeeds.
- The destination URL replaces the redirect in the same field.
- Only link values change: the response has the same fields, results, order and
positionwith or withoutresolveGoto. - Resolving adds to the response time. Allow several extra seconds on top of the normal search latency.
- Cost: no extra credits — a request costs the same with or without
resolveGoto.
dmca_messages
When Google has removed pages from a result page, it prints a notice under the results saying so. dmca_messages carries that block. The key is absent when the page shows no notice, so its presence is itself the signal that something was removed for this query.
{
"dmca_messages": {
"title": "Notices about Filtered Results",
"messages": [
{
"content": "In response to multiple complaints we received under the US Digital Millennium Copyright Act, we have removed 2 results from this page. If you wish, you may read the DMCA complaints that caused the removals at LumenDatabase.org: Complaint, Complaint.",
"highlighted_words": [
{ "text": "US Digital Millennium Copyright Act", "link": "https://www.google.com/support/answer/1386831" },
{ "text": "Complaint", "link": "https://www.google.com/goto?url=CAESYgHrOzAVyuo5c-Uj…" },
{ "text": "Complaint", "link": "https://www.google.com/goto?url=CAESYgHrOzAVaXZLynLN…" }
]
}
]
}
}| Field | Type | Description |
|---|---|---|
title | string | Heading Google gives the block, in the language of hl. Omitted when the page has none |
messages | array | One entry per notice. A page can carry several |
messages[].content | string | The notice as plain text, with the linked phrases inlined where they appear |
messages[].highlighted_words | array | The linked phrases inside content, in the order they appear. Omitted when the notice has no links |
messages[].highlighted_words[].text | string | The linked phrase exactly as it reads in content |
messages[].highlighted_words[].link | string | Absolute URL the phrase points at |
- Copyright takedowns are not the only thing in this block. Court-ordered and other legal removals are printed the same way and appear as further
messagesentries. Readcontentto tell them apart rather than assuming every entry is a copyright complaint. - Repeated
textvalues are normal and are not duplicates. Two removals render as two links both readingComplaint, each pointing at a different notice. - The complaint links are usually Google redirects. On the page layout Google serves today it publishes no destination for them, so
linkishttps://www.google.com/goto?url=…. PassresolveGoto=trueto get the notice URL in the response instead. Links Google does publish outright, such as its own help page, come back as the destination. - The block is read on every result page, not just the first, so a notice on
start=20is returned like any other.
navigation[]
| Field | Type | Description |
|---|---|---|
title | string | Tab name (e.g., "All", "Images", "News", "Maps") |
url | string | URL for that search type |
pagination
{
"current": 0,
"next": "https://www.google.com/search?q=...&start=10",
"other_pages": {
"2": "...&start=10",
"3": "...&start=20"
}
}Example Requests
Basic search:
/plugin/google/search?token=TOKEN&q=pizza+new+yorkTurkish results from Turkey, mobile device:
/plugin/google/search?token=TOKEN&q=pizza&hl=tr&gl=tr&device=mobile&google_domain=google.com.trLocation-based search (auto UULE encoding):
/plugin/google/search?token=TOKEN&q=restaurants&gl=tr&location=Istanbul,Istanbul,TurkeyAdvanced filtering:
/plugin/google/search?token=TOKEN&q=python+tutorial&lr=lang_en&cr=countryUS&nfpr=truePagination (2nd page):
/plugin/google/search?token=TOKEN&q=pizza+new+york&start=10Resolve Google redirect links:
/plugin/google/search?token=TOKEN&q=what+is+seo&gl=us&hl=en&resolveGoto=trueFetch fresh results:
/plugin/google/search?token=TOKEN&q=test&cache=falseDebug mode (include raw HTML):
/plugin/google/search?token=TOKEN&q=test&include_html=trueSearch queries must be URL-encoded. The response is gzip compressed with a minimum size of 2KB.

