Google Custom Search API in Ruby
The call that still answers.
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 page shows.
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"]}"
endAn 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"]
endParameters 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.
$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 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 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 SpecStream scores
The same numbers as the benchmarks page, from the official tasks and scorers.
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 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
Something we didn't answer? Talk to a human
How do I call the Google Custom Search API from Ruby?
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?
How do I get more than 10 results in Ruby?
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?
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?
What should Ruby do with a 429?
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 overviewdevelopers.google.com/custom-search/v1/overview, read Nov 9, 2026
- Google Custom Search JSON API reference, cse.listdevelopers.google.com/custom-search/v1/reference/rest/v1/cse/list, read Nov 9, 2026
- Google Custom Search JSON API, using RESTdevelopers.google.com/custom-search/v1/using_rest, read Nov 9, 2026
- Google Custom Search terms of servicedevelopers.google.com/custom-search/terms, read Nov 9, 2026
- Programmable Search Engine blog: updates to our web search productsprogrammablesearchengine.googleblog.com/2026/01/updates-to-our-web-search-products.html, read Nov 9, 2026
- Google Cloud: Agent Search pricingcloud.google.com/generative-ai-app-builder/pricing, read Nov 9, 2026
- Google Cloud: servingConfigs.search methodcloud.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 Searchai.google.dev/gemini-api/docs/google-search, read Nov 9, 2026
- Brave Search APIbrave.com/search/api/, read Nov 9, 2026
- Brave, alternatives to the Google Search APIbrave.com/learn/google-api-shutdown/, read Nov 9, 2026
- google-apis-customsearch_v1: service.rbgithub.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.rbgithub.com/googleapis/google-api-ruby-client/blob/main/google-apis-core/lib/google/apis/core/base_service.rb, read Nov 10, 2026
- SpecStream benchmarksspecstream.ai/benchmarks, read Nov 10, 2026
- SpecStream pricingspecstream.ai/pricing, read Nov 10, 2026
- SpecStream for agentsspecstream.ai/agents.md, read Nov 10, 2026




