# Google Search SERP Scraper (`axiomworks/google-search-results-scraper`) Actor

Scrape Google Search pages for keywords or google.com/search URLs: position, title, URL, displayed URL, site name and snippet per organic result, by country and language. Optional ads, People Also Ask, related searches and AI Overview flag are saved to a separate serp-pages dataset.

- **URL**: https://apify.com/axiomworks/google-search-results-scraper.md
- **Developed by:** [Axiom Works](https://apify.com/axiomworks) (community)
- **Categories:** SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.79 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Google Search SERP Scraper

### What does Google Search SERP Scraper do?

Google Search SERP Scraper turns Google search result pages into structured data. You give it one or more search phrases, or full google.com/search links, and it returns one dataset row for every organic result: the title, the real target URL, the address line shown under the title, the site name, the snippet, and the date when Google shows one at the start of the snippet.

You choose the country (the `gl` parameter) and the interface language (the `hl` parameter), so you can see what searchers in Germany, the United Kingdom or the United States are shown for the same phrase. Each row repeats the query, country, language, result page and absolute rank, so rows from many searches can live in one dataset without losing track of where they came from.

Google links most results through a redirect address. By default the Actor follows each one with a single lightweight request and stores the final address in `url`. You can switch this off to save time, in which case `url` holds Google's redirect link.

The Actor can also save the extra parts of each results page as additional rows in the same dataset (`recordType` `serpPage`): paid results (ads), People Also Ask questions, related searches, the total number of results Google reports, and a flag showing whether an AI Overview block was present. It does not use a login and only reads public results pages.

This Actor is an independent tool and is not affiliated with, endorsed by or sponsored by Google.

Typical uses:

- **Rank tracking.** Run the same phrases on a schedule and compare the `position` of your pages and your competitors' pages.
- **Content research.** Read the titles and snippets that already rank for a topic before you write about it.
- **Competitor monitoring.** See which sites appear for the phrases that matter to you, and in which country.
- **Lead and source lists.** Collect the sites that rank for a niche query and use the `url` field as a starting list.
- **Market comparison.** Run one phrase with several country and language codes and compare the results.

### What data can you get?

Every organic result becomes one row in the default dataset.

| Field | Description |
| --- | --- |
| `recordType` | `organic` for organic results, `serpPage` for per-page SERP feature rows |
| `id` | Stable id derived from query, country, language and position |
| `searchQuery` | The search phrase that produced the row |
| `countryCode` | Country code used for the search (`gl`) |
| `languageCode` | Language code used for the search (`hl`) |
| `page` | Results page the row came from, starting at 1 |
| `position` | Absolute rank among organic results for that search |
| `title` | Result title |
| `url` | Target URL of the result. If it could not be resolved, or **Resolve result URLs** is off, this is Google's redirect link |
| `displayedUrl` | The address line shown under the title |
| `siteName` | Site name shown next to the result |
| `description` | Result snippet |
| `date` | Date shown at the start of the snippet, when present |
| `sourceUrl` | The Google results page the row came from |
| `scrapedAt` | ISO timestamp of when the row was saved |

Fields that Google does not show for a result, such as `date` or `siteName`, can be empty for that row.

With **Include SERP features** switched on, the default dataset also gets one row per results page, marked `"recordType": "serpPage"` (organic rows are marked `"recordType": "organic"`):

| Field | Description |
| --- | --- |
| `searchQuery`, `countryCode`, `languageCode`, `page` | The search and page the row describes |
| `url` | The Google results page address |
| `resultsTotal` | Total results count Google reports for the search, or null when not shown |
| `hasAiOverview` | True when an AI Overview block was present. Only the flag is stored, not its text |
| `paidResults` | Ads on the page, each with `title`, `displayedUrl`, `description` and `url` |
| `peopleAlsoAsk` | People Also Ask questions found on the page |
| `relatedQueries` | Related searches listed by Google |

### How to use Google Search SERP Scraper

1. Open the Actor on Apify and go to the **Input** tab.
2. Add your search phrases in **Search queries**. You can also paste full google.com/search links; their `q`, `gl`, `hl` and `start` parameters are used.
3. Set how many result pages you want per query. Google returns up to 10 organic results per page, often fewer when ads, maps or other blocks are shown.
4. Pick a country and a language.
5. Leave **Resolve result URLs** on if you want the real target addresses.
6. Optionally set **Max organic results** (default 100) to cap the total number of rows, and switch on **Include SERP features** if you also want ads, questions and related searches.
7. Click **Start**. When the run finishes, open the **Output** tab and download the dataset as JSON, CSV, Excel or HTML, or read it through the API.

The default proxy setting uses Apify Proxy. Result pages are requested through the GOOGLE\_SERP proxy group, and when a page has to be opened in a browser instead, the proxy you choose in the input is used. Residential proxies are recommended for that case.

Google sometimes shows a block page or a CAPTCHA. The Actor does not solve CAPTCHAs. It retries with a fresh proxy session several times, and if a page still returns no results it logs a warning and moves on. If you see this often, lower **Max concurrency** or use residential proxies.

### Input

| Field | Type | Description |
| --- | --- | --- |
| `queries` | array, required | Search terms, or full google.com/search URLs. Example: `["web scraping api"]` |
| `maxItems` | integer | Stop after this many organic results in total across all queries, 1-10000. Default 100 |
| `maxPagesPerQuery` | integer | Result pages per query, 1 to 10, default 1. Google caps depth at roughly 100 results |
| `countryCode` | string | Two-letter country code (`gl`), default `us` |
| `languageCode` | string | Interface language code (`hl`), default `en` |
| `resolveUrls` | boolean | Resolve Google redirect links to the real target URL, default true |
| `includeSerpFeatures` | boolean | Also save one row per results page (`recordType` `serpPage`), costs extra per page row, default false |
| `maxConcurrency` | integer | Result pages processed in parallel, 1 to 10, default 3 |
| `proxyConfiguration` | object | Apify Proxy settings; residential proxies are recommended |

Example input:

```json
{
  "queries": ["web scraping api"],
  "maxPagesPerQuery": 1,
  "countryCode": "us",
  "languageCode": "en",
  "resolveUrls": true
}
```

If the input is invalid, for example an empty `queries` list, a link that is not a google.com/search address, a link without a `q` parameter, or a `maxItems` that is not a positive whole number, the run stops right away with a clear error message.

### Output

A real row from a run with the input above:

```json
{
	"recordType": "organic",
	"id": "7466883dec730892c9b8",
	"searchQuery": "web scraping api",
	"countryCode": "us",
	"languageCode": "en",
	"page": 1,
	"position": 1,
	"title": "How to Use WebScrapingAPI To Scrape Any Website",
	"url": "https://www.webscrapingapi.com/webscrapingapi-guide",
	"displayedUrl": "https://www.webscrapingapi.com › Blog › Guides",
	"siteName": "WebScrapingAPI",
	"description": "If you are interested in web scrapers and want a solution that can extract various data from the Internet, you've come to the right place!",
	"date": "Apr 28, 2026",
	"sourceUrl": "https://www.google.com/search?q=web+scraping+api&gl=us&hl=en",
	"scrapedAt": "2026-09-30T18:09:43.881Z"
}
```

Results change over time and by location, so the titles, snippets and positions you get will differ from this example.

### How much does it cost?

The Actor is charged per result: you pay for each organic result row saved to the dataset. If you switch on **Include SERP features**, each extra results-page row is also charged. See the Pricing tab of the Actor for the current rates.

To keep a run inexpensive, set **Max organic results** and a small number of result pages, and switch off **Resolve result URLs** when the redirect links are enough.

### Use with the API

You can start the Actor and read its results through the Apify API.

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("axiomworks/google-search-results-scraper").call(
    run_input={"queries": ["web scraping api"], "maxPagesPerQuery": 1}
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["position"], item["title"], item["url"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('axiomworks/google-search-results-scraper').call({
    queries: ['web scraping api'],
    maxPagesPerQuery: 1,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

cURL:

```bash
curl -X POST "https://api.apify.com/v2/acts/axiomworks~google-search-results-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"queries": ["web scraping api"], "maxPagesPerQuery": 1}'
```

You can also connect runs to schedules, webhooks and the usual Apify integrations to send results to spreadsheets, databases or other tools.

### Use with AI agents (MCP)

You can call this Actor from any MCP client through the Apify MCP server. Add this to your client configuration:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com/?tools=axiomworks/google-search-results-scraper"
    }
  }
}
```

Example prompts:

- "Search Google for 'web scraping api' in the US and list the top 10 titles with their URLs."
- "Search 'headless browser' with language de and includeSerpFeatures on, and return the serpPage rows with peopleAlsoAsk and relatedQueries."
- "Compare the top results for 'best crm software' in the US and the UK."

The argument descriptions in the input schema tell the agent which fields to fill in.

### FAQ

**How many results can I get for one query?**
Google returns up to 10 organic results per page, often fewer, and limits depth to roughly 100 results, so the most you can expect for one phrase is up to about 100 rows.

**Why is `url` sometimes a Google link?**
Either **Resolve result URLs** is off, or the redirect could not be followed. In both cases `url` holds Google's redirect link instead of the target address.

**Why is `date` empty for some rows?**
The field is only filled when Google shows a date at the start of the snippet.

**Does it return the AI Overview text?**
No. Only a true or false flag, `hasAiOverview`, is saved in the `serpPage` rows.

**Can I paste a Google search link instead of a phrase?**
Yes. Links must be google.com/search addresses with a `q` parameter. Their `q`, `gl`, `hl` and `start` values are used.

**Does it solve CAPTCHAs?**
No. If Google shows one, the Actor retries with a fresh proxy session and then moves on to the next page.

**Are the results the same as what I see in my browser?**
Not always. Google personalizes and changes results by location and time, so positions can differ from what you see when you search yourself.

**Does it use AI?**
No. This Actor does not use any third-party AI processor.

### Is it legal to scrape Google Search?

The Actor reads publicly visible search result pages and does not log in to any account. Whether your use of the collected data is lawful depends on what you collect, where you are, and what you do with it. Search results can contain personal data, and Google's terms of service place limits on automated access. You are responsible for making sure that your use of search results complies with Google's terms and with the laws that apply to you, including data protection rules. If in doubt, ask a legal adviser.

### Feedback

If a field is missing, a search returns fewer rows than you expect, or Google changes its page layout and something stops working, open an issue from the Actor page on Apify with the run link and the input you used. Reports like these are the fastest way to get a fix.

# Actor input Schema

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

Search terms, or full google.com/search URLs (their q, gl, hl and start parameters are used).

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

Maximum number of dataset rows (organic results plus serpPage rows if enabled), 1-10000. Default 100.

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

Result pages per query, 1-10, default 1. Google returns up to 10 organic results per page, often fewer, and caps depth at roughly 100 results.

## `countryCode` (type: `string`):

Two-letter country code (gl parameter), e.g. us, de, gb.

## `languageCode` (type: `string`):

Interface language code (hl parameter), e.g. en, de.

## `resolveUrls` (type: `boolean`):

Google links results through redirectors. Resolve them to the real target URL with one lightweight request per result.

## `includeSerpFeatures` (type: `boolean`):

Also save one row per result page (recordType 'serpPage') with ads, People Also Ask, related searches, total results and AI Overview flag. Costs extra per page row. Default false.

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

Number of result pages processed in parallel.

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

With Apify Proxy, result pages are requested through the GOOGLE\_SERP proxy group. The proxy chosen here is used when a page has to be loaded in a browser; residential proxies are recommended.

## Actor input object example

```json
{
  "queries": [
    "web scraping api"
  ],
  "maxItems": 10,
  "maxPagesPerQuery": 1,
  "countryCode": "us",
  "languageCode": "en",
  "resolveUrls": true,
  "includeSerpFeatures": false,
  "maxConcurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

No description

# 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 api"
    ],
    "maxItems": 10,
    "maxPagesPerQuery": 1,
    "countryCode": "us",
    "languageCode": "en",
    "resolveUrls": true,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("axiomworks/google-search-results-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 api"],
    "maxItems": 10,
    "maxPagesPerQuery": 1,
    "countryCode": "us",
    "languageCode": "en",
    "resolveUrls": True,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("axiomworks/google-search-results-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 api"
  ],
  "maxItems": 10,
  "maxPagesPerQuery": 1,
  "countryCode": "us",
  "languageCode": "en",
  "resolveUrls": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call axiomworks/google-search-results-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,axiomworks/google-search-results-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/X3cweoULQoGO6XWjx/builds/wIM3YW5l6EGOzLAFK/openapi.json
