Google Search scraper artworkOrganic search results

Google Search Scraper.

Run the same searches regularly and see which sites appear. Compare rankings, titles and snippets to find gaps in your content and competitors you hadn't considered.

Run on Apify

Ways to use this scraper.

Try these examples for marketing, growth and competitor research.

01

Track searches that matter to your buyers

For a B2B team, follow ten searches such as best CRM for small teams each week in the same market. Note new competitors, ranking changes and the promises in result titles.

Keep a weekly SERP review with your site's observed rank and competitor movements.

02

Choose what content to research next

Group recurring titles by intent, such as comparisons, pricing, tutorials or alternatives. Check where your content is missing or answers a different question.

Prioritize content ideas and link each one to the results that prompted it.

03

Find companies in a specific niche

Search a narrow category and review the domains in the results. Check the websites, then send the shortlist to Website Contacts for further account research.

Keep a company list with search sources and manually checked websites.

FROM FIRST RUN TO REPEATABLE WORKFLOW

Your first run, step by step.

  1. Open the Actor on Apify, select its Input tab and switch to the JSON editor if you want to paste a configuration.
  2. Replace the sample query with your own. Put each search in queries as a separate string. Quotes, site: and minus terms work as they do in Google; avoid turning a whole watchlist into one long query.
  3. Set country and language explicitly, then start with maxItems 20 per query. The Actor uses Apify’s GOOGLE_SERP proxy group; make sure your account has access to it before running.
  4. Start the run and watch the log. Check which targets and filters were actually read before assuming an empty result means nothing exists.
  5. Open Storage → Dataset, inspect several records and export JSON for nested data or CSV for a first spreadsheet review.
First-run configuration
{
  "queries": ["web scraping tools"],
  "country": "US",
  "language": "en",
  "maxItems": 20
}

Paste this into the Actor’s JSON input editor. Replace the example targets with yours before running.

Check the current input form on Apify

Choose how to search.

This scraper returns organic Google results. Use normal queries or search operators, and keep the country, language and result limit consistent when comparing runs. It does not collect paid ads or AI Overviews.

Search methodWhen to use itWhat changes
Keyword discoveryWhen to use itYou want to find relevant results before choosing specific targets.What changesEnter search queries as separate list items. Quoted phrases, site: filters and minus terms work. A pasted Google search URL contributes its query text, not every URL filter.
Search page URLsWhen to use itYou already configured the search on the source website.What changesOne search query alongside or instead of queries. For several topics, use queries so each search keeps its own ranking context.
Market selectionWhen to use itYou need a view of a particular country or market.What changesSearch market as a two-letter code or country name. This changes geographic ranking; it is separate from language and from the proxy’s exit country.

The difference that matters

Maximum organic results per query, not across the entire run. Two queries with maxItems 20 can return up to 40 rows. Google serves 10 results per page.

Compare the related scraper

Every input, explained.

Use the exact field names below in JSON. In Apify’s form, enter list items separately, choose filters, and keep numbers and booleans in their proper types.

Default and prefill are different. A default applies when you omit a setting; a prefill is an example already entered in Apify’s form. Review prefilled targets and limits before every run. Some settings have no schema default. You still need to supply at least one supported target.

Targets and search inputs2
queries
ListForm prefill: ["web scraping tools"]

Enter search queries as separate list items. Quoted phrases, site: filters and minus terms work. A pasted Google search URL contributes its query text, not every URL filter.

query
Text

One search query alongside or instead of queries. For several topics, use queries so each search keeps its own ranking context.

Markets, dates and filters4
country
Text

Search market as a two-letter code or country name. This changes geographic ranking; it is separate from language and from the proxy’s exit country.

language
TextDefault: en

Google’s interface language, using a code such as en, de or pt-BR. It does not guarantee every returned page is written in that language.

timeRange
TextDefault: any

Filters Google’s indexed recency window: hour, day, week, month, year or any. It is not a historical SERP snapshot and does not reconstruct rankings from the past.

Suggested JSON values
any hour day week month year
safeSearch
True or falseDefault: false

Enables Google’s SafeSearch filter. Keep it consistent between scheduled runs so changes in filtering do not look like ranking changes.

Limits, details and proxies5
maxItems
IntegerDefault: 10

Maximum organic results per query, not across the entire run. Two queries with maxItems 20 can return up to 40 rows. Google serves 10 results per page.

maxConcurrency
IntegerDefault: 3

1-10 queries run in parallel. Each query’s pages remain sequential. Start with 3 and a small number of results per query.

delayMs
IntegerDefault: 800

Pause between page requests, in milliseconds. Increase it when the source throttles you; reducing it does not remove network delays or the source’s rate limits.

maxPagesPerQuery
Integer

Page limit for each query. One page is 10 organic results. maxItems 100 with maxPagesPerQuery 2 can collect at most about 20 results per query.

proxyConfiguration
ObjectDefault: {"useApifyProxy":true,"apifyProxyGroups":["GOOGLE_SERP"]}

This Actor forces Apify’s GOOGLE_SERP group; other groups do not return usable results. Only the proxy country is honored. Your Apify plan must provide access to that group.

Advanced settings and recovery4
proxyRotations
IntegerDefault: 3

Retries refused work with a new proxy session. More retries can recover temporary blocks but add time and traffic. Keep the default until the log shows a reason to change it.

resume
True or falseDefault: true

Saves progress about every 30 seconds so an Apify restart or migration can continue the current run. Leave it on for normal use.

continueFromLastRun
True or falseDefault: false

Continues unfinished work from the previous run with matching input. Earlier results remain in that run’s dataset. Keep false for a fresh collection or a recurring snapshot.

impersonate
TextDefault: chrome131

The browser identity used for requests. Keep the Actor’s default unless you are diagnosing immediate blocks. It changes the request fingerprint, not the data you ask for.

This reference follows the Actor’s published input fields. Check the live form before changing a production workflow. Check the current input form on Apify.

Configurations you can copy.

Each example is a separate run. Start small, inspect the results, then increase coverage. Update the targets, countries and dates to match your question.

A repeatable search visibility check

Three queries with maxItems 20 can return up to 60 rows. Run the same configuration weekly and store query, position and collection time. Use a separate run with another country to compare markets.

A repeatable search visibility check
{
  "queries": [
    "web scraping tools",
    "competitor ad tracking",
    "marketing data pipeline"
  ],
  "country": "US",
  "language": "en",
  "maxItems": 20,
  "safeSearch": false
}

Research content on one domain

Use site: and quoted phrases to find indexed pages on a particular domain. These results reflect Google’s index, not a complete crawl of that website. Replace apify.com and the phrases with your research targets.

Research content on one domain
{
  "queries": ["site:apify.com \"ad library\"", "site:apify.com \"google search\""],
  "country": "GB",
  "language": "en",
  "maxItems": 10
}

Find recently indexed content

Use the week filter to find recent material for a content review. It filters indexed recency; it does not recreate last week’s rankings or prove when a page was first published.

Find recently indexed content
{
  "queries": ["dbt BigQuery tutorial", "marketing analytics SQL"],
  "country": "US",
  "language": "en",
  "timeRange": "week",
  "maxItems": 20
}

Run, check, export, repeat.

Each row belongs to a query and position. A URL may be reconstructed from Google’s displayed breadcrumbs; inspect url_is_exact and verify uncertain addresses before crawling or joining them to an exact page. Save a dated snapshot for ranking comparisons. This dataset does not contain search volume, paid ads or historical rankings.

  1. Check the dataset and the run’s SUMMARY record. Compare the number collected with your cap, inspect failed or skipped inputs, and verify a few original source links.
  2. Keep the original IDs and add collected_at and run_id when saving results. Export CSV for flat columns; retain JSON when arrays or nested details matter.
  3. Save the tested configuration as an Apify Task and schedule it. For repeated snapshots, leave continueFromLastRun false. Deduplicate new records by source ID while retaining each observation date.
  4. In Make or n8n, wait for a successful run, fetch its dataset and map fields into Sheets or your warehouse. Send records to Looker Studio through a reporting table; use dbt to flatten and test warehouse models.
  5. The same JSON works with Apify’s Actor API. In Claude with Apify MCP, name this Actor, ask it to inspect the live schema, and give explicit targets, markets and result limits before it runs.

Resume is not a fresh snapshot

resume protects the current run if Apify restarts it. continueFromLastRun continues an earlier run with the same configuration; earlier records stay in the earlier dataset. Combine both datasets for the complete collection, and raise a previously reached result cap when continuing. Start fresh when you want to see what changed today.

Follow the Sheets, Claude, Looker and BigQuery setup guides
Run this Actor from the API

Save one configuration above as input.json. Set APIFY_TOKEN to your Apify API token in your terminal, then send the file as the request body.

Start the run
curl --fail-with-body --request POST \
  --url "https://api.apify.com/v2/actors/jmlp~google-search-scraper/runs" \
  --header "Authorization: Bearer $APIFY_TOKEN" \
  --header "Content-Type: application/json" \
  --data-binary @input.json

The response contains a run ID and defaultDatasetId, not finished results. Wait for the run to succeed, set DATASET_ID to that dataset ID, then fetch its items. For large datasets, use limit and offset to page through the export.

Fetch the dataset
curl --fail-with-body \
  --url "https://api.apify.com/v2/datasets/$DATASET_ID/items?format=json" \
  --header "Authorization: Bearer $APIFY_TOKEN"

Apify’s run and export API reference
Dataset export options

When the results look wrong.

Change one setting at a time, keep a small cap, and check the run summary before scaling up.

The source keeps returning empty pages or access errors.

This Actor forces Apify’s GOOGLE_SERP group; other groups do not return usable results. Only the proxy country is honored. Your Apify plan must provide access to that group.

Can I treat every result URL as an exact page address?

Each row belongs to a query and position. A URL may be reconstructed from Google’s displayed breadcrumbs; inspect url_is_exact and verify uncertain addresses before crawling or joining them to an exact page. Save a dated snapshot for ranking comparisons. This dataset does not contain search volume, paid ads or historical rankings.

The selected country returns no useful results.

Search market as a two-letter code or country name. This changes geographic ranking; it is separate from language and from the proxy’s exit country.

The run succeeded but returned nothing

Success means the Actor finished handling the request, not that the source returned data. Check SUMMARY.inputProblem, SUMMARY.problem and the log for missing targets, unsupported filters or refused requests. Test one known target with fewer filters.

Fewer records than expected

Check the global limit, per-search or per-page limits, platform coverage and deduplication. Several searches can find the same record. A source’s headline count can include records the public endpoint does not return. Review unfinished jobs before treating the dataset as complete.

Use the results in your tools.

Google Sheets

Append query, position, title, url, url_is_exact and collected_at. Parse the domain into its own column, then filter for your site and recurring competitors.

Read the setup
Claude + MCP

Ask Claude to group search intent and compare title themes, citing the query and position. It needs more than search results to judge the full contents or quality of a page.

Read the setup
Looker Studio

Use a sheet of dated observations to track your rank and how often competitors appear. Keep the country and language the same across runs.

Read the setup
BigQuery + dbt

Partition observations by collection date and keep query, country and language. Compare ranks for verified domains in SQL. Check reconstructed URLs before treating them as exact page IDs.

Read the setup
Copy a prompt for Claude
Prompt for Claude + Apify MCP
Inspect jmlp/google-search-scraper and collect up to 20 organic results for best CRM for small teams in US English. Group headline themes and recurring domains, cite query and position, flag reconstructed URLs, and suggest content questions to research. Do not invent search volumes.

The fields you’ll get.

Keep the collection time and original IDs with your records. You’ll need them to check where a result came from or compare it with a later run.

Before you draw conclusions

The scraper returns organic results. Search volume and paid keyword data need another source. Google results can vary by market and time. Some URLs are reconstructed from breadcrumbs, so check url_is_exact and validate links before crawling them or joining data by page.

query / position
The search term and one-based organic rank.
title / snippet
The headline and description presented in the result.
url
A URL derived from the displayed result.
url_is_exact
Whether the displayed URL was exact or a breadcrumb path was reconstructed.
displayed_url
Google’s original displayed URL or breadcrumb text.

Common questions.

Does it return search volume or keyword difficulty?

No. Its core output contains organic results, rank, title, URL and snippet. Use a separate data source for search volume or keyword difficulty.

Are all returned URLs exact?

No. When Google displays a breadcrumb path, the scraper may reconstruct it. The url_is_exact flag makes that distinction explicit.

Source and current product details: JMLP’s Google Search Actor on Apify.

Other scrapers
you might use.

Let’s talk about
your project.

Tell me what you need to collect or understand. I can help with a custom scraper, a pipeline or the analysis.

Start a project