<!-- twin of https://specstream.ai/google-custom-search-api-php -->
# Google Custom Search API in PHP: the call that still answers

Compared on Nov 10, 2026. The page: https://specstream.ai/google-custom-search-api-php

Google closed the Custom Search JSON API to new customers and ends it on January 1, 2027. A new PHP project gets no key, and code that calls `googleapis.com/customsearch/v1` stops answering on that day.

SpecStream answers the same request at `https://api.specstream.ai/customsearch/v1` in Google's JSON. In PHP you change the host in the URL and the key; `items`, `title`, `link` and `snippet` read as before, and any `cx` is accepted.

Searches cost $5 per 1,000 of up to 10 results, Google's own rate, with no 10,000 a day cap. SpecStream scores 97.9 F-score on all 1,000 SimpleQA Verified questions, on the [benchmarks](https://specstream.ai/benchmarks?utm_source=alternatives&utm_campaign=google-custom-search-api-php) page.

[Get an API key](https://specstream.ai/signup?src=alternatives-google-custom-search-api-php&utm_source=alternatives-md&utm_campaign=google-custom-search-api-php&returnTo=%2Fworkspace%2Fsearch%3Froute%3Dgoogle) New accounts get $10 of credit, no card. It covers 2,000 searches.

## Custom Search from PHP: SpecStream, Google and Vertex AI Search

| | SpecStream | Google Custom Search JSON API | Vertex AI Search |
|---|---|---|---|
| The URL PHP calls | `GET https://api.specstream.ai/customsearch/v1` with `key`, `q`, `num` and `start`. Any `cx` is accepted | `GET https://www.googleapis.com/customsearch/v1` with an API key, `q` and `cx`, the ID of a Programmable Search Engine you set up | `POST https://discoveryengine.googleapis.com/v1/{servingConfig}:search`, with its own request body and reply |
| New keys | Yes. A key takes a minute and comes with $10 of credit, no card | No. Closed to new customers, and discontinued on January 1, 2027 | Yes. A free trial covers 10,000 queries per account a month |
| What it searches | The whole live web, read at the moment of the call. `siteSearch` keeps a search to one site | The Programmable Search Engine that `cx` names | Up to 50 domains. For full web search, Google asks you to contact it |
| Price | $5 per 1,000 searches of up to 10 results, with no daily cap | $5 per 1,000 queries after 100 free a day, up to 10,000 a day, for existing customers | $1.50 per 1,000 queries on Search Standard Edition, $4.00 on Search Enterprise Edition |
| What json_decode returns | Google's JSON: `items` with `title`, `link`, `snippet`, `displayLink`, `htmlTitle`, `htmlSnippet` and `formattedUrl`, plus `searchInformation.totalResults` and `queries.nextPage` | An `items` array with the URL, title and text snippets of each result | Its own reply shape. Search Enterprise Edition adds generated answers at $4.00 per 1,000 queries |
| Results a call | `num` from 1 to 10, with `start` for the next page, as on Google | 1 to 10 a call, and never more than 100 for one query | Billed per query: every API call to Enterprise Search counts as one |
| A busy or failed call | `429` in Google's error form with `Retry-After`, and not charged | Its terms say you may be charged for each successful request | A request is any API call, made directly, through an integration or in the console |

## Why PHP code that called Custom Search moves to SpecStream

### The search with PHP's curl extension

One request, Google's JSON back. A search that finds nothing has no `items` key, on Google and here, so the loop reads `$data['items'] ?? []`.

```
<?php
$params = http_build_query([
    'key' => getenv('SPECSTREAM_API_KEY'),
    'cx'  => 'any',
    'q'   => 'open source vector databases',
    'num' => 10,
]);
// was https://www.googleapis.com/customsearch/v1
$ch = curl_init("https://api.specstream.ai/customsearch/v1?$params");
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$data = json_decode($body, true);
if ($status !== 200) {
    throw new RuntimeException($data['error']['message']);
}

echo $data['searchInformation']['totalResults'], " results\n";
foreach ($data['items'] ?? [] as $item) {
    echo $item['title'], "\n  ", $item['link'], "\n  ", $item['snippet'], "\n";
}
```

A refused call answers in Google's error object, with `code`, `message`, `errors` and `status`, so the error branch you already have stays as it is.

### Without the curl extension: file_get_contents

`ignore_errors` lets PHP read the JSON body of a `4xx` reply instead of returning `false`.

```
<?php
$url = 'https://api.specstream.ai/customsearch/v1?' . http_build_query([
    'key' => getenv('SPECSTREAM_API_KEY'),
    'cx'  => 'any',
    'q'   => 'open source vector databases',
]);
$context = stream_context_create(['http' => ['timeout' => 30, 'ignore_errors' => true]]);
$data = json_decode(file_get_contents($url, false, $context), true);

foreach ($data['items'] ?? [] as $item) {
    echo $item['title'], ' ', $item['link'], "\n";
}
```

### If the code uses google/apiclient

The client's `CustomSearchAPI` class takes the root address as the second argument of its constructor. Its source documents it as `@param string $rootUrl The root URL used for requests to the service.`, and the constructor sets both `rootUrl` and `rootUrlTemplate` from it. Here is the same call before and after, with that one option set and the key swapped.

```
// Before: Google
$client = new Google\Client();
$client->setDeveloperKey(getenv('GOOGLE_API_KEY'));
$service = new Google\Service\CustomSearchAPI($client);
$results = $service->cse->listCse(['q' => 'open source vector databases', 'cx' => $cx]);

// After: the same call, with the constructor's $rootUrl
$client = new Google\Client();
$client->setDeveloperKey(getenv('SPECSTREAM_API_KEY'));
$service = new Google\Service\CustomSearchAPI($client, 'https://api.specstream.ai/');
$results = $service->cse->listCse(['q' => 'open source vector databases', 'cx' => $cx]);
```

Tested on November 10, 2026 with google/apiclient 2.20.1: the client's own `listCse` call read 10 results from `https://api.specstream.ai/`. Pass the address as the constructor's second argument; setting `$service->rootUrl` after construction is not enough.

### Paging, and a busy call

`start` counts from 1. While more results exist, the reply carries `queries.nextPage[0].startIndex`. A busy call answers `429` with `Retry-After` in Google's error form and costs nothing, so `search()` waits that many seconds and sends it again. Each page is one search at $0.005.

```
<?php
function search(string $q, int $start): array
{
    $url = 'https://api.specstream.ai/customsearch/v1?' . http_build_query([
        'key' => getenv('SPECSTREAM_API_KEY'), 'cx' => 'any', 'q' => $q, 'num' => 10, 'start' => $start,
    ]);
    while (true) {
        $retryAfter = 1;
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_HEADERFUNCTION => function ($ch, $line) use (&$retryAfter) {
                if (stripos($line, 'Retry-After:') === 0) {
                    $retryAfter = (int) trim(substr($line, 12));
                }
                return strlen($line);
            },
        ]);
        $body = curl_exec($ch);
        $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        if ($status !== 429) {
            return json_decode($body, true);
        }
        sleep(max(1, $retryAfter));
    }
}

$start = 1;
for ($page = 0; $page < 3; $page++) {
    $data = search('open source vector databases', $start);
    foreach ($data['items'] ?? [] as $item) {
        echo $item['link'], "\n";
    }
    $next = $data['queries']['nextPage'][0]['startIndex'] ?? null;
    if ($next === null) {
        break;
    }
    $start = $next;
}
```

### The parameters the route honours

Nine parameters shape the search: `q`, `num`, `start`, `hq`, `exactTerms`, `orTerms`, `excludeTerms`, `siteSearch` and `siteSearchFilter`. `fileType`, `dateRestrict`, `lowRange` and `highRange` are accepted and not applied. Any `cx` is accepted, so the engine ID in your config can stay. Every parameter and field is in the [migration guide](https://specstream.ai/migrate/google-custom-search?utm_source=alternatives&utm_campaign=google-custom-search-api-php).

### $5 per 1,000 searches, no daily cap

A call of up to 10 results costs $0.005. Google charges the same $5 per 1,000 and stops at 10,000 queries a day; SpecStream has no daily cap. It scores 97.9 F-score on all 1,000 SimpleQA Verified questions (978 correct) and 64.0 item F1 and 43.4 row F1 on all 200 WideSearch tasks. On production on November 9, 2026, the median search took 0.82 s at 1 a second and 1.07 s at 20 a second.

### What a search leaves behind

With `zero_retention` on the key, a search keeps only the charge: account, key, request ID, price and time. The query text and the reply are not kept after the answer. Set it when you create the key, or later on the same key.

### The same key reads pages and returns rows

The [Extract API](https://specstream.ai/extract-api?utm_source=alternatives&utm_campaign=google-custom-search-api-php) reads up to 20 pages or documents a call at $0.80 per 1,000 pages and scores 0.908 F1 on WCXB. [Deep Search](https://specstream.ai/deep-search?utm_source=alternatives&utm_campaign=google-custom-search-api-php) turns a request in plain words into structured rows at 5¢, 15¢ or $1 a row, and quotes the most a run can cost before it starts.

## Other options for the Custom Search JSON API in PHP

### Vertex AI Search

Google's pick for search over up to 50 domains, from $1.50 per 1,000 queries on Search Standard Edition. It is a `POST` to `discoveryengine.googleapis.com` with a different body and reply, so the PHP that reads `items` is rewritten. SpecStream answers the `GET` your code sends today, across the whole web.

### Grounding with Google Search in the Gemini API

A Gemini model runs the search and writes an answer from it. On Gemini 3 and newer models it costs $14 per 1,000 search requests after 5,000 free a month, and PHP receives the model's text. SpecStream returns the result list itself at $5 per 1,000.

### Brave Search API

Brave's own index at $5 per 1,000 requests, with $5 in free credits every month. Brave's guide calls the move swapping the endpoint and updating how you read the response. On SpecStream the PHP that reads Google's response stays as written.

## How to run the PHP example

1. Create a key under Agents & API in the workspace. A new account gets $10 of credit, no card, which covers 2,000 searches.

2. Put the key in the environment and check the route from the shell.

```
export SPECSTREAM_API_KEY="your key"
curl "https://api.specstream.ai/customsearch/v1?key=$SPECSTREAM_API_KEY&cx=any&q=open+source+vector+databases&num=3"
```

3. Run the curl example above with `php search.php`. In code that already calls Google, replace `https://www.googleapis.com` or `https://customsearch.googleapis.com` with `https://api.specstream.ai` and the key with the SpecStream key. `cx` can stay.

4. With google/apiclient, pass `'https://api.specstream.ai/'` as the second argument of `new Google\Service\CustomSearchAPI(...)`, as shown above.

The same move in Python, with Google's own Python client, is on the [Python page](https://specstream.ai/google-custom-search-api-python?utm_source=alternatives&utm_campaign=google-custom-search-api-php).

When your product needs every match instead of a page of links, write the request in plain words. SpecStream reads it back as the conditions every row must meet and the facts to bring back, quotes the most it can cost, and delivers the rows after you approve.

## Frequently asked questions

### How do I call the Google Custom Search API from PHP?

Send a `GET` to `/customsearch/v1` with `key`, `cx` and `q` using curl or `file_get_contents`, then `json_decode` the reply and read `items`. Google closed the API to new customers and ends it on January 1, 2027. The same request answers at `https://api.specstream.ai/customsearch/v1` with a SpecStream key.

### Can I keep google/apiclient?

The client's documented setting for the address is `$rootUrl`, the second argument of the `CustomSearchAPI` constructor, which defaults to `https://customsearch.googleapis.com/`. Set it to `https://api.specstream.ai/` and use a SpecStream key with `setDeveloperKey`. Tested on November 10, 2026 with google/apiclient 2.20.1: the client's own `listCse` call read 10 results.

### When does the Custom Search JSON API stop working?

On January 1, 2027, and it is already closed to new customers. The [shutdown page](https://specstream.ai/google-custom-search-api-shutdown?utm_source=alternatives&utm_campaign=google-custom-search-api-php) has Google's own words and dates.

### How do I get more than 10 results in PHP?

Send the call again with `start` set to 11, then 21, or read `queries.nextPage[0].startIndex` from each reply. Each page of up to 10 results is one search at $0.005.

### Do I still need a search engine ID (cx)?

No. Any `cx` value is accepted, so a config that sends one runs unchanged. SpecStream searches the whole live web, and `siteSearch` keeps a search to one site.

### How much does the Custom Search API cost from PHP?

Google charges existing customers $5 per 1,000 queries after 100 free a day, up to 10,000 a day. SpecStream charges $5 per 1,000 searches of up to 10 results with no daily cap, and new accounts get $10 of credit, no card. Every rate is on the [pricing](https://specstream.ai/pricing?utm_source=alternatives&utm_campaign=google-custom-search-api-php) page.

### What happens when the API is busy?

It answers `429` in Google's error form with a `Retry-After` header, and the call is not charged. Wait that many seconds and send it again, as the paging example does.

## Sources

SpecStream wrote this page. Each figure about Google comes from Google's own pages, the client setting from the client's own source, and each other figure from that company's own page.

- [Google Custom Search JSON API overview](https://developers.google.com/custom-search/v1/overview), read Nov 9, 2026
- [Google Custom Search JSON API reference, cse.list](https://developers.google.com/custom-search/v1/reference/rest/v1/cse/list), read Nov 9, 2026
- [Google Custom Search JSON API, using REST](https://developers.google.com/custom-search/v1/using_rest), read Nov 9, 2026
- [Google Custom Search terms of service](https://developers.google.com/custom-search/terms), read Nov 9, 2026
- [Programmable Search Engine blog: updates to our web search products](https://programmablesearchengine.googleblog.com/2026/01/updates-to-our-web-search-products.html), read Nov 9, 2026
- [Google Cloud: Agent Search pricing](https://cloud.google.com/generative-ai-app-builder/pricing), read Nov 9, 2026
- [Google Cloud: servingConfigs.search method](https://cloud.google.com/generative-ai-app-builder/docs/reference/rest/v1/projects.locations.collections.engines.servingConfigs/search), read Nov 9, 2026
- [Gemini API: Grounding with Google Search](https://ai.google.dev/gemini-api/docs/google-search), read Nov 9, 2026
- [Brave Search API](https://brave.com/search/api/), read Nov 9, 2026
- [Brave, alternatives to the Google Search API](https://brave.com/learn/google-api-shutdown/), read Nov 9, 2026
- [google/apiclient services: CustomSearchAPI.php](https://github.com/googleapis/google-api-php-client-services/blob/main/src/CustomSearchAPI.php), read Nov 10, 2026
- [SpecStream benchmarks](https://specstream.ai/benchmarks), read Nov 10, 2026
- [SpecStream pricing](https://specstream.ai/pricing), read Nov 10, 2026
- [SpecStream for agents](https://specstream.ai/agents.md), read Nov 10, 2026
