Introduction#
Since late June 2026, a Google search result no longer tells you where it links.
Until this summer a Google result link carried its destination in the open, in the href itself or in the old /url?q=https://… redirect. Now Google swaps the address for an encrypted token, google.com/goto?url=CAES…, that only Google can turn back into a URL. Click it and Google forwards you. Read it and you get nothing.
This isn’t the first time Google has tried blocking scrapers. Back in 2025, Google started refusing searches that had JavaScript disabled. /goto is different: the search goes through, but every URL on the page has to be handed back to Google before it means anything.
Google started rolling out /goto redirects in late June, and the people who measure search results felt it before Google said a word. On 26 August the change went worldwide, with /goto on close to 100% of the result pages we fetched, and Google confirmed it, citing “technical measures against evolving forms of abuse”. Within days Ahrefs had warned its customers of a ranking-data disruption, and SISTRIX told its readers that every resolved URL now costs an extra request, that tracking “becomes somewhat more complex and more expensive”, and that it expects “further measures of this kind”.
Every SERP API, rank tracker and AI agent now sits behind that resolver. We ran the same queries through 11 SERP APIs on Google, Google Light, Google News and Google Lens and measured how many links came back as tokens.

What is a google.com/goto redirect#
A /goto link is a Google-hosted redirect, funneling the client through Google’s servers to retrieve the full URL. This serves as an anti-bot and anti-scraping method. The anchor now points at https://www.google.com/goto?url=<token>. The token carries the destination URL, encrypted under a key only Google holds. Following the link is a GET to google.com/goto, which decrypts the token and answers 302 Location: <destination>.
Google serves /goto per request and has not said to whom. Asked about the rollout, a Google spokesperson told Search Engine Roundtable only: “We have a long history of deploying technical measures against evolving forms of abuse, and we regularly take steps to protect our services and users.” Automated clients get it: Google served a wrapped page to every SERP API we tested. Ordinary browsers are mixed. A logged-in session still, mostly, gets plain links, but Search Engine Roundtable saw /goto in several browsers, and ScrapingBee got it by hand in Chrome and Edge while Brave and LibreWolf got plain links.
Why /goto is a problem#
The old /url? links carried the destination in plaintext, so anyone could read it. Whereas /goto URLs require Google to resolve them first.
Every SERP API, rank tracker, and AI agent that reads search results now depends on Google’s resolver being up. If that service goes down, every token in flight stops resolving.
0 links resolved · 0 GETs to Google
/goto links
google.com/goto?
one GET per token
Google /goto resolver
decrypts the token with Google's key
302 + Location
resolved links
https://
The token itself is also a verdict. Google decides per request who gets one, and every SERP API we tested landed on the token side of that line. That alone is a little scary, and it is only one step away from throttling or pricing the resolver for everyone on that side. Every resolution also names the result being followed, so Google gets a live view of automated search traffic: which queries, which results, at what volume, across the whole API and AI ecosystem.
That step may already be under way. On 1 September Search Engine Roundtable reported a new “Verifying your request” interstitial on logged-out and private sessions: a Continue button that stays grey for a few seconds until Google has decided you are human.
Anatomy of a /goto token#
Let’s start with how Google builds the token. The goto URL carries one encoded string, the token. Take this example, which points at searchapi.io:
https://www.google.com/goto?url=CAESUQHrOzAVnRziCxV9rxOarqrlt8nP9qfbxGq-zqC6baiBdXoHh2v-ZE3J-HweeNLhoSRT-gHkQAVAdb31hegzZdL-x-eqEosk6Fzgs92fB6-xHQ
The token is everything after ?url=: 114 characters of base64url, which decode to 85 bytes. Researchers at Scraping.club analyzed 183 goto URLs and found that the token consists of a protobuf envelope, five constant bytes, and noise after that. Reading that noise as an IV, a ciphertext and a tag is a hypothesis: the bytes are consistent with it but do not prove it.
Token · 114 base64url characters, 85 bytes once decoded
CAESUQHrOzAVnRziCxV9rxOarqrlt8nP9qfbxGq-zqC6baiBdXoHh2v-ZE3J-HweeNLhoSRT-gHkQAVAdb31hegzZdL-x-eqEosk6Fzgs92fB6-xHQ
Protobuf envelope · 4 bytes
08 01 sets field 1 to 1, 12 opens field 2 as length-delimited bytes, and 51 (81) is its length: the rest of the token. Base64 writes 08 01 12 as CAES, which is why every token starts with it.
Tink key prefix · 5 bytes
01 is Tink's TINK prefix type, then the 4-byte key id EB 3B 30 15. It is the same on every token; the day it changes, Google has rotated its key.
IV · 12 bytes
A fresh random AES-GCM nonce for every token, which is why the same link gets a different token on every render.
Encrypted URL · 48 bytes
GCM keeps the plaintext's length. https://www.searchapi.io/ is 25 of these bytes; what the other 23 hold is a guess.
Authentication tag · 16 bytes
A keyed checksum only Google's key can produce. Google checks it before it reads anything, which is what Flip a bit shows.
Step 1 of 5
Google answers 302 with Location: https://www.searchapi.io/
Google answers 400 with no Location: with a bit changed, the tag no longer matches.
Every token starts with CAES. Base64 turns three bytes into four characters, and CAES is what you get from exactly 08 01 12. Read as protobuf, 08 is field 1 with wire type 0 (a varint) and the next byte, 01, is its value. Then 12 is field 2 with wire type 2 (length-delimited bytes).
That is the plainest message there is, {1: 1, 2: <bytes>}: a small integer and a payload. Google has been shipping a protobuf on every result link for years: the ved= parameter on the old /url? links is one, and YouTube’s search filters are another.
Filter a search to videos and the URL gains sp=EgIQAQ%3D%3D, which is 12 02 10 01: a protobuf {2: {2: 1}}. CAES is the same habit. The length byte is right. A length-delimited field carries its own size, and in our example the byte after 12 is 0x51, which is 81. The decoded token is 85 bytes: 4 of envelope, 81 of payload, ending exactly on the last byte of the buffer. A byte that counts the bytes after it is a length field.
The next five bytes never change. The payload opens with 01 EB 3B 30 15 everywhere. The layout hints at Tink, Google’s own crypto library, which stamps exactly this 5-byte output prefix on every ciphertext: 0x01 for its TINK prefix type, then the big-endian key id.
One key id everywhere means one primary key in one server-side keyset, and the prefix doubles as a free rotation detector: the day those four bytes change, Google has rolled the key.
Twelve bytes that are different every time. Tink’s default AEAD is AES-GCM, and it writes a fresh random 12-byte IV at the front of every ciphertext it produces. That randomness is why the same link gets a different token on every render.
Forty-eight bytes that are the URL, encrypted. GCM is a stream mode, so ciphertext is the same length as plaintext: 81 bytes of payload minus 5 of prefix, 12 of IV and 16 of tag leaves 48, and the destination, https://www.searchapi.io/, is 25 of them. The other 23 ride along encrypted. What they hold is a guess: a timestamp, a result position, a click id. How many there are is measurable, and that is the test below.
Sixteen bytes of authentication tag. The payload ends with C7 E7 AA … B1 1D, the GCM authentication tag: in effect a keyed checksum over the token, one only Google’s key can produce, and Google checks it before it reads anything. Flip a single bit of the payload and the endpoint answers HTTP 400 with no Location (we did, on the token above), which is also what stops /goto from working as an open redirect.
Resolving is the only “supported” operation#
The supported way to learn a destination is to ask Google, and that is by design. A GET with redirects unfollowed hands back the answer in a header:
curl -sS -o /dev/null -w '%{http_code} %{redirect_url}\n' \
'https://www.google.com/goto?url=CAESUQHrOzAVnRziCxV9rxOarqrlt8nP9qfbxGq-zqC6baiBdXoHh2v-ZE3J-HweeNLhoSRT-gHkQAVAdb31hegzZdL-x-eqEosk6Fzgs92fB6-xHQ'
302 https://www.searchapi.io/
It has to be a GET: a HEAD to the same URL answers 200 with no Location.
And asking is deliberately easy. No cookies, no Referer, no JavaScript: plain curl from one office IP resolved every token we tried, 302 with a Location every time. The token is not tied to a front door either: the same token returns the same Location on google.com, google.co.uk and google.de.
Tokens keep, too: twenty we captured on 31 August all still resolved on 2 October. They also repeat within a page, but never across pages: 340 of the 607 pages with tokens we captured had at least one token twice, and no token showed up on two pages. So deduplicate per page before resolving, because every unique token is still a round trip, about 160 ms each from our office on a fresh connection.
Testing SERP APIs against /goto#
Every SERP API should receive /goto tokens from Google. What the customer gets depends on what the provider does with them. We tested 11 SERP API providers on Google, Google Light, Google News, and Google Lens to see how they handle /goto resolving. For every call we keep the JSON response as returned, and, where the provider exposes it, the raw Google HTML behind it. Providers hand over that HTML in one of two ways: in the same request, as the page the JSON was parsed from, or through a second request to the provider’s API. All providers run at once, one thread pool each, so all queries are measured in one time window.
Dataset#
The dataset we used for this analysis is 20 queries for Google and Google Light, 20 for Google News and 20 for Google Lens. The 20 web queries run on both Google and Google Light and cover plain organic, question, local, news, video, shopping and navigational intent. The 20 news queries are the kind that runs on a news tab: companies and products, markets, sports, science and health, entertainment, and two boolean strings with OR and quoted phrases. Google Lens takes an image URL where the others take search terms, so its 20 queries are product photos on the retailer and marketplace CDNs.
Every query goes to every provider measured on its engine with the same parameters: gl=us, hl=en, device=desktop. A provider that calls this parameter differently is mapped accordingly. A provider whose default page size is not Google’s ten gets it pinned to ten. Where the API has a resolver switch it is on: SearchApi’s link=resolved on every surface, Nimble’s resolve_url=true.
Method#
Every link is either wrapped (a /goto link) or clean. A goto token is easy to spot: a base64url string of 100-plus characters that starts with CAES, and nothing else in a SERP payload looks like that. So wrapped is unambiguous wherever it appears. Clean only means an absolute URL off Google in a field that is supposed to hold a destination.
JSON: read the field the customer reads. Providers do not share the same schema, but every API documents which field holds a result’s URL, link for some, url for others. A result is any object in a list that carries the field, and its class is the class of the field’s value, or empty if the field is blank. A result whose destination is clean but which keeps the original /goto in a second field (original_link, redirect_link) counts as clean. The provider is showing its work, and the rate does not punish it.
HTML: read every anchor. Every <a href> in the raw page is classified by the same rule. Anchors that stay on Google (search, maps, pagination, the footer) drop out, and what is left points either at the destination or at /goto. Only anchors count, not <link href>, which points at Google’s asset hosts.
The metric: goto rate
To objectively measure the impact of /goto on each provider, we decided to report a single number, the goto rate, taken in two places: in the JSON the customer receives, and in the raw HTML Google served the provider.
In the JSON, the unit is the result. Results whose destination field stays on Google are not destinations and are left out, the rest are wrapped, clean or empty. goto rate = wrapped results ÷ results
In the HTML, the unit is the anchor that leaves Google: wrapped if it is /goto or carries the token inside the old /url? wrapper (q=CAES… or url=CAES…), clean if it points straight at the destination. goto rate (HTML) = wrapped anchors ÷ anchors leaving Google
The JSON rate is what the customer gets. On its own it cannot say whether a provider resolves anything. A JSON rate of 0% has two very different causes: the provider followed every token to its destination, or Google never sent a token in the first place. The HTML rate is what tells them apart, because it is the page before the parser touched it. So every cell in the results is really a pair, written JSON over HTML, and the pair reads like this:

What the rate does not measure: correctness. A clean link is any absolute URL off Google in the destination field. We do not check that it is the right URL. A provider could score 0% by resolving to a wrong page, by rebuilding the link from the displayed breadcrumb, or by serving a stale destination, and this metric would not notice.
Results#
Every number below comes from one run on 7 September 2026.
Google serves a wrapped page to every provider. Every link leaving Google on it is a /goto token. The provider, not Google, decides what ends up in your JSON. Six hand back clean links on every surface they cover. The other five resolve the organic results and leave the rest as tokens, and on Google News and Google Lens several of them resolve nothing at all.
In the JSON: share of results the customer gets that are /goto#
Three patterns in this table. Six providers are clean, or within a few points of it, on every surface they cover: SearchApi, SerpApi, HasData, Traject Data and Serper at 0%, Nimble at 4% to 5%. Four are wrapped only sometimes, from Scrapingdog’s 11% to Oxylabs’ and ScrapingBee’s 34%, with Bright Data in between. And the rate only reaches 100% in the News and Lens columns: Oxylabs on both, Bright Data on News. DataForSEO is the odd one out, clean on Google and News and 85% on Lens, with Bright Data’s 87% on Lens beside it.
| provider | Google Light | Google News | Google Lens (visual matches) | |
|---|---|---|---|---|
| SearchApi | 0% | 0% | 0% | 0% |
| SerpApi | 0% | 0% | 0% | 0% |
| HasData | 0% | 0% | 0% | n/a |
| Scrapingdog | 2% | 11% | 0% | 0% |
| ScrapingBee | 33% | 34% | n/a | n/a |
| DataForSEO | n/a | 0% | 0% | 85% |
| Oxylabs | n/a | 34% | 100% | 100% |
| Bright Data | 21% | 18% | 100% | 87% |
| Traject Data | n/a | 0% | 0% | n/a |
| Nimble | 4% | 5% | n/a | n/a |
| Serper | 0% | n/a | n/a | n/a |
*Lower is better for the customer.
Organic results
The same rate on the main result list alone: the organic results on the web SERP, the stories on Google News, the visual matches on Google Lens. Nothing nested under it counts, so sitelinks, video cards, AI-overview references and People-also-ask links are out. It is the one block every provider parses, which makes the cell comparable across providers that parse different numbers of blocks.
| provider | Google Light | Google News | Google Lens (visual matches) | |
|---|---|---|---|---|
| SearchApi | 0% | 0% | 0% | 0% |
| SerpApi | 0% | 0% | 0% | 0% |
| HasData | 0% | 0% | 0% | n/a |
| Scrapingdog | 0% | 0% | 0% | 0% |
| ScrapingBee | 0% | 0% | n/a | n/a |
| DataForSEO | n/a | 0% | 0% | 100% |
| Oxylabs | n/a | 0% | 100% | 100% |
| Bright Data | 0% | 0% | 100% | 100% |
| Traject Data | n/a | 0% | 0% | n/a |
| Nimble | 0% | 0% | n/a | n/a |
| Serper | 0% | n/a | n/a | n/a |
*Lower is better for the customer. A cell lower here than in the All results table is a provider that resolves its organic links and leaves the other blocks wrapped.
In the HTML: share of links leaving Google that are /goto#
This is the page before any parser touched it, and it is the same page for everyone. The Google column is 100% in nine of the ten rows with HTML: Google served a fully wrapped web SERP to every provider, whatever proxy pool, tier or fetch method sits behind the API. Traject Data’s 0% is not the exception it looks like. Its HTML comes back with every href already pointing at the destination, while Google’s click beacons on the same anchors, and the page’s script data, still carry the tokens. News and Lens are 100% wherever a Google page was captured, with DataForSEO’s Lens page at 95%. On Lens that pairing is weaker evidence than on the web SERP: SerpApi and Scrapingdog return clean visual-match links with no resolver switch, over pages whose anchors are all wrapped, so a 0% there does not by itself prove a round trip through Google’s resolver.
| provider | Google Light | Google News | Google Lens (visual matches) | |
|---|---|---|---|---|
| SearchApi | 0% | 100% | 100% | 100% |
| SerpApi | 0% | 100% | 100% | 100% |
| HasData | 0% | 100% | 100% | n/a |
| Scrapingdog | 4% | 100% | n/a | 100% |
| ScrapingBee | 100% | 100% | n/a | n/a |
| DataForSEO | n/a | 100% | 100% | 95% |
| Oxylabs | n/a | 100% | 100% | 100% |
| Bright Data | 100% | 100% | 100% | 100% |
| Traject Data | n/a | 0% | 0% | n/a |
| Nimble | 100% | 100% | n/a | n/a |
| Serper | n/a | n/a | n/a | n/a |
0% means no anchor leaving Google carried a token (on the basic-HTML page there was nothing to resolve, for Traject Data, see above), 100% means every link leaving Google in the raw page was a /goto token
What 0% does not mean. Every 0% above means not wrapped. It does not mean correct. A provider that resolves to a stale destination, or rebuilds the link from the displayed breadcrumb, scores the same 0% as one that followed every token. The raw HTML holds the tokens, each one can be resolved through Google and diffed against the provider’s clean link, and that check is the next thing to run.
Conclusion#
Which SERP APIs resolve /goto? On the web SERP, all of them, for the organic results. Below the organic block, and on News and Lens, about half do not. Providers differ in which blocks they resolve and in whether the JSON admits what was done.

Google did not remove the URLs from search results. It put its resolver between the SERP APIs and every one of them.