# Google Search Scraper - SERP, People Also Ask & Real URLs (`lanternlab318/google-search`) Actor

- **URL**: https://apify.com/lanternlab318/google-search.md
- **Developed by:** [Lantern Lab](https://apify.com/lanternlab318) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $7.49 / 1,000 search pages

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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 Results Scraper

Give it search terms, a country and a language, and get Google's public search result pages back as structured data: every organic result with its position, title, real destination URL, displayed URL and snippet, the People Also Ask questions, related searches, text ads, Google's result count and the search URL. One row per search page. No Google login, no cookies, no account of your own. **$7.49 per 1,000 search pages plus $0.005 per run start, and only pages that returned results are charged.** See [Pricing](#pricing).

Input:

```json
{
  "queries": ["how to make cold brew coffee", "kaffeemaschine kaufen"],
  "countryCode": "us",
  "languageCode": "en",
  "maxPagesPerQuery": 2
}
```

Output: one row per search page (`recordType: "search_page"`), followed by a free run summary. This sample is shortened from real pages (the ad comes from a different search):

```json
{
  "recordType": "search_page",
  "searchQuery": { "term": "how to make cold brew coffee", "page": 1, "languageCode": "en", "countryCode": "us" },
  "searchUrl": "https://www.google.com/search?q=how+to+make+cold+brew+coffee&hl=en&gl=us",
  "totalResultsText": "About 145 results",
  "totalResults": 145,
  "organicResults": [
    {
      "position": 2,
      "title": "A Guide to Cold Brew Coffee",
      "url": "https://www.seriouseats.com/guide-to-cold-brew-5196803",
      "urlStatus": "resolved",
      "displayedUrl": "https://www.seriouseats.com › Ingredients › Drink Guides",
      "siteName": "Serious Eats",
      "snippet": "Cold brew is made using a tighter ratio—Wolczynski recommends starting with 12 ounces (340g) of coffee to 64 ounces (1814g) of water, which is a much higher ...",
      "dateText": null
    }
  ],
  "peopleAlsoAsk": [
    { "question": "How do I sweeten cold brew coffee?" },
    { "question": "Can you make cold brew in the fridge?" }
  ],
  "relatedSearches": [
    { "query": "Cold brew ratio", "url": "https://www.google.com/search?q=Cold+brew+ratio&hl=en&gl=us" }
  ],
  "paidResults": [
    {
      "position": 1,
      "title": "14 Insurers Compared In Mins",
      "url": "https://www.chill.ie/",
      "displayedUrl": "https://www.chill.ie › get-a-quote-now",
      "advertiser": "Chill.ie",
      "description": "Compare Prices At Chill ie — Get an instant €40 discount when you buy online. Compare 14 trusted insurers in minutes. …",
      "placement": "middle"
    }
  ],
  "hasNextPage": true,
  "duplicatesSkipped": 0,
  "charged": true,
  "partial": false,
  "partialReason": null,
  "statusText": "Collected 8 organic result(s) from this page.",
  "collectedAt": "2026-10-30T07:41:12.311Z"
}
```

### What it does

- **Returns the real result URL.** Google's page no longer links straight to the result; it links through an opaque redirect. The Actor resolves each one to the address it opens, at no extra charge, and `urlStatus` tells you how each `url` was obtained.
- **People Also Ask, related searches, ads and the result count** on every page, along with the organic results.
- **Stops at `maxPagesPerQuery`.** Page 2 onwards is fetched only while Google shows a next page, and never past your limit, so you never pay for pages you did not ask for.
- **Deduplicates.** A search term repeated in your list is searched once. An organic result that Google repeats on a later page of the same term is left out of that page and counted in `duplicatesSkipped`.
- **Never reports a failure as "no results".** A page Google refused, did not render, or that the run never reached is still written, with `partial: true`, the reason in `partialReason`, and `null` (not empty) result lists. Such a page is not charged.

### Input

| Field | Default | What it does |
| --- | --- | --- |
| `queries` | — | Search terms, one per line. Google operators such as `site:` or quotes work as they do on Google. |
| `countryCode` | `us` | Google's `gl` parameter: which country's results to show. |
| `languageCode` | `en` | Google's `hl` parameter: the interface language. |
| `maxPagesPerQuery` | `1` | Pages per search term, 1 to 10. About 10 organic results per page. |
| `resolveUrls` | `true` | Resolve each organic result to its real URL. The lookups go through the Apify datacenter proxy and are not charged. Turn it off and `url` is `null` with `urlStatus: "not_requested"`. |
| `proxyConfiguration` | Apify Google SERP proxy | Keep the default. Google answers other IPs with a page that only asks for JavaScript, and the run reports that as `page_not_rendered`. |

### Output fields

- `organicResults[].position` counts from 1 across all pages of a term. `dateText` is the date Google prints in front of some snippets, exactly as written. `displayedUrl` is the breadcrumb Google shows; for forum and video results Google puts text there, such as a comment or view count.
- `organicResults[].urlStatus`: `resolved` (from Google's redirect), `from_page` (Google gave the address directly), `unresolved` (Google would not resolve it, so `url` is `null`; about one result in ten in our tests), `not_requested`. Google's `srsltid` click identifier is removed from resolved URLs.
- `peopleAlsoAsk[].question`: the questions shown. Google loads the answers only when a question is expanded, so answers are not included.
- `paidResults`: the text ads Google rendered on the page. `url` is the advertiser's landing page; `placement` is `top`, `middle` or `bottom` relative to the organic results. Ads follow the location of the IP Google sees, which can differ from `countryCode` (organic results follow `countryCode`), and many pages carry none.
- `totalResults`: the number in Google's own "About N results" estimate, or `null` when the page carries none.
- `partialReason` on an uncollected page: `blocked`, `page_not_rendered`, `upstream_error`, `charge_limit_reached`, `payment_required`, `not_collected`.

The run summary (last dataset row and `RUN_SUMMARY` in the key-value store) gives pages written and charged, empty and missing pages by reason, URLs resolved, duplicates skipped, and why the run stopped early, if it did.

### Pricing

Pay per event:

- **Search page: $7.49 per 1,000** ($0.00749 each), charged only for a page that returned at least one organic result. Empty pages, refused pages and pages never reached are free.
- **Run start: $0.005** per run.

Example: 100 search terms × 2 pages = 200 pages ≈ $1.50 + $0.005. Set a maximum charge per run in Apify Console; the Actor stops before a page it could not pay for and marks the rest `charge_limit_reached`.

### Limits

- **Public data only.** Google's ordinary public search page; no login and no personal data beyond what Google prints in a result.
- Not included: AI Overviews and AI Mode, image/video/news tabs, and rank tracking over time.
- Google decides what a page holds. Result counts per page vary (forums, videos and other blocks take slots), and deeper pages can stop early when Google has no next page.

# Actor input Schema

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

One search term per line, for example "best running shoes" or "site:example.com pricing". Google operators work as they do on Google. Every term is searched separately; a term repeated in the list is searched once.

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

Two-letter country code of the results to show (Google's gl parameter), for example "us", "gb", "de", "jp".

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

Interface language of the search (Google's hl parameter), for example "en", "de", "ja", "pt-BR". Pair it with a matching country.

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

Hard limit on search pages per term, about ten organic results each. Further pages are fetched only while Google shows a next page, and never past this limit.

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

Google's page links each organic result through an opaque redirect. When on, every result's url is resolved to the address it opens, at no extra charge. When off, url is null with urlStatus "not_requested".

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

The proxy the search page requests go through. Keep the default: Google answers datacenter and residential IPs with a page that only asks for JavaScript, while the Apify Google SERP proxy group returns the rendered results. With any other group, pages come back as partialReason "page_not_rendered" and are not charged.

## Actor input object example

```json
{
  "queries": [
    "how to make cold brew coffee"
  ],
  "countryCode": "us",
  "languageCode": "en",
  "maxPagesPerQuery": 1,
  "resolveUrls": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "GOOGLE_SERP"
    ]
  }
}
```

# Actor output Schema

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

No description

## `runSummary` (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": [
        "how to make cold brew coffee"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("lanternlab318/google-search").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": ["how to make cold brew coffee"] }

# Run the Actor and wait for it to finish
run = client.actor("lanternlab318/google-search").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": [
    "how to make cold brew coffee"
  ]
}' |
apify call lanternlab318/google-search --silent --output-dataset

```

## MCP server setup

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

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/1J0dzi7NFygnFViNs/builds/YZBZE8D8cOqNin3Zd/openapi.json
