# Google Custom Search API Replacement (Drop-in JSON) (`s3nafps/google-custom-search-api`) Actor

Drop-in replacement for Google's Custom Search JSON API, which shuts down Jan 1, 2027. Same parameters, same customsearch#search JSON, Google-style errors. Live web address or batch runs, plus optional Markdown of every result page for LLMs and AI agents.

- **URL**: https://apify.com/s3nafps/google-custom-search-api.md
- **Developed by:** [mohamed senator](https://apify.com/s3nafps) (community)
- **Categories:** AI, Developer tools, SEO tools
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 search answereds

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?

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's **Custom Search JSON API** (`customsearch/v1`) is closed to new customers and shuts down on **January 1, 2027**. This actor answers in **exactly the same JSON format**, with the same parameters, so you can switch by changing the web address and the key. Your code that reads `items[].title`, `items[].link` and `items[].snippet` keeps working.

It also does something Google's API never did: add **`markdown=true`** and each result comes with the clean text of its page, ready for LLMs, RAG pipelines and AI agents.

*Not affiliated with or endorsed by Google. Results come from public Google Search pages.*

![Change one line: the Google Custom Search URL becomes this actor's URL, with the same parameters and JSON](https://raw.githubusercontent.com/s3nafps/google-custom-search-api/main/docs/images/hero.png)

### Why use it

- **Drop-in:** same `q`, `num`, `start`, `gl`, `hl`, `lr`, `cr`, `safe`, `dateRestrict`, `siteSearch`, `exactTerms`, `excludeTerms`, `orTerms`, `fileType`, and the same `customsearch#search` JSON, including `queries.nextPage`, `searchInformation`, `htmlTitle` and `htmlSnippet`.
- **Two ways in:** a live web address that works like Google's endpoint, or normal actor runs for lists of searches, schedules and integrations.
- **Built for AI agents:** optional Markdown of every result page, and it works with Apify's MCP server, LangChain and any HTTP client.
- **Cheaper than Google's $5 per 1,000:** see pricing below.

### Use cases

- **Migrating off Google's Custom Search JSON API** before January 1, 2027, without rewriting the code that reads the JSON.
- **Search tool for AI agents and RAG:** one call returns results plus the Markdown text of each page.
- **SEO and rank checks:** run lists of searches per country and language on a schedule.
- **Research and monitoring:** track who ranks for a topic, a brand or a site with `siteSearch` and `dateRestrict`.

### Speed and price at a glance

![Answer time of 19 searches in a live test: 2 under 3 s, 6 between 3 and 6 s, 7 between 6 and 9 s, 2 between 9 and 15 s, 2 over 15 s](https://raw.githubusercontent.com/s3nafps/google-custom-search-api/main/docs/images/speed.png)

From a live test on October 18, 2026. 88 of 90 test searches were answered; the other 2 returned a 503 error and were not charged.

![Price per 1,000 searches: about $4 for this actor, $5 for Google Custom Search, $5 and $20 for other Store replacements](https://raw.githubusercontent.com/s3nafps/google-custom-search-api/main/docs/images/price.png)

Apify Store prices on October 19, 2026. Platform cost is for the Free and Starter plans and is lower on bigger plans.

### Switch in one minute

Before (Google):

```
GET https://www.googleapis.com/customsearch/v1?key=GOOGLE_API_KEY&cx=ENGINE_ID&q=coffee
```

After (this actor):

```
GET https://s3nafps--google-custom-search-api.apify.actor/customsearch/v1?token=APIFY_TOKEN&q=coffee
```

`key` becomes your [Apify API token](https://console.apify.com/settings/integrations) (`token=` or an `Authorization: Bearer` header). `cx` is accepted and ignored, so old URLs don't break.

#### curl

```bash
curl "https://s3nafps--google-custom-search-api.apify.actor/customsearch/v1?q=best+espresso+machine&num=5&gl=us" \
  -H "Authorization: Bearer $APIFY_TOKEN"
```

#### Python

```python
import os, requests

r = requests.get(
    "https://s3nafps--google-custom-search-api.apify.actor/customsearch/v1",
    params={"q": "best espresso machine", "num": 5, "gl": "us"},
    headers={"Authorization": f"Bearer {os.environ['APIFY_TOKEN']}"},
    timeout=60,
)
for item in r.json().get("items", []):
    print(item["title"], item["link"])
```

#### JavaScript

```js
const url = new URL('https://s3nafps--google-custom-search-api.apify.actor/customsearch/v1');
url.search = new URLSearchParams({ q: 'best espresso machine', num: '5' });
const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.APIFY_TOKEN}` } });
const data = await res.json();
console.log(data.items?.map((i) => i.link));
```

#### LangChain tool for an AI agent

```python
import os, requests
from langchain_core.tools import tool

@tool
def google_search(query: str) -> str:
    """Search Google and return the top results with page text."""
    r = requests.get(
        "https://s3nafps--google-custom-search-api.apify.actor/customsearch/v1",
        params={"q": query, "num": 5, "markdown": "true", "markdownMaxChars": 4000},
        headers={"Authorization": f"Bearer {os.environ['APIFY_TOKEN']}"},
        timeout=90,
    )
    items = r.json().get("items", [])
    return "\n\n".join(f"# {i['title']}\n{i['link']}\n{i.get('markdown', i['snippet'])}" for i in items)
```

### Normal runs (lists of searches)

Start the actor with a list of searches. Each search becomes one dataset row in Google's format, plus `searchQuery`:

```json
{
  "queries": ["best coffee grinder 2026", "how to make cold brew"],
  "num": 10,
  "gl": "us",
  "markdown": false
}
```

Per-search settings work too: `{"queries": [{"q": "kaffee", "gl": "de", "hl": "de"}]}`. Runs stop cleanly at your **maximum cost per run**.

### Parameters

| Parameter | Supported | Notes |
| --- | --- | --- |
| `q` | Yes | Required |
| `num` | Yes | 1 to 10 (default 10) |
| `start` | Yes | 1 to 91, up to 100 results per query |
| `gl`, `hl`, `lr`, `cr` | Yes | Country and language |
| `safe` | Yes | `active` or `off` |
| `dateRestrict` | Yes | `d5`, `w2`, `m6`, `y1`... |
| `siteSearch`, `siteSearchFilter` | Yes | Include (`i`) or exclude (`e`) one site |
| `exactTerms`, `excludeTerms`, `orTerms`, `fileType` | Yes | Added to the search the way Google does |
| `cx`, `key` | Accepted, ignored | |
| `markdown`, `markdownMaxChars` | Extra | Page text as Markdown (default 20,000 characters per page) |
| `searchType=image`, `sort`, `rights`, image filters | Not yet | Returns a 400 error in Google's format |

Errors use Google's shape (`error.code`, `error.message`, `error.errors[]`, `error.status`). If Google doesn't answer after a few quick retries (at most about 20 seconds) you get a **503** with a `Retry-After: 1` header and are **not charged** the search fee, so it is safe to retry right away. In normal runs, searches that got a 503 are retried once automatically at the end of the run.

### Pricing

Pay per event, plus Apify's own platform costs:

| Event | Price |
| --- | --- |
| Search answered (even with zero results) | **$1.50 per 1,000** ($0.0015 each; Silver $0.0013, Gold and above $0.0012) |
| Page converted to Markdown | **$1 per 1,000** ($0.001 each; Silver $0.0009, Gold and above $0.0008) |

Apify's platform costs are mostly the Google search proxy. In our October 20, 2026 test they came to about **$2.90 per 1,000 searches** on the Free and Starter plans (less on larger plans). Total: **about $4.40 per 1,000 searches**, below Google's $5. Failed searches (503) are not charged the search fee.

### FAQ

**How fast is it?** Most searches answer in a few seconds (see the speed test above). If Google's proxy stalls, the actor starts a second try in parallel after 5 seconds and gives up after about 20 seconds with a 503, so a request never hangs. Set your client timeout to 30 seconds, or 45 with Markdown, and retry on 503. For big lists, normal runs do 5 searches at a time.

**What happens when a search fails?** You get Google's error format with HTTP 503 and you are not charged. Retrying usually succeeds, because each try goes out through a different proxy IP.

**Why is `totalResults` only a few hundred?** It is Google's own count, and Google now shows small estimates for most searches. The number is passed on as Google gives it.

**Are the results the same as the old API?** The old API searched a "Programmable Search Engine" you set up. This actor returns google.com results, which are usually fuller. To limit results to one site, use `siteSearch`.

**What is in `pagemap`?** `pagemap.metatags` (title, description, author, publish date) is filled when `markdown=true`, because it needs the page itself.

**Why is the web address slow on the first call?** After a quiet spell Apify starts the actor again, which adds a few seconds. Apify bills compute while it stays awake, so for occasional batches normal runs are cheaper.

**Can AI agents use it through MCP?** Yes. Add the actor to [Apify's MCP server](https://mcp.apify.com) and agents can call it as a tool.

### Related Actors

- [Website to Markdown for LLMs & RAG](https://apify.com/s3nafps/ai-ready-webpage-extractor): turn whole websites into clean Markdown.
- [Google Trends Scraper: Bulk Compare 100+ Keywords](https://apify.com/s3nafps/google-trends-bulk-compare): compare hundreds of keywords on one Google Trends scale.
- [YouTube Transcripts for AI](https://apify.com/s3nafps/youtube-transcripts-for-ai): bulk YouTube transcripts from videos, channels and search as text, Markdown and RAG chunks.

Found a bug or need a parameter? Open an issue on the actor's Issues tab. If it saves you time, a review on the Store helps others find it.

# Actor input Schema

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

One search per line, like Google's q parameter. Each search gets one dataset row in Google's Custom Search JSON format. Advanced: in JSON you can pass objects such as {"q": "coffee", "num": 5, "gl": "de"} to set parameters per search.

## `num` (type: `integer`):

1 to 10, like Google's num parameter.

## `start` (type: `integer`):

Position of the first result, 1 to 91. Use 11 for the second page.

## `gl` (type: `string`):

Two-letter country code that boosts results from that country, such as us, gb, de or sa.

## `hl` (type: `string`):

Language code such as en, de or ar.

## `safe` (type: `string`):

Filter explicit results.

## `dateRestrict` (type: `string`):

Only results from the last N days, weeks, months or years: d5, w2, m6, y1.

## `siteSearch` (type: `string`):

Only return results from this site, for example example.com.

## `markdown` (type: `boolean`):

Add the clean text of each result page as Markdown, ready for AI models. Charged per page converted.

## `markdownMaxChars` (type: `integer`):

Characters kept per page when Markdown is on.

## Actor input object example

```json
{
  "queries": [
    "best coffee grinder 2026",
    "how to make cold brew"
  ],
  "num": 10,
  "safe": "off",
  "markdown": false,
  "markdownMaxChars": 20000
}
```

# 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": [
        "best coffee grinder 2026",
        "how to make cold brew"
    ]
};

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

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

```

## MCP server setup

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

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/LDJdxg8IEJg74GiAF/builds/saeTOmJhe9N3EkKW0/openapi.json
