{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreibinelxeogn2je25migzpdyphrxyrreuooveq2bpunbmxcphmvbre",
    "uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mprkbzbpzog2"
  },
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreidnfxpt44pexqmlghirwq2l3qt6oonk5wh6xhg3562h62xdtkpyj4"
    },
    "mimeType": "image/webp",
    "size": 78140
  },
  "path": "/scrapemint/scrape-google-trends-without-an-api-key-including-the-scraper-flag-google-hands-you-8o7",
  "publishedAt": "2026-07-03T21:23:25.000Z",
  "site": "https://dev.to",
  "tags": [
    "ai",
    "api",
    "javascript",
    "showdev",
    "https://apify.com/scrapemint/google-trends-scraper"
  ],
  "textContent": "Google Trends has no official API, and most wrapper libraries rot within months. But the Trends site itself runs on a keyless JSON API that anyone can call, and it serves the exact numbers you see in the UI. Here is the full recipe, including one gotcha where Google quietly labels your session a scraper.\n\n##  The two step flow\n\nTrends works in two steps. First you call `explore`, which returns a list of widgets, one per chart on the page, each with a signed token:\n\n\n\n    GET https://trends.google.com/trends/api/explore\n        ?hl=en-US&tz=0\n        &req={\"comparisonItem\":[{\"keyword\":\"web scraping\",\"geo\":\"US\",\"time\":\"today 12-m\"}],\"category\":0,\"property\":\"\"}\n\n\nThen you call a widget data endpoint with that widget's `request` and `token`:\n\n\n\n    GET https://trends.google.com/trends/api/widgetdata/multiline?hl=en-US&tz=0&req=<widget.request>&token=<widget.token>\n\n\nThe widget kinds map to endpoints: `TIMESERIES` uses `multiline`, `GEO_MAP` uses `comparedgeo`, and both `RELATED_QUERIES` and `RELATED_TOPICS` use `relatedsearches`.\n\n##  The cookie trick\n\nCall `explore` cold and you get a 429. The API wants a `NID` cookie, and here is the counterintuitive part: you get it by requesting the public explore page first, and that page may itself respond 429 while still setting the cookie you need.\n\n\n\n    const res = await fetch('https://trends.google.com/trends/explore?geo=US&q=test');\n    // res.status may be 429. The Set-Cookie header is still there.\n    const cookies = res.headers.getSetCookie();\n\n\nGrab the cookie from the 429 response, retry `explore`, and everything works.\n\n##  Strip the anti JSON prefix\n\nEvery Trends response starts with a junk line like `)]}'` to break naive `JSON.parse` calls. Drop everything up to the first newline:\n\n\n\n    const body = await res.text();\n    const data = JSON.parse(body.slice(body.indexOf('\\n') + 1));\n\n\n##  The scraper flag\n\nHere is the part I have not seen documented. Look inside the widget `request` object that `explore` returns to a keyless session:\n\n\n\n    \"userConfig\": { \"userType\": \"USER_TYPE_SCRAPER\" }\n\n\nGoogle knows. And it is mostly fine with it: the timeline, regions and related queries widgets all return full data anyway. But the related topics widget returns an empty list for scraper sessions. You cannot cheat it either, because the token is signed over the request. Edit `userType` to `USER_TYPE_LEGIT_USER` and the endpoint answers 401.\n\nSo the practical rule: related queries yes, related topics no. Plan your product around that.\n\n##  What you get\n\nThe `multiline` response is a dated timeline of 0 to 100 values, `comparedgeo` gives a value per region, and `relatedsearches` gives two ranked lists, top and rising, where rising entries carry growth like `+250%` or `Breakout`. That rising list is the most useful output in the whole API: it shows what people are starting to search around a niche before it peaks.\n\nPace your calls (a second or two between keywords) and refresh the cookie when a 429 slips through, and the whole thing runs reliably from a datacenter IP with no proxy.\n\n##  If you want it as a service\n\nI packaged this recipe as a pay per use actor: keywords in, JSON rows out (interest over time, interest by region, related rising and top queries), with the first rows of every run free: https://apify.com/scrapemint/google-trends-scraper\n\nPart of a growing set of keyless scrapers I build in public. The theme stays the same: find the JSON endpoint a site already uses, skip the browser, and the data costs almost nothing.",
  "title": "Scrape Google Trends Without an API Key (Including the Scraper Flag Google Hands You)"
}