# Google Search Scraper (`jmlp/google-search-scraper`) Actor

Scrape Google organic search results - position, title, URL and snippet - for any list of queries, with country, language and time-range targeting. Ads and AI overviews excluded, so position is true organic rank. One request per 10 results.

- **URL**: https://apify.com/jmlp/google-search-scraper.md
- **Developed by:** [Mary Lou](https://apify.com/jmlp) (community)
- **Categories:**
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 2 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $0.15 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Google Search Scraper — SERP results, rank tracking, no browser

Scrape Google organic search results for any list of keywords and export them
as JSON, CSV or Excel. Every row gives you **organic rank, title, URL and
snippet**, with country, language and time-range targeting.

Built for SEO monitoring and rank tracking: schedule it, diff `position` over
time, and watch your keywords move. No browser, no API key, nothing to
configure.

***

### What you can do with it

- **Keyword rank tracking** — run daily on your target keywords and chart
  position over time
- **SERP monitoring** — see who is ranking, who moved, and who appeared
- **Competitor analysis** — find every page a competitor ranks for using
  `site:` queries
- **Local SEO** — the same keyword in `us`, `gb`, `de` returns different
  rankings; compare them
- **Content research** — pull the top results and snippets for a topic cluster
- **Lead sourcing** — turn `site:` and intent queries into domain lists, then
  feed them to a contact scraper
- **Feed a RAG pipeline or LLM** with fresh, ranked search results

***

### How to scrape Google search results

Paste your search terms into **Search queries**, one per line, and press Start.
That is the whole setup.

Google search operators all work:

| Operator | Example |
| --- | --- |
| Site restriction | `site:apify.com actor` |
| Exact phrase | `"web scraping tools"` |
| Exclusion | `python scraper -selenium` |
| Either/or | `serp api OR serp scraper` |
| Title match | `intitle:pricing` |
| File type | `filetype:pdf market report` |

***

### Input

| Field | What it does |
| --- | --- |
| **Search queries** | One per line. Operators supported |
| **Results per query** | How many organic results to return. 10 = one request |
| **Country** | 2-letter code (`us`, `gb`, `de`) — changes the ranking Google serves |
| **Language** | 2-letter interface language code |
| **Time range** | Past hour, 24 hours, week, month, year, or any time |
| **SafeSearch** | Google's SafeSearch filtering |
| **Max concurrency** | How many queries to run at once |

***

### Output

One row per organic result. Export as **JSON, CSV, Excel, XML or RSS**.

```json
{
  "query": "web scraping tools",
  "position": 3,
  "title": "5 Best Web Scraping Tools (Ranked By Real Tests)",
  "url": "https://www.scrapingdog.com/blog",
  "url_is_exact": false,
  "displayed_url": "https://www.scrapingdog.com › Blog",
  "snippet": "Jan 25, 2026 — Scrapingdog - Best overall for scalable, cost..."
}
```

| Field | Notes |
| --- | --- |
| `query` | Which search this result came from, so multi-keyword runs stay separable |
| `position` | **True organic rank.** Ads, AI overviews, shopping carousels and video shelves are excluded, not filtered afterwards |
| `title` | The result headline |
| `url` | Absolute URL — see the section below on how it is derived |
| `url_is_exact` | Whether the URL is verbatim or reconstructed |
| `displayed_url` | Google's own breadcrumb form, verbatim and never guessed |
| `snippet` | The description text beneath the result |

The same URL ranking for two different queries is **two rows**, because it is
two rankings. Dedup is per query, not global.

***

### About the URL field — read this one

**Google now encrypts its outbound result links.** Every link on a modern
results page points at `/goto?url=<encrypted blob>`; the blob is opaque, the
tracking attribute repeats the same blob, and requesting it directly returns an
error. The real destination is genuinely no longer in the page.

What *is* in the page is the URL Google displays to a human —
`https://github.com › apify`. So:

- **`displayed_url`** is exactly what Google showed. Never guessed.
- **`url`** is that turned back into a usable link.
- **`url_is_exact`** tells you which kind you got:
  - `true` — Google displayed a bare domain, and the URL is verbatim
  - `false` — the path was rebuilt from breadcrumbs. The **domain is always
    right**; the path is usually right but can be off, because Google
    prettifies breadcrumbs and truncates long ones. When a path is truncated,
    only the origin is returned rather than a URL containing a literal `...`
  - `null` — the result showed no URL at all, typically a video

Filter on `url_is_exact` when you need certainty. Measured across 20 results:
5 exact, 12 reconstructed, 3 with no URL. Any scraper claiming exact Google
URLs today is either following every link individually — at 10x the requests —
or guessing without telling you.

***

### Pricing

**$0.15 per 1,000 results, plus Apify platform usage.**

| Plan tier | Price per 1,000 results |
| --- | --- |
| Free / Bronze | $0.17 |
| Silver, Gold, Platinum, Diamond | $0.15 |

**Read the usage half before you scale this one up.** Google serves no results
at all to an ordinary HTTP client, so every request goes through Apify's SERP
proxy, billed separately at **$2.50 per 1,000 SERPs** on Free and Starter. One
request returns 10 results, so that works out at **about $0.25 per 1,000
results** — larger than the scraper charge itself, and the biggest line in any
run. It falls to $2.00 per 1,000 SERPs on Scale and $1.70 on Business.

Compute is negligible next to that (~$0.01 per 1,000 results): there is no
browser and no per-query API fee.

**Requests cost money, not results.** 10 results is one request, 100 results is
ten. Set **Results per query** to exactly what you need.

***

### FAQ

**Why can't I just fetch google.com/search myself?**
You can, and you will get a page with no results in it. Google retired its
no-JavaScript results page. A direct fetch returns a "please enable JavaScript"
redirect; an older browser signature gets the cookie consent wall instead. Nine
user-agent and parameter combinations were tested and every one returned HTTP
200 with zero results. This Actor routes through Apify's SERP proxy, which is
what makes plain HTTP work at all.

**Does it use a headless browser?**
No — and that is deliberate. A headless Chromium version was built and measured
first: it timed out at 75 seconds per query and needed a ~1.5 GB image. Plain
HTTP through the SERP proxy is 1.3–4 seconds per request and far cheaper.

**Do I need to configure a proxy?**
No. It is fixed to the one group that works. A country set in the proxy options
is honoured; the group is not changeable, because nothing else returns results.

**Are ads included?**
No. `position` is organic rank only.

**Can I get more than 100 results per keyword?**
Yes, but Google's own result depth thins out fast and quality drops. Raise
**Results per query**; each extra page is one more request.

**Why is a snippet occasionally empty?**
Some result types genuinely carry no description text. Coverage was 30/30 in
the last measured run.

**How fast is it?**
Measured on the platform: 60 results across 3 queries in 13 seconds.

**Can I run it on a schedule?**
Yes — that is the main use. Run daily on a keyword list and diff `position` to
track rankings.

***

### Related scrapers

- **Website Contact Scraper** — turn the domains you find here into emails,
  phone numbers and social profiles
- **LinkedIn Jobs Scraper** — job postings without a login or cookie
- **TikTok Ad Library Scraper**, **Meta Ads Library Scraper**, **Google Ads
  Transparency Center Scraper** — competitor ad intelligence

# Actor input Schema

## `queries` (type: `array`):

What to search for, one per line. Google operators work - "site:example.com", quoted phrases, minus terms.

## `query` (type: `string`):

A single query, for convenience when you only have one.

## `maxItems` (type: `integer`):

How many organic results to return for each query. Google serves 10 per page, so 10 costs one request and 100 costs ten.

## `country` (type: `string`):

2-letter country code to search as, e.g. us, gb, de. Changes the ranking, which is the point if you care about local results.

## `language` (type: `string`):

2-letter interface language code, e.g. en, de, fr.

## `timeRange` (type: `string`):

Only return pages Google has indexed within this window.

## `safeSearch` (type: `boolean`):

Turn on Google's SafeSearch filtering.

## `maxConcurrency` (type: `integer`):

How many queries to run at once.

## `delayMs` (type: `integer`):

Politeness pause between result pages of the same query.

## `maxPagesPerQuery` (type: `integer`):

Safety cap while testing. One page is 10 results.

## `proxyConfiguration` (type: `object`):

This Actor always uses Apify's GOOGLE\_SERP proxy group, because Google returns a JavaScript-required page with no results to anything else. Only the country setting here is honoured; the group is fixed.

## `proxyRotations` (type: `integer`):

If a query is refused, mint a new proxy session and retry this many times.

## `resume` (type: `boolean`):

Save progress every ~30s so a migrated or restarted run picks up where it stopped.

## `continueFromLastRun` (type: `boolean`):

If your previous run with the same input was interrupted, do only the queries it missed.

## `impersonate` (type: `string`):

Which browser TLS fingerprint to present.

## Actor input object example

```json
{
  "queries": [
    "web scraping tools"
  ],
  "maxItems": 10,
  "language": "en",
  "timeRange": "any",
  "safeSearch": false,
  "maxConcurrency": 3,
  "delayMs": 800,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "GOOGLE_SERP"
    ]
  },
  "proxyRotations": 3,
  "resume": true,
  "continueFromLastRun": false,
  "impersonate": "chrome131"
}
```

# Actor output Schema

## `results` (type: `string`):

One row per organic result: rank, title, URL and snippet.

## `summary` (type: `string`):

How many results per query, how many carried a URL and a snippet, and whether anything was refused.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "queries": [
        "web scraping tools"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("jmlp/google-search-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "queries": ["web scraping tools"] }

# Run the Actor and wait for it to finish
run = client.actor("jmlp/google-search-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "queries": [
    "web scraping tools"
  ]
}' |
apify call jmlp/google-search-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jmlp/google-search-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/W8FTeOLpRP0geGt51/builds/1UwwDALM2xXR4F5Su/openapi.json
