/serpApi endpoint scrapes Google search results and returns normalized JSON. It handles the CAPTCHA gauntlet, ad rendering delays, and proxy rotation that make Google scraping painful.
Minimum request
Response shape
keyword, proxyCountry, totalResults, the count
fields (topAdsCount, bottomAdsCount, organicResultsCount,
resultsCollected), pagesFetched (how many result pages were rendered - this
is how many API requests the call was billed, see pages_to_check),
the three result lists (topAds, bottomAds, organic) and aiOverview. On a country fallback it also carries requestedCountry and
note (see proxy_country below).
aiOverview
Google’s AI-written summary above the results, or null when the search does
not have one. Whether Google generates an overview depends on the query and the
region, so treat this as an extra rather than a guarantee - a commercial query
usually has none, an informational one usually does.
A real example, trimmed, for
keyword=how do solar panels work:
null.
topAds and bottomAds cover the paid units wherever Google puts them,
including the blocks it now renders inside the organic column. Anything paid is
in one of those two lists and never in organic.Useful parameters
pages_to_check
How many SERP pages to scrape, 1 to 10. Defaults to 1.
topAds, bottomAds, and organic with position reflecting cross-page ordering.
Billing: each result page rendered is charged as one API request - a pages_to_check=3 call that returns three pages counts as 3 requests, not 1. The pagesFetched field in the response tells you exactly how many were charged (it can be lower than pages_to_check when Google has fewer pages). /getPageSource and /getImage bill a flat 1 per request; plugins that page through results count pages the same way as /serpApi, and each plugin page says how its calls are counted.
proxy_country
Two-letter ISO code forcing the request through that country’s IP pool. Affects what Google shows you - SERP composition varies by region.
BE, CA, CH, CN, DE, DK, ES, FR,
GB, HK, HR, IE, IT, JP, KR, LT, NL, NO, PL, RO, SE,
SG, TW, US.
Country fallback. If you request a country we do not have an exit for, the
request does not error. The results are served from an available exit, and
the JSON response carries two extra fields so you know it happened:
requestedCountry and note appear only on a country fallback. On a
normal request where your proxy_country is available, neither field is
present and proxyCountry echoes the country you asked for.wait_after_load
Extra seconds to wait before extraction, on top of the wait we already do.
You should not normally need it: extraction waits for the paid units to finish
arriving before reading the page, so ads are not missed because of timing. Reach
for this only when you have a specific reason to let a SERP settle longer - it
is added to your response time as-is.
captcha_pause
Seconds to pause if Google shows a CAPTCHA, giving an interactive user time to solve it. Only useful when running the API in attended mode. Leave at 0 for automated workloads - rely on proxy_country rotation instead.
Errors
A keyword Google genuinely has no results for is not an error: you get
200
with resultsCollected: 0 and an empty organic array. This is normal for narrow
site: queries, and it means you can trust an empty result set instead of retrying it.
Tips
- Phrase keywords like a user would.
keyword=best+pizza+nycperforms differently fromkeyword=best%20pizza%20nyc%202026. Use spaces or+, not commas. - Watch
totalResultsfor sanity. AtotalResultsof 0 with valid organic results means the count couldn’t be extracted - not that there are no results. - Cross-page dedup is your job. If you call with
pages_to_check=10, expect some overlap on borderline results. Dedup onurl.

