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

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

Google ends the Custom Search JSON API on January 1, 2027 and no longer takes new customers. Ruby code that calls `googleapis.com/customsearch/v1` cannot get a new key, and its calls stop on that date.

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

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

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

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

| | SpecStream | Google Custom Search JSON API | Vertex AI Search |
|---|---|---|---|
| The URI Ruby 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 `q`, an API key and `cx`, your Programmable Search Engine ID | `POST https://discoveryengine.googleapis.com/v1/{servingConfig}:search`, with a different request and reply |
| Takes new customers | Yes. A key is ready in a minute with $10 of credit, no card | No. Closed to new customers, discontinued on January 1, 2027 | Yes. Its free trial gives 10,000 queries per account a month |
| What it searches | The whole live web, read when the call arrives. `siteSearch` keeps a search to one site | The Programmable Search Engine you configured | Up to 50 domains. Google asks full web search users to contact it |
| Price | $5 per 1,000 searches of up to 10 results, no daily cap | $5 per 1,000 queries after 100 free a day, at most 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.parse gives you | Google's JSON: `items` with `title`, `link`, `snippet`, `displayLink`, `htmlTitle`, `htmlSnippet` and `formattedUrl`, `searchInformation.totalResults` and `queries.nextPage` | `items`, each with the URL, title and text snippets of a result | Its own reply. Search Enterprise Edition includes generated answers at $4.00 per 1,000 queries |
| Results a call | `num` from 1 to 10 and `start` for later pages, as on Google | 1 to 10 a call, never more than 100 for a query | Billed per query: each API call to Enterprise Search is one |
| A busy or failed call | `429` with `Retry-After` in Google's error form, not charged | Its terms say you may be charged for each successful request | Every API call is a request, direct or through an integration or the console |

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

### The search with Net::HTTP

Standard library only: `net/http` and `json`. A search with no results has no `items` key on Google or here, so `fetch("items", [])` keeps the loop safe.

```
require "net/http"
require "json"

uri = URI("https://api.specstream.ai/customsearch/v1") # was https://www.googleapis.com
uri.query = URI.encode_www_form(
  key: ENV.fetch("SPECSTREAM_API_KEY"),
  cx: "any",
  q: "open source vector databases",
  num: 10
)

response = Net::HTTP.get_response(uri)
data = JSON.parse(response.body)
raise data.dig("error", "message") unless response.is_a?(Net::HTTPSuccess)

puts "#{data.dig("searchInformation", "totalResults")} results"
data.fetch("items", []).each do |item|
  puts item["title"], "  #{item["link"]}", "  #{item["snippet"]}"
end
```

An error comes back as Google's `error` object with `code`, `message`, `errors` and `status`, so a `rescue` that reads the message needs no change.

### If the code uses google-apis-customsearch_v1

Google's Ruby gem starts from `DEFAULT_ENDPOINT_TEMPLATE = "https://customsearch.$UNIVERSE_DOMAIN$/"` and exposes the address as a writer on the service, `def root_url= url_or_template` in `google-apis-core`. Before and after, the same call with that one option set and the key changed:

```
require "google/apis/customsearch_v1"

# Before: Google
service = Google::Apis::CustomsearchV1::CustomSearchAPIService.new
service.key = ENV["GOOGLE_API_KEY"]
result = service.list_cses(q: "open source vector databases", cx: cx)

# After: the same call, with root_url set
service = Google::Apis::CustomsearchV1::CustomSearchAPIService.new
service.root_url = "https://api.specstream.ai/"
service.key = ENV["SPECSTREAM_API_KEY"]
result = service.list_cses(q: "open source vector databases", cx: cx)
```

Tested on November 10, 2026 with google-apis-customsearch_v1 0.25.0: the gem's own `list_cses` read 10 results from `https://api.specstream.ai/`.

### Paging and the 429 reply

`start` begins at 1. While more results exist, each reply holds `queries.nextPage[0].startIndex`. When search is busy the route answers `429` with `Retry-After` in Google's error form and charges nothing; `search` sleeps that long and asks again. Each page is one search at $0.005.

```
require "net/http"
require "json"

def search(q, start)
  uri = URI("https://api.specstream.ai/customsearch/v1")
  uri.query = URI.encode_www_form(key: ENV.fetch("SPECSTREAM_API_KEY"), cx: "any", q: q, num: 10, start: start)
  loop do
    response = Net::HTTP.get_response(uri)
    return JSON.parse(response.body) unless response.code == "429"
    sleep [response["Retry-After"].to_i, 1].max
  end
end

start = 1
3.times do
  data = search("open source vector databases", start)
  data.fetch("items", []).each { |item| puts item["link"] }
  next_page = data.dig("queries", "nextPage", 0)
  break unless next_page
  start = next_page["startIndex"]
end
```

### Parameters the route honours

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

### $5 per 1,000 and the top scores published

A search of up to 10 results is $0.005, the same $5 per 1,000 as Google, and there is no cap where Google stops at 10,000 queries a day. SpecStream 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 was 0.82 s at 1 search a second and 1.07 s at 20 a second.

### What is kept from a search

A key with `zero_retention` keeps no query text and no reply once the answer is sent. Only the charge stays: account, key, request ID, price and time. Set it when the key is created or later.

### 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-ruby) 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-ruby) returns every match for a plain request as 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 Ruby

### Vertex AI Search

Google's named alternative for up to 50 domains, at $1.50 per 1,000 queries on Search Standard Edition. It takes a `POST` to `discoveryengine.googleapis.com` with a body and reply of its own, so the Ruby that reads `items` is rewritten. SpecStream answers the `GET` your code sends, across the whole web.

### Grounding with Google Search in the Gemini API

A Gemini model searches, then writes an answer grounded in the results. On Gemini 3 and newer it costs $14 per 1,000 search requests once 5,000 free a month are used, and Ruby receives the answer. SpecStream hands your code the `items` list 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. In Brave's words, migrating is mostly swapping the endpoint and updating how you read the response. With SpecStream the Ruby that reads Google's response is unchanged.

## How to run the Ruby example

1. Create a key under Agents & API in the workspace. New accounts start with $10 of credit, no card, which covers 2,000 searches.

2. Export the key and call the route once from a 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. Save the Net::HTTP example as `search.rb` and run `ruby search.rb`. 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` stays.

4. With `google-apis-customsearch_v1`, add `service.root_url = "https://api.specstream.ai/"` before the first call, as shown above.

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

When you need every match instead of ten links, write the request in plain words. SpecStream reads it back as the conditions each 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 Ruby?

Send `GET /customsearch/v1` with `key`, `cx` and `q` through `Net::HTTP`, parse the body with `JSON.parse` 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.

### Which Ruby gem is the Google Custom Search client?

`google-apis-customsearch_v1`, whose `CustomSearchAPIService#list_cses` sends the call. Its documented address setting is `service.root_url=`; set it to `https://api.specstream.ai/` with a SpecStream key in `service.key`. Tested on November 10, 2026 with google-apis-customsearch_v1 0.25.0: the gem's own `list_cses` read 10 results.

### When is the Custom Search JSON API shut down?

On January 1, 2027, and new customers are already refused. The [shutdown page](https://specstream.ai/google-custom-search-api-shutdown?utm_source=alternatives&utm_campaign=google-custom-search-api-ruby) quotes Google's own dates.

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

Send `start: 11`, then `start: 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 the cx search engine ID?

No. Any `cx` is accepted, so credentials that hold one keep working unchanged. SpecStream searches the whole live web, and `siteSearch` keeps a search to one site.

### How much does the Google Custom Search API cost?

$5 per 1,000 queries after 100 free a day, up to 10,000 a day, for existing customers. 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-ruby) page.

### What should Ruby do with a 429?

Sleep for the seconds in `Retry-After` and send the call again; the `429` is in Google's error form and is not charged. The paging example above does this.

## Sources

SpecStream wrote this page. Each figure about Google comes from Google's own pages, the gem's setting from the gem'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-apis-customsearch_v1: service.rb](https://github.com/googleapis/google-api-ruby-client/blob/main/generated/google-apis-customsearch_v1/lib/google/apis/customsearch_v1/service.rb), read Nov 10, 2026
- [google-apis-core: base_service.rb](https://github.com/googleapis/google-api-ruby-client/blob/main/google-apis-core/lib/google/apis/core/base_service.rb), 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
