Google Search Results (SERP) Scraper By Site & File Type avatar

Google Search Results (SERP) Scraper By Site & File Type

Pricing

from $4.99 / 1,000 results

Go to Apify Store
Google Search Results (SERP) Scraper By Site & File Type

Google Search Results (SERP) Scraper By Site & File Type

Scrape Google SERPs by specific websites, domains, and file types. Extract organic results, titles, URLs, snippets, rankings, and indexed PDFs or documents for SEO research, competitor analysis, site audits, and content discovery.

Pricing

from $4.99 / 1,000 results

Rating

0.0

(0)

Developer

API Empire

API Empire

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

a day ago

Last modified

Share

Google SERP Scraper โ€” Extract Search Results & Documents as JSON

This Actor runs Google searches built from real search operators โ€” site:, filetype:, intitle:, inurl: and exact-phrase โ€” and returns organic results, paid results, People Also Ask items and related searches, plus a documentResults[] audit showing which operators each result actually satisfied, the file-type badge Google printed, and the real cite URL. Every response comes back as typed, normalized JSON โ€” no HTML, no selectors, no parsing. Read on for the exact input fields, every output key the Actor writes, and how to filter for genuine operator matches.


What is Google Search Results (SERP) Scraper By Site & File Type?

It is an Apify Actor that turns a list of keywords into full Google search-results pages, with Google's own site:, filetype:, intitle:, inurl: and exact-phrase operators exposed as first-class input fields instead of raw query syntax. No Google account or login is required โ€” only an Apify account to run the Actor. It is reachable as a plain HTTP endpoint or through Apify's MCP server, so an agent can call it directly.

  • ๐Ÿ”Ž Scrape organic search results โ€” title, URL, snippet, position, sitelinks
  • ๐Ÿ—‚๏ธ Scrape audited documents โ€” one record per result with the file-type badge, the "N pages" line when Google prints it, and matchedOperators[] showing which operator that result really satisfied
  • ๐Ÿ’ฌ Scrape paid results, shopping products, People Also Ask and related searches from the same run
  • ๐Ÿ“ค Export as JSON, CSV or Excel from the Apify dataset
  • ๐Ÿšซ No proxy management โ€” the Actor escalates from Apify's Google-SERP proxy to residential proxies on its own when a request is challenged

What data does Google Search Results (SERP) Scraper By Site & File Type collect?

Every keyword returns organic results plus an audited document table, and โ€” when present on the page โ€” paid results, shopping products, People Also Ask and related searches.

Data TypeKey FieldsJSON Field Names
Organic search resultstitle, URL, displayed URL, snippet, positionorganicResults[] (title, url, displayedUrl, description, position)
Audited documentsfile-type badge, page count, matched operatorsdocumentResults[] (fileType, fileTypeSource, pageCountText, matchedOperators)
Paid results & shopping productsad title/URL, product title, pricespaidResults[], paidProducts[] (title, url, prices)
People Also Askrelated question, linkpeopleAlsoAsk[] (question, url)
Related searchessuggested query, linkrelatedQueries[] (title, url)

Need more Google data?

If the page you need isn't a search-results page, the same account publishes Google Search Results Scraper (Knowledge Panel & Answer Box) for answer-box and knowledge-panel data, Google Scholar Scraper for academic search, and Google Maps Reviews Scraper for location data โ€” each scoped to its own part of Google rather than bolted onto this one.


How does Google Search Results (SERP) Scraper By Site & File Type differ from the official Google API?

Google's own programmatic route to search results, the Custom Search JSON API, is closed to new customers โ€” existing integrations must migrate off it by January 1, 2027. This Actor runs today, on any Apify account, with no Google Cloud project or search-engine registration.

FeatureGoogle Custom Search JSON APIGoogle Search Results (SERP) Scraper By Site & File Type
New sign-upsClosed to new customers; must migrate by Jan 1, 2027Open โ€” run it from any Apify account today
Free quota100 queries/day free, then $5 per 1,000 up to 10,000/dayPay per result row returned (Apify pay-per-event); no daily query cap in the Actor
Results per queryMax 10 per request (num 1โ€“10), capped at 100 results totalUp to 10 results per page, walks up to 100 pages per keyword
SetupGoogle Cloud project, API key, and a Programmable Search Engine ID (cx)Apify account and a keyword โ€” no Google credential of any kind
site: / filetype: / intitle: / inurl: operatorsAccepted as raw query syntax, unauditedExposed as dedicated input fields, and every result is checked against them (matchedOperators[])
Result freshnessLiveLive, per run

The official API is the right call if you already have an active Custom Search Engine project and stay under its query caps. This Actor is the fit once you need site:/filetype: audited at the result level, more than 100 results per query, or you can't get a new Custom Search project approved at all.


Why do developers and teams scrape Google search results?

For AI engineers and agent builders

organicResults[] and documentResults[] drop straight into a RAG index or an agent tool without an HTML-parsing step. An agent researching a regulatory topic can run site:europa.eu filetype:pdf, then filter documentResults[] on matchedOperators and fileType: "PDF" to keep only genuine PDF hits before it ever reaches an LLM context window โ€” Google routinely loosens filetype: and returns HTML pages anyway, so that filter step matters.

For marketers and SEO teams

site:yourdomain.com walked page by page shows what Google has actually indexed and how each URL is displayed, using the same displayedUrl and snippet Google shows a searcher. site:competitor.com intitle:"pricing" returns only a competitor's pricing pages, with matchedOperators showing which ones Google actually honoured rather than loosely matched.

For researchers and analysts

site:*.gov or site:europa.eu combined with fileTypes: ["pdf"] surfaces published reports and filings that a normal keyword search buries under news and blog content, and documentsFound / fileTypeBreakdown tell you at a glance how many of a search's results are real documents versus HTML pages โ€” public search-results scope only, nothing behind a login.

For developers building data products

resolvedQuery and operatorMatchSummary give a stable, auditable record of exactly what was searched and how well it was honoured, which is a workable base for a scheduled index-monitoring tool or a document-discovery pipeline โ€” run it on an Apify schedule and pipe the dataset into your own store.


How to scrape Google search results (step by step)

  1. Open Google Search Results (SERP) Scraper By Site & File Type on its Apify Store listing.
  2. Provide keyword โ€” one search per line. It's the only required field.
  3. Set the operator fields that apply โ€” site, fileTypes, wordsInTitle, wordsInUrl, forceExactMatch โ€” and pick a limit for how many pages to walk.
  4. Click Start and watch rows stream into the dataset per page, per keyword.
  5. Open the Output tab and export the run as JSON, CSV or Excel.

What to do when Google changes its structure

The Actor is maintained, and the output schema stays the same on your end regardless of what changes in Google's markup. Every selector that reads the page โ€” the result container, the file-type badge, the "N pages" line โ€” is checked against a ladder of fallback selectors rather than one hard-coded class, so a single class-name change doesn't silently zero out a field.


What changed in Google search scraping recently?

Google has made two dated, verifiable changes that affect anyone pulling search results at scale. On September 11, 2025, Google deprecated the &num=100 parameter that let a single request return 100 results โ€” confirmed by Google's own Danny Sullivan as an intentional move, not a bug โ€” so any bulk pull now has to walk pages 10 results at a time instead of one large request (Search Engine Land). Separately, Google's own Custom Search JSON API โ€” the official programmatic route โ€” has been closed to new customers, with existing integrations required to migrate off it by January 1, 2027.

For a DIY scraper, both changes mean more requests per crawl and one less officially sanctioned path to get there. This Actor already paginates in 10-result pages by design and doesn't depend on the official API, so neither change requires any action from you โ€” the Actor is maintained on an ongoing basis as Google's markup continues to shift.


โฌ‡๏ธ Input

Only keyword is required โ€” every operator and localization field below is optional.

ParameterRequiredTypeDescriptionExample Value
keywordYesstringOne search per line. Operators typed here win over the fields below โ€” a line reading site:europa.eu budget keeps its own site: and the site field is skipped for it. Default "nike"."annual report"
siteNostringAdds site: to the query, e.g. europa.eu. Subdomains are included. Default ""."europa.eu"
fileTypesNoarray (select, enum: pdf, doc, docx, xls, xlsx, ppt, pptx, txt, rtf, epub, ps, xml, csv, odt, ods, odp)Adds filetype:. Several values become filetype:pdf OR filetype:docx. Default [].["pdf"]
wordsInTitleNoarrayEach entry becomes an intitle:"โ€ฆ" operator. Default [].["annual"]
wordsInUrlNoarrayEach entry becomes an inurl:"โ€ฆ" operator. Default [].["reports"]
forceExactMatchNobooleanWraps the keyword line in quotes for a verbatim phrase match. Default false.false
limitNostring (select, enum: 10, 20, 30, 40, 50, 100, all)How many result pages to walk. Each page returns up to 10 results and is a charged row. Default "all"."20"
include_mergedNobooleanAdds one extra row per keyword merging every page (searchQuery.page = "all"). Default true.true
startNointegerGoogle start offset for the first result.10
pageNointegerFetch only this page number (1 = first page).1
countryNostring (select, ISO 3166-1 alpha-2, 249 entries)Main country selector โ€” sets Google gl and the default proxy region."US"
glNostring (select, same 249-entry ISO 3166-1 alpha-2 list)Override the results country (Google gl)."GB"
hlNostring (select, 44 language codes)Interface language (Google hl). The "N pages" line is only parsed in English."en"
lrNostring (select, lang_xx codes matching the hl list)Restrict results by content language (Google lr)."lang_en"
crNostring (select, countryXX codes matching the country list)Restrict results by country of origin (Google cr)."countryUS"
tbsNostringRaw Google time filter, e.g. qdr:y (past year)."qdr:y"
proxy_locationNostring (select, enum: us, ca)Forces the proxy exit region independently of the Google country."us"
includeDocumentResultsNobooleanReturns documentResults[] โ€” the audit table. While on, Google's Web-only tab is not requested, because that tab drops the related-searches block this Actor reads. Default true.true
onlyResultsMatchingAllOperatorsNobooleanDrops audited records that don't match every requested operator. Applies to documentResults[] only; organicResults[] is untouched. Default false.false
onlyQueriesWithDocumentsNobooleanDrops a keyword whose SERP carried no file-type badge on any result. The dropped keyword still gets an uncharged accounting row. Default false.false
enrichOrganicResultsNobooleanReplaces organicResults[].displayedUrl/.description/.emphasizedKeywords with the real printed values. Default false.false
forceWebOnlyTabNobooleanSends udm=14 on every request, reproducing the base Actor's request shape byte-for-byte. Default false.false
dedupeResultsAcrossPagesNobooleanEach row carries only its own page's new results instead of repeating pages 1..n. Default false.false

Example JSON input

{
"keyword": "annual report",
"site": "europa.eu",
"fileTypes": ["pdf"],
"wordsInTitle": ["annual"],
"wordsInUrl": [],
"forceExactMatch": false,
"limit": "20",
"include_merged": true,
"country": "US",
"gl": "US",
"hl": "en",
"tbs": "qdr:y",
"proxy_location": "us",
"includeDocumentResults": true,
"onlyResultsMatchingAllOperators": false,
"onlyQueriesWithDocuments": false,
"enrichOrganicResults": false,
"forceWebOnlyTab": false,
"dedupeResultsAcrossPages": false
}

Common pitfall: leaving limit at its default "all" walks up to 100 pages per keyword, and โš ๏ธ every page is a separate charged row โ€” for a narrow operator search that only has one page of real hits, set limit to a small value like "10" or "20" instead of paying for 99 empty pages.


โฌ†๏ธ Output

Results are pushed to the Apify dataset as typed, normalized JSON โ€” one row per page, per keyword, plus an optional merged row per keyword. Export as JSON, CSV or Excel from the Output tab, or pull rows with the API/SDK.

Search result row

{
"searchQuery": {
"term": "annual report site:europa.eu filetype:pdf",
"url": "https://www.google.com/search?q=annual+report+site%3Aeuropa.eu+filetype%3Apdf&gl=us&hl=en",
"device": "DESKTOP",
"page": 1,
"type": "SEARCH",
"domain": "google.com",
"countryCode": "US",
"languageCode": "en",
"locationUule": null,
"resultsPerPage": 10
},
"searchQueryTerm": "annual report site:europa.eu filetype:pdf",
"url": "https://www.google.com/search?q=annual+report+site%3Aeuropa.eu+filetype%3Apdf&gl=us&hl=en",
"hasNextPage": true,
"serpProviderCode": "O",
"resultsTotal": 1240,
"relatedQueries": [
{ "title": "europa.eu annual report 2024 pdf", "url": "https://www.google.com/search?q=europa.eu+annual+report+2024+pdf" }
],
"paidResults": [],
"paidProducts": [],
"aiOverview": null,
"organicResults": [
{
"title": "Annual Report 2024 - European Union",
"url": "https://www.srb.europa.eu/system/files/media/document/annual_report_2024.pdf",
"displayedUrl": "www.srb.europa.eu",
"description": "annual report site:europa.eu filetype:pdf",
"emphasizedKeywords": ["annual report site:europa.eu filetype:pdf"],
"siteLinks": [],
"productInfo": {},
"type": "organic",
"position": 1
}
],
"suggestedResults": [
{ "title": "europa.eu annual report 2024 pdf", "url": "https://www.google.com/search?q=europa.eu+annual+report+2024+pdf", "type": "organic", "position": 1 }
],
"peopleAlsoAsk": [
{ "answer": null, "question": "How many pages is the EU annual report?", "title": "How many pages is the EU annual report?", "url": "https://www.google.com/search?q=how+many+pages+eu+annual+report", "date": null }
],
"customData": {
"perplexitySearch": { "enablePerplexity": false, "searchRecency": null, "returnImages": false, "returnRelatedQuestions": false },
"chatGptSearch": { "enableChatGpt": false },
"maximumLeadsEnrichmentRecords": 0,
"leadsEnrichmentDepartments": []
},
"htmlSnapshotUrl": null,
"resolvedQuery": "annual report site:europa.eu filetype:pdf",
"operatorsRequested": ["site:europa.eu", "filetype:pdf"],
"documentResultsCount": 10,
"documentsFound": 8,
"fileTypeBreakdown": { "PDF": 8 },
"operatorMatchSummary": { "site:europa.eu": 10, "filetype:pdf": 8 },
"documentResults": [ { "...": "see the audited document record below" } ]
}

Audited document record (documentResults[] item)

{
"position": 1,
"title": "Annual Report 2024 - European Union",
"url": "https://www.srb.europa.eu/system/files/media/document/annual_report_2024.pdf",
"displayedUrl": "www.srb.europa.eu โ€บ media โ€บ document",
"siteDomain": "www.srb.europa.eu",
"description": "The Single Resolution Board's 2024 annual report summarises resolution planning and crisis-management activity across the Banking Union.",
"emphasizedKeywords": ["annual", "report"],
"fileType": "PDF",
"fileTypeSource": "badge",
"pageCountText": "79 pages",
"isDocument": true,
"matchedOperators": ["site:europa.eu", "filetype:pdf"]
}

fileTypeSource is "badge" when Google printed the file-type badge itself, and "url-extension" when no badge was on the page and the value was derived from the URL instead โ€” the two are never mixed together silently. pageCountText is Google's own "N pages" line; Google only prints it on some results, so it is null on the rest and is never estimated.

All other row-level fields

FieldTypeNotes
hasNextPagebooleanWhether a further page exists for this keyword
serpProviderCodestringConstant "O" (organic)
resultsTotalintegerGoogle's own "About N results" count, parsed from the page; 0 when not printed
paidResults[]arraySame shape as organicResults[] items, with type: "paid"
paidProducts[]arraytitle, displayedUrl, prices[] from shopping-style cards
peopleAlsoAsk[]arrayquestion, title, url; answer and date are always null โ€” the Actor does not expand PAA answers
relatedQueries[] / suggestedResults[]array or nullGoogle's real "Related searches" block; null when the page carries none, never fabricated. suggestedResults[] is the same data re-shaped with type/position
aiOverviewobject or nullOnly populated when an internal AI-overview lookup is configured โ€” a path this Actor's public input schema does not expose, so on a normal Console run this is always null
customDataobjectperplexitySearch, chatGptSearch, maximumLeadsEnrichmentRecords, leadsEnrichmentDepartments โ€” legacy fields carried from the base Actor's output contract; they stay at their false/0/[] defaults on a normal Console run
htmlSnapshotUrl / htmlstring or nullOnly present when the run was configured to persist the raw SERP HTML; absent otherwise
errorReason / errorMessage / chargedstring / string / falsePresent only on the uncharged accounting rows described below โ€” never on a normal result row

โš ๏ธ aiOverview, and every field inside customData, depend on internal configuration keys (aiMode, serpApiKey, perplexitySearch, chatGptSearch, lead-enrichment settings) that are not part of this Actor's public input schema โ€” on a normal Console or API run through the documented keyword/site/fileTypes fields above, expect aiOverview: null and customData at its listed defaults every time.

An uncharged accounting row is written instead of a result row when a keyword is blocked after the full proxy retry ladder, or dropped by onlyQueriesWithDocuments. It carries errorReason (e.g. JS_CHALLENGE_BOOTSTRAP, CHALLENGE_PAGE, SORRY_REDIRECT, FILTERED_NO_DOCUMENT_RESULTS), errorMessage, charged: false, plus searchQueryTerm, resolvedQuery, operatorsRequested and searchQuery โ€” but none of organicResults, paidResults, peopleAlsoAsk or the other result-only fields. To keep only billed rows in an export, filter out any item where errorReason is present (equivalently, charged !== false).


How does Google Search Results (SERP) Scraper By Site & File Type compare to other Google scrapers?

FeatureThis ActorGeneric Google SERP scraper
Operator inputsite:, filetype:, intitle:, inurl:, exact-phrase as dedicated fieldsUsually only a free-text keyword field; operators have to be typed by hand into the query
Operator verificationEvery result carries matchedOperators[], checked against its own URL/title/badgeNot verified โ€” a loosened operator match looks identical to a real one
File-type auditfileType, fileTypeSource, pageCountText per resultNot exposed
Related searchesParses Google's real "Related searches" block; null when absentOften fabricated from the first organic titles when the real block isn't parsed
Output formatTyped, normalized JSON, stable field namesVaries โ€” often raw HTML or inconsistent field names

If you're building an AI agent or RAG pipeline, the output-format row is the decision-maker โ€” parsing HTML inside an agent loop is a reliability failure mode, not a feature.


How many results can you scrape with Google Search Results (SERP) Scraper By Site & File Type?

Each page returns up to 10 organic results. limit controls how many pages are walked per keyword: numeric values (10โ€“100) map to ceil(n / 10) pages, and "all" (the default) walks up to 100 pages โ€” up to 1,000 results per keyword. start/page can target a specific offset or single page instead of walking from the top. There is no fixed cap on the number of keyword lines in one run. Every page fetched is a separate charged row, so a broad limit: "all" run on a keyword with few real matches produces mostly empty pages at cost โ€” narrow the operator fields or lower limit for a tighter search.


Integrate Google Search Results (SERP) Scraper By Site & File Type and automate your workflow

Google Search Results (SERP) Scraper By Site & File Type works with any language or tool that can send an HTTP request.

REST API integration

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("<YOUR_USERNAME>/google-search-results-serp-scraper-by-site-and-file-type").call(
run_input={
"keyword": "annual report",
"site": "europa.eu",
"fileTypes": ["pdf"],
"limit": "20",
"includeDocumentResults": True,
}
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["resolvedQuery"], item.get("documentsFound"))

Works in Python, Node.js, Go, Ruby, cURL.

MCP for AI agents

This Actor is reachable through Apify's MCP server. Register it directly with https://mcp.apify.com?tools=<YOUR_USERNAME>/google-search-results-serp-scraper-by-site-and-file-type, or run it locally via npx @apify/actors-mcp-server --tools <YOUR_USERNAME>/google-search-results-serp-scraper-by-site-and-file-type. Compatible with Claude, Cursor, VS Code and any other MCP client.

Automation platforms (n8n, Make, LangChain)

In n8n, the official @apify/n8n-nodes-apify package's "Run Actor" and "Get Dataset Items" nodes point at this Actor's ID and pass the same input fields shown above. In Make, the official Apify app's "Run an Actor" module does the same, either synchronously or via a "Watch Actor Runs" trigger. For LangChain, ApifyWrapper.call_actor() from langchain_apify runs the Actor and hands its dataset rows straight to a document loader for RAG indexing.


Yes โ€” scraping publicly available search-results pages is generally lawful, and this Actor only returns what Google already shows any visitor: organic listings, paid results, People Also Ask and related searches. This is business and catalogue-style data (page titles, URLs, snippets, file-type badges), not personal profile data, so Google's Terms of Service and database-rights considerations apply rather than GDPR/CCPA. A search result can incidentally surface a person's name inside a page title or snippet, so consult legal counsel before storing or reusing bulk personal data extracted this way for commercial purposes.


Frequently asked questions

Does this Actor work without a Google account?

Yes. No Google account, cookie or login is required โ€” the Actor fetches Google's public search-results pages through Apify's own proxy. Only an Apify account is needed to run it.

How often is the scraped data updated?

Every run fetches live from Google โ€” there is no caching layer. Each keyword is searched fresh for that run.

What happens when a keyword is blocked or returns nothing?

The Actor retries through Apify's Google-SERP proxy, then escalates to residential proxies (three retries at each tier). If every attempt fails, it writes an uncharged accounting row with an errorReason (JS_CHALLENGE_BOOTSTRAP, CHALLENGE_PAGE, SORRY_REDIRECT, FETCH_FAILED, or similar) instead of a fabricated result row โ€” nothing is billed for a keyword that produced no usable page.

Can I scrape gated or paywalled content this way?

No. The Actor returns only what's on Google's own public search-results page โ€” titles, snippets and links. It never fetches or bypasses the linked pages themselves, so content behind a login or paywall on the destination site is not retrieved.

How is this Actor billed?

Pay-per-event: one row_result charge per result row actually pushed โ€” a per-page row and, if include_merged is on, one merged row per keyword. Blocked or filtered-out keywords produce an uncharged accounting row instead, identifiable by charged: false. Exact pricing is set on the Actor's Console listing.

Does this Actor work for AI agent workflows and LLM pipelines?

Yes. It's callable as a plain HTTP endpoint by any agent framework, or through Apify's MCP server (https://mcp.apify.com) as a registered tool. Every response is typed JSON โ€” no parsing step before passing it to an LLM.

How does this Actor handle Google's anti-bot system?

Requests start on Apify's Google-SERP proxy and escalate to a sticky residential proxy tier on any block, retrying up to three times per tier. Block detection checks for a real result container first โ€” a page that has one is never flagged as blocked โ€” before falling back to challenge-page and JS-bootstrap markers, so a genuine large results page is never mistaken for a block.

How does this Actor compare to other Google SERP scrapers?

Unlike a scraper that only exposes a free-text keyword field, this one turns site:, filetype:, intitle: and inurl: into dedicated inputs and audits every result against them with matchedOperators[], since Google itself doesn't always honour an operator strictly.

Does this Actor return data in a format LLMs can use directly?

Yes. Typed, normalized JSON with stable field names on every row. Pass it directly into an LLM context window, index it into a vector store, or route it through an agent tool call.

Can I use this Actor without managing proxies?

Yes. The Google-SERP-to-residential escalation is handled automatically inside the Actor โ€” no separate proxy configuration is required to run it.

What happens when Google changes its structure or blocks the scraper?

The Actor is maintained and the output schema stays stable โ€” field names and types don't change on your end. Every page-reading selector is checked against a ladder of fallback selectors rather than one fixed class, so a single markup change doesn't silently zero out a field.


Your feedback

Found a bug, or missing a field you need from a Google search result? We want to know. Use the Issues tab on this Actor's Apify Store page โ€” active maintenance is how the schema stays stable for you.