logo

SERP API

View as Markdown

Search 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/search

Request Parameters

Required

ParameterTypeDescription
tokenstringYour Scrape.do API authentication token
qstringSearch query. URL-encode spaces and special characters. pizza+new+york or pizza%20new%20york

General

ParameterTypeDefaultDescription
devicestringdesktopDevice type for SERP layout. Accepted values: desktop, mobile
startinteger0Result 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
cachebooleantrueReuses a successful identical search for up to 6 hours. Set false to fetch a fresh page from Google instead. See Caching
include_htmlbooleanfalseWhen 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
resolveGotobooleanfalseWhen 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

ParameterTypeDefaultDescription
hlstringenHost 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 →
glstringusGeo 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_domainstringgoogle.comGoogle 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 →
locationstring-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
uulestring-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

ParameterTypeDefaultDescription
lrstring-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 →
crstring-Country Restrict. Filters results to pages originating from a specific country only. Supports 240+ countries. Format: countryXX. Examples: countryTR (Turkey), countryUS, countryDE. Full list →
safestring-SafeSearch. Send active to filter adult content from results
nfprbooleanfalseWhen true, disables Google's automatic spelling correction. The "Did you mean..." suggestion will not alter results
filterstring-Controls "Similar Results" and "Omitted Results" filters. Send 0 to disable and show all results. Default (not sent or 1) = filters active
time_periodstring-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 start on 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 429 and 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 429 until it finishes.
  • Changing any other parameter that affects results (q, gl, hl, location, device, …) starts a separate search. cache, include_html and resolveGoto do not, so a page sent with cache=false still belongs to the same sweep.
  • A sweep that takes longer than the window simply continues on a new session.
  • A single search sent without start is 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_ads and bottom_ads are filled as usual. related_questions, top_stories, local_results, and local_map are empty on these pages, while related_searches and video_results can still appear. The first page (start=0) carries every section.
  • With device=mobile, Google does not return numbered pagination links, so pagination.next is omitted and pagination.other_pages is empty. Manual pagination still works: send start=0,10,20,… yourself rather than following pagination.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

FieldTypeWhen EmptyDescription
search_parametersobjectalways presentEcho of the request parameters sent by the client
search_informationobjectalways presentSearch metadata: total results, timing, detected location
organic_resultsarray[]Standard web search results
top_adsarray[]Sponsored results above organic results
bottom_adsarray[]Sponsored results below organic results
top_storiesarray[]News and stories carousel
related_questionsarray[]"People Also Ask" expandable questions
related_searchesarray[]Related search query suggestions
ai_overviewobjectnullAI-generated overview with text blocks and references. See ai_overview
navigationarray[]Search type tabs (All, Images, News, etc.)
video_resultsarray[]Video results section
discussions_and_forumsarray[]Forum and Reddit-style discussion results
dmca_messagesobjectomittedFiltered-result notices (removed results, copyright or legal removals). Only present when Google removed results on this page. See dmca_messages
local_mapobjectnullMap widget with link and thumbnail image
local_resultsobjectnullLocal business listings (map pack)
knowledge_graphobjectnullKnowledge panel for entities
paginationobjectnullPagination links and metadata
htmlstringomittedRaw 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).

FieldTypeDescription
page_titlestringHTML page title returned by Google
query_displayedstringThe query as displayed by Google on the results page
total_resultsintegerApproximate total number of results reported by Google
time_taken_displayedfloatSearch time in seconds as displayed by Google
organic_results_statestringState of results (e.g., "Results for exact spelling")
results_forstringCorrected query if Google auto-corrected the search term
countrystringDetected country from results context
citystringDetected 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.

FieldTypeDescription
positionintegerResult 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
titlestringPage title
linkstringAbsolute URL to the page. See Result links for when a Google redirect URL is returned
displayed_linkstringURL as shown in the SERP (breadcrumb format)
snippetstringResult description/snippet text
snippet_highlighted_wordsstring[]Bold/highlighted words within the snippet
sourcestringSource website name (e.g., "Eater New York", "Reddit · r/FoodNYC")
datestringPublication date (e.g., "3 days ago", "Nov 23, 2025")
sitelinksobjectSitelinks 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_snippetobjectRich snippet data: { "bottom": { "extensions": ["(41,361)", ...] } }

top_ads[] & bottom_ads[]

Sponsored results. top_ads appear above organic results, bottom_ads appear below.

FieldTypeDescription
positionintegerAd position within its group (1-indexed)
titlestringAd headline
urlstringAd destination URL
displayed_urlstringURL shown in the ad
descriptionstringAd description text
phonestringPhone number (if displayed in the ad)

knowledge_graph

Rich knowledge panel. Fields vary by entity type (person, business, place, etc.).

FieldTypeDescription
titlestringEntity name
typestringEntity type (e.g., "Restaurant", "Person", "Movie")
knowledge_graph_typestringInternal Knowledge Graph type identifier
kgmidstringKnowledge Graph Machine ID
descriptionstringEntity description text
sourceobject{ "name": "Wikipedia", "link": "..." }
header_imagesarray[{ "image": "...", "source": "..." }]
factsarray[{ "label": "Origin", "value": "Italy" }]
ratingfloatRating score
review_countintegerNumber of reviews
addressstringPhysical address
phonestringPhone number
websitestringOfficial website URL
open_hoursarray[{ "name": "Monday", "value": "10AM-11PM" }]
local_mapobject{ "link", "gps_coordinates": { "latitude", "longitude" } }
social_mediaarray[{ "platform": "Twitter", "url": "..." }]
related_topicsarray[{ "title": "Chicago-style pizza" }]
user_reviewsarray[{ "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://..."
}
FieldTypeDescription
places[].positionintegerPosition in the local pack (1-indexed)
places[].titlestringBusiness name
places[].ratingfloatStar rating (e.g., 4.5)
places[].pricestringPrice range (e.g., "$10–20", "$$")
more_locations_linkstringURL to see more local results

local_map

Map widget associated with local results.

{
  "link": "https://...",
  "image": "data:image/png;base64,..."
}
FieldTypeDescription
linkstringURL to the map or top local result
imagestringBase64-encoded map thumbnail image

"People Also Ask" expandable question boxes.

FieldTypeDescription
questionstringThe question text

video_results[]

FieldTypeDescription
positionintegerPosition in the video section
titlestringVideo title
urlstringVideo URL
sourcestringPlatform name and channel (e.g., "YouTube · Bon Appétit")
durationstringVideo 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[]

FieldTypeDescription
positionintegerPosition in the discussions section
titlestringThread title
linkstringThread URL
sourcestringPlatform name (e.g., "Reddit", "Stack Overflow")
datestringPost date
communitystringCommunity name (e.g., "r/FoodNYC")

FieldTypeDescription
querystringRelated search query text
linkstringGoogle 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:

StateDescription
completeFull AI Overview content returned inline
deferredContent not yet available — use the async endpoint with the returned session_key
unavailableReturned by the follow-up endpoint when Google did not produce an answer for the session
nullNo 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

FieldTypeDescription
statestring"complete", "deferred", or "unavailable"
text_blocksarrayContent blocks (only when state=complete)
referencesarrayCited sources (only when state=complete)
session_keystringSession key for the follow-up endpoint (only when state=deferred). Any references already available are also carried in the deferred object

TextBlock Object

FieldTypeDescription
typestring"paragraph", "heading", "list" or "table"
snippetstringText content (for paragraphs and heading titles)
snippet_linksarrayInline links within the snippet (optional)
listarrayList items when type=list (optional)
table_headersarrayColumn headers as strings, when type=table
table_rowsarrayTable rows, each an array of strings, when type=table
reference_indexesarray of intIndexes into the references array (optional)

ListItem Object

FieldTypeDescription
snippetstringItem text
snippet_linksarrayInline links (optional)
listarrayNested sub-items (optional, recursive)

Reference Object

FieldTypeDescription
titlestringPage title
linkstringURL
snippetstringDescription excerpt
sourcestringDomain or site name

SnippetLink Object

FieldTypeDescription
textstringLink text
linkstringURL

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
ParameterRequiredDescription
tokenYesYour API token
session_keyYesSession key from the deferred response
include_htmlNoWhen 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.


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 toward position.
  • 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, link is 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 — link is Google's redirect on the domain you searched. It lands on the destination when you follow it, or pass resolveGoto=true to get the destination in the response.

ai_overview.references[].link follows the same rules.

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 position with or without resolveGoto.
  • 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…" }
        ]
      }
    ]
  }
}
FieldTypeDescription
titlestringHeading Google gives the block, in the language of hl. Omitted when the page has none
messagesarrayOne entry per notice. A page can carry several
messages[].contentstringThe notice as plain text, with the linked phrases inlined where they appear
messages[].highlighted_wordsarrayThe linked phrases inside content, in the order they appear. Omitted when the notice has no links
messages[].highlighted_words[].textstringThe linked phrase exactly as it reads in content
messages[].highlighted_words[].linkstringAbsolute 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 messages entries. Read content to tell them apart rather than assuming every entry is a copyright complaint.
  • Repeated text values are normal and are not duplicates. Two removals render as two links both reading Complaint, 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 link is https://www.google.com/goto?url=…. Pass resolveGoto=true to 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=20 is returned like any other.

FieldTypeDescription
titlestringTab name (e.g., "All", "Images", "News", "Maps")
urlstringURL 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+york

Turkish results from Turkey, mobile device:

/plugin/google/search?token=TOKEN&q=pizza&hl=tr&gl=tr&device=mobile&google_domain=google.com.tr

Location-based search (auto UULE encoding):

/plugin/google/search?token=TOKEN&q=restaurants&gl=tr&location=Istanbul,Istanbul,Turkey

Advanced filtering:

/plugin/google/search?token=TOKEN&q=python+tutorial&lr=lang_en&cr=countryUS&nfpr=true

Pagination (2nd page):

/plugin/google/search?token=TOKEN&q=pizza+new+york&start=10

Resolve Google redirect links:

/plugin/google/search?token=TOKEN&q=what+is+seo&gl=us&hl=en&resolveGoto=true

Fetch fresh results:

/plugin/google/search?token=TOKEN&q=test&cache=false

Debug mode (include raw HTML):

/plugin/google/search?token=TOKEN&q=test&include_html=true

Search queries must be URL-encoded. The response is gzip compressed with a minimum size of 2KB.

On this page