Rebounder Tech Blog

Written by the people who actually run these systems in production.

Google Custom Search API 403: Switched to Brave Search API

Published About 5 min readBy the Rebounder engineering team — the people who operate these systems

This article may contain affiliate links. Its content is not affected by advertising.

In short

Search-rank checks skip WebSearch because it drifts from Japanese results via a US region, and Google Custom Search JSON API kept returning 403, so Brave Search API is the default instead.

Conclusion first

This blog (tech.rebounder.jp) uses Brave Search API, not Google Custom Search JSON API, to check search rank when deciding whether an SEO angle can win. The reason is simple: Google was tried first, and it didn’t work. The WebSearch tool isn’t used either — it routes through a US region and drifts from Japanese search results. In past operation this caused a real false report of “outside the top results” for a target keyword, when a proper re-check later showed 4 out of 5 pages were actually ranked #1.

Why the WebSearch tool doesn’t work here

This blog is written by scheduled, unattended runs with no screen. There’s no human opening google.co.jp in Chrome to confirm anything, so checking rank has to go through an API. The first obvious candidate, the WebSearch tool, was rejected because it routes through a US region, so results drift from Japan-targeted, Japanese-language search results.

That drift isn’t a minor rounding error. In past operation (writing on note), one angle was judged “outside the top results” for its target keyword and dropped. Re-measuring later from a Japanese IP on google.co.jp showed 4 out of 5 pages were actually ranked #1. Far from being outside the results, an angle that was nearly a monopoly had been discarded purely because of regional drift. Misjudging rank is a double loss: winnable angles get dropped, and time gets spent on angles that were never going to win.

Google Custom Search JSON API didn’t work either

With WebSearch ruled out, the obvious next try is Google’s own API. But even with GOOGLE_CSE_KEY and GOOGLE_CSE_ID (cx) set up correctly, it kept returning 403 This project does not have the access to Custom Search JSON API.

Suspecting a misconfiguration, four things were checked.

Checked Result
API enabled Yes. Dashboard logged 20 requests at 100% error rate — requests were reaching Google
Key Valid (removing cx returns 400, a different error from a broken key)
cx Recognized (a bogus cx returns 400; only a real one returns 403)
Billing Linked
Key’s API restriction A required field that can’t be removed — ruled out as the cause

A freshly created project returned the same 403. Having ruled out this much, the only remaining explanation is that the account, not a missing local setting, is what’s restricting JSON API access for new projects and new search engines. This wasn’t the kind of failure that a configuration fix resolves.

Choosing Brave Search API — and what was traded away

Brave Search API was adopted instead. It’s called with country=jp, search_lang=jp, and ui_lang=ja-JP to get Japan-targeted, Japanese-language results. A $5 free credit is granted every month, and at $5 per 1,000 requests that works out to a permanent 1,000 free requests/month. Actual usage runs about 2.3 queries per article at 3–4 articles/day, comfortably inside that budget.

There is a real trade-off, though: what comes back is Brave’s own index, not Google’s ranking. What these checks actually need to know is whether a keyword’s space is already covered by major Japanese tech blogs like Qiita, Zenn, or Classmethod — a question of what articles exist — and that holds up reasonably well regardless of which index is behind it. What’s lost is Google’s exact fine-grained rank and whether an AI Overview (a generative summary box) shows up. For the handful of angles Brave’s results alone can’t settle, a second check against real Google results is run through Serper.dev (whose free tier is a one-time 96 requests at signup, not a recurring allowance).

A self-imposed guardrail against surprise billing

Brave’s free credit also has a ceiling, and going over it doesn’t produce a bill — the API just stops responding, which for a scheduled run means silent failure. To avoid that, a SOFT_LIMIT (800 requests) and a HARD_LIMIT (950 requests) are enforced locally, stopping requests before Brave’s own 1,000-request cap is reached.

The other constraint is that Brave’s terms of service prohibit storing responses. That rules out caching, so only the query text and timestamp are logged — never the response itself.

The waste of re-checking the same angle repeatedly

That log turned out to matter in practice. On one day, a single angle around PGRST201 was checked seven times across four separate runs. The runbook said in writing that an angle judged a loss should be recorded and never checked again, but nothing had actually been recorded in the backlog — so every run rediscovered the same angle from scratch, checked it, judged it a loss, and discarded it without recording anything. Of that day’s 84 queries, 7 were the same angle, and the angle behind an already-published article was checked 3 more times.

A rule written down in a runbook gets broken. The log had the full history the whole time; what was missing was a way for the checking step to actually look at it. Since then, before sending a query, it’s checked against the existing log and a warning is raised on overlap. It doesn’t block — a keyword can legitimately overlap between two different angles by coincidence — so the call is left to a human.

Summary

Even something as seemingly simple as checking search rank ended up choosing “what actually worked,” not “what looked simplest.” WebSearch was rejected for regional drift, Google’s own official API was rejected for a persistent 403, and Brave is what was left standing by elimination. Hitting the same kind of wall is worth treating as a possible account-level restriction early, rather than assuming it’s a local misconfiguration — that assumption is what costs the most time to unwind.

Frequently asked questions

Q1Why can't the WebSearch tool be used to check search rank?

It routes through a US region, so results drift from Japan-targeted, Japanese results. This once caused a false report of "outside the top results" for a keyword; re-checking on google.co.jp from Japan showed 4 of 5 pages were actually ranked #1.

Q2Can the Google Custom Search JSON API 403 be fixed by correcting a misconfiguration?

Not in this case. API enablement, key, cx, and billing link were all confirmed correct. The dashboard logged 20 requests at a 100% error rate, meaning requests reached Google and were rejected there. A brand-new project returned the same 403, pointing to an account-level restriction.

Q3Does Brave Search API return the same results as Google?

No, it's Brave's own independent index. What these checks need is whether a keyword is already saturated by major Japanese tech blogs like Qiita or Zenn, which holds up across a different index. What's lost is Google's exact ranking, so a few angles get a second check via Serper.

Q4Is there a risk of being billed after using up the free tier?

Brave gives 1,000 requests/month free, but going over doesn't bill — the API just stops, noticed only after the fact. A self-imposed SOFT_LIMIT (800) and HARD_LIMIT (950) stop requests before Brave's own cap. Responses can't be cached under Brave's terms, so only queries are logged.

Environment verified

  • Astro ^7.2.2 / tsx ^4.23.12 (tech-rebounder, scheduled runs with no screen)
  • Google Custom Search JSON API 403 confirmed on 2026-08-16; Brave Search API set as default since

What this article is based on

  • TypeScript file lines 1-20commit e3b2ac5
  • TypeScript file lines 55-68commit e3b2ac5
  • TypeScript file lines 103-117commit e3b2ac5
  • TypeScript file lines 170-200commit e3b2ac5
  • Markdown file lines 218-242commit 2ec76dd

Every claim in this article comes from the records above. The repositories we operate are private so we cannot link to them, but which file, which lines, and at which commit we read them is recorded for every article. Nothing here is written from guesswork.