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

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

The Custom Search JSON API is closed to new customers, and Google ends it on January 1, 2027. A Java service that calls `googleapis.com/customsearch/v1` gets no new key and no answer after that date.

SpecStream answers that request at `https://api.specstream.ai/customsearch/v1` and returns Google's JSON. In Java the change is the base URI and the key. `items`, `title`, `link` and `snippet` parse as they did, and any `cx` is accepted.

A search of up to 10 results costs $0.005, the $5 per 1,000 Google charges, with no 10,000 a day cap. SpecStream scores 97.9 F-score on all 1,000 SimpleQA Verified questions; see the [benchmarks](https://specstream.ai/benchmarks?utm_source=alternatives&utm_campaign=google-custom-search-api-java).

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

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

| | SpecStream | Google Custom Search JSON API | Vertex AI Search |
|---|---|---|---|
| The request Java sends | `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`, the ID of the Programmable Search Engine you configured | `POST https://discoveryengine.googleapis.com/v1/{servingConfig}:search`, a different request and reply |
| Open to new customers | Yes. Create a key in a minute and start with $10 of credit, no card | No. Closed to new customers; discontinued on January 1, 2027 | Yes, with a free trial of 10,000 queries per account a month |
| Search scope | The whole live web at the time of the call. `siteSearch` limits a search to one site | Only the Programmable Search Engine you configured | Up to 50 domains. Full web search means contacting Google |
| Price | $5 per 1,000 searches of up to 10 results. No daily cap | $5 per 1,000 queries after 100 free queries 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 the JSON holds | Google's shape: `items` with `title`, `link`, `snippet`, `displayLink`, `htmlTitle`, `htmlSnippet` and `formattedUrl`, `searchInformation.totalResults` and `queries.nextPage` | An `items` array carrying the URL, title and text snippets of each result | A different reply. Generated answers come with Search Enterprise Edition at $4.00 per 1,000 queries |
| Results a call | `num` from 1 to 10 and `start` to page, as on Google | 1 to 10 results a call, never more than 100 for a query | Billed per query: any API call to Enterprise Search counts |
| When a call fails | A busy call answers `429` with `Retry-After` in Google's error form and is not charged | Its terms say you may be charged for each successful request | Any API call counts as a request, direct or through an integration or the console |

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

### The search with java.net.http.HttpClient

The request uses the JDK's own `HttpClient` (Java 11 and newer); Jackson parses the reply. When a search finds nothing, Google and SpecStream both leave out `items`, and `path("items")` then iterates over nothing.

```
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;

public class CustomSearch {
    public static void main(String[] args) throws Exception {
        String base = "https://api.specstream.ai"; // was https://www.googleapis.com
        String q = URLEncoder.encode("open source vector databases", StandardCharsets.UTF_8);
        URI uri = URI.create(base + "/customsearch/v1?key=" + System.getenv("SPECSTREAM_API_KEY")
                + "&cx=any&num=10&q=" + q);

        HttpClient http = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder(uri).timeout(Duration.ofSeconds(30)).build();
        HttpResponse<String> response = http.send(request, HttpResponse.BodyHandlers.ofString());

        JsonNode data = new ObjectMapper().readTree(response.body());
        if (response.statusCode() != 200) {
            throw new IllegalStateException(data.path("error").path("message").asText());
        }
        System.out.println(data.path("searchInformation").path("totalResults").asText() + " results");
        for (JsonNode item : data.path("items")) {
            System.out.println(item.path("title").asText());
            System.out.println("  " + item.path("link").asText());
            System.out.println("  " + item.path("snippet").asText());
        }
    }
}
```

Errors keep Google's object, `error.code`, `error.message`, `error.errors` and `error.status`, so the exception path in your service reads the same fields.

### If the code uses google-api-services-customsearch

Google's generated Java client sets its address on the builder. In its source, `CustomSearchAPI.java` declares `DEFAULT_ROOT_URL = "https://customsearch.googleapis.com/"` and the builder overrides `public Builder setRootUrl(String rootUrl)`. The same call before and after, with that one option and the key changed:

```
// Before: Google
CustomSearchAPI service = new CustomSearchAPI.Builder(
        new NetHttpTransport(), GsonFactory.getDefaultInstance(), null)
    .setApplicationName("search")
    .build();
Search result = service.cse().list()
    .setQ("open source vector databases").setCx(cx)
    .setKey(System.getenv("GOOGLE_API_KEY"))
    .execute();

// After: the same call, with setRootUrl on the builder
CustomSearchAPI service = new CustomSearchAPI.Builder(
        new NetHttpTransport(), GsonFactory.getDefaultInstance(), null)
    .setApplicationName("search")
    .setRootUrl("https://api.specstream.ai/")
    .build();
Search result = service.cse().list()
    .setQ("open source vector databases").setCx(cx)
    .setKey(System.getenv("SPECSTREAM_API_KEY"))
    .execute();
```

Tested on November 10, 2026 with google-api-services-customsearch v1-rev20240821-2.0.0: the client's own `cse().list().execute()` read 10 results from `https://api.specstream.ai/`.

### Paging and a busy reply

`start` is 1-based. Each reply names the next page in `queries.nextPage[0].startIndex` while there is one. A busy call comes back `429` with a `Retry-After` header, in Google's error form, and is not charged; `search` sleeps for that many seconds and sends it again. Every page is one search at $0.005.

```
static JsonNode search(HttpClient http, String q, int start) throws Exception {
    URI uri = URI.create("https://api.specstream.ai/customsearch/v1?key=" + System.getenv("SPECSTREAM_API_KEY")
            + "&cx=any&num=10&start=" + start + "&q=" + URLEncoder.encode(q, StandardCharsets.UTF_8));
    HttpRequest request = HttpRequest.newBuilder(uri).timeout(Duration.ofSeconds(30)).build();
    while (true) {
        HttpResponse<String> response = http.send(request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() != 429) {
            return new ObjectMapper().readTree(response.body());
        }
        long wait = response.headers().firstValue("Retry-After").map(Long::parseLong).orElse(1L);
        Thread.sleep(Math.max(1L, wait) * 1000);
    }
}

// three pages, up to 30 results
HttpClient http = HttpClient.newHttpClient();
int start = 1;
for (int page = 0; page < 3; page++) {
    JsonNode data = search(http, "open source vector databases", start);
    for (JsonNode item : data.path("items")) {
        System.out.println(item.path("link").asText());
    }
    JsonNode next = data.path("queries").path("nextPage").path(0);
    if (next.isMissingNode()) {
        break;
    }
    start = next.path("startIndex").asInt();
}
```

### Parameters the route honours

Nine: `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 properties file can stay. The [migration guide](https://specstream.ai/migrate/google-custom-search?utm_source=alternatives&utm_campaign=google-custom-search-api-java) lists every parameter and field.

### Google's rate without the 10,000 a day cap

SpecStream charges $5 per 1,000 searches of up to 10 results, the rate Google charges, and has no daily cap where Google stops at 10,000 queries. Scores: 97.9 F-score on all 1,000 SimpleQA Verified questions (978 correct), 64.0 item F1 and 43.4 row F1 on all 200 WideSearch tasks. Measured on production on November 9, 2026: a median of 0.82 s at 1 search a second and 1.07 s at 20 a second.

### What is kept from a search

On a key with `zero_retention` set, nothing of the search stays after the reply except the charge's account, key, request ID, price and time. Turn it on when you make 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-java) reads up to 20 pages or documents a call at $0.80 per 1,000 pages, with 0.908 F1 on WCXB. [Deep Search](https://specstream.ai/deep-search?utm_source=alternatives&utm_campaign=google-custom-search-api-java) takes a request in plain words and returns every match as structured rows at 5¢, 15¢ or $1 a row, with the most a run can cost quoted before it starts.

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

### Vertex AI Search

Google points Custom Search users here for up to 50 domains, at $1.50 per 1,000 queries on Search Standard Edition. The call is a `POST` to `discoveryengine.googleapis.com` with its own request and reply, so the Java that parses `items` is rewritten. SpecStream answers the request your code already builds, on the whole web.

### Grounding with Google Search in the Gemini API

Gemini searches and writes an answer grounded in what it found, at $14 per 1,000 search requests on Gemini 3 and newer after 5,000 free a month. Your service receives the model's answer. SpecStream returns `items` to your code at $5 per 1,000.

### Brave Search API

Search on Brave's own index for $5 per 1,000 requests, with $5 in free credits every month. Brave's guide says migrating is swapping the endpoint and updating how you read the response. With SpecStream the Java that reads Google's response is kept.

## How to run the Java example

1. Create a key under Agents & API in the workspace. New accounts get $10 of credit, no card, enough for 2,000 searches.

2. Set the key in the environment and try the route 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. Add Jackson (`com.fasterxml.jackson.core:jackson-databind`) to the build and run `CustomSearch` above. In code that already calls Google, change `https://www.googleapis.com` or `https://customsearch.googleapis.com` to `https://api.specstream.ai` and use the SpecStream key. Leave `cx` in place.

4. With `google-api-services-customsearch`, add `.setRootUrl("https://api.specstream.ai/")` to the builder, as shown above.

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

When the job is every match rather than a results page, write the request in plain words. SpecStream reads it back as the conditions each row must meet and the facts to return, quotes the most it can cost, and delivers the rows once you approve.

## Frequently asked questions

### How do I use the Google Custom Search API in Java?

Send `GET /customsearch/v1` with `key`, `cx` and `q` through `java.net.http.HttpClient` and read `items` from the JSON. 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.

### What is the customsearch Java client's setting for the address?

`Builder.setRootUrl`, which replaces `DEFAULT_ROOT_URL`, `https://customsearch.googleapis.com/`. Set it to `https://api.specstream.ai/` and pass a SpecStream key to `setKey`. Tested on November 10, 2026 with google-api-services-customsearch v1-rev20240821-2.0.0: the client's own `cse().list().execute()` read 10 results.

### When does Google shut down the Custom Search JSON API?

On January 1, 2027. 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-java) has Google's words and dates.

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

Ask again with `start` at 11, then 21, or take `queries.nextPage[0].startIndex` from each reply. Every page of up to 10 results is one search at $0.005.

### Do I need a Programmable Search Engine ID (cx)?

No. Any `cx` value is accepted, so a properties file that sets one stays as it is. SpecStream searches the whole live web; `siteSearch` keeps a search to one site.

### What does the Custom Search JSON API cost?

$5 per 1,000 queries after 100 free a day, capped at 10,000 a day, for existing customers until January 1, 2027. SpecStream costs $5 per 1,000 searches with no daily cap. Every rate is on the [pricing](https://specstream.ai/pricing?utm_source=alternatives&utm_campaign=google-custom-search-api-java) page.

### How should Java handle a 429 from the route?

Read the `Retry-After` header, wait that many seconds and send the call again. The `429` comes in Google's error form and is not charged.

## 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-api-services-customsearch: CustomSearchAPI.java](https://github.com/googleapis/google-api-java-client-services/blob/main/clients/google-api-services-customsearch/v1/2.0.0/com/google/api/services/customsearch/v1/CustomSearchAPI.java), 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
