<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Esteban Ortega</title>
    <description>The latest articles on DEV Community by Esteban Ortega (@esteban_ortega).</description>
    <link>https://dev.to/esteban_ortega</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4052111%2Fab39b4b1-9853-4212-8e14-543d116ace49.png</url>
      <title>DEV Community: Esteban Ortega</title>
      <link>https://dev.to/esteban_ortega</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/esteban_ortega"/>
    <language>en</language>
    <item>
      <title>How to Scrape Telegram Channels Without the API (No Login, No Phone Number)</title>
      <dc:creator>Esteban Ortega</dc:creator>
      <pubDate>Fri, 31 Jul 2026 05:34:32 +0000</pubDate>
      <link>https://dev.to/esteban_ortega/how-to-scrape-telegram-channels-without-the-api-no-login-no-phone-number-2p66</link>
      <guid>https://dev.to/esteban_ortega/how-to-scrape-telegram-channels-without-the-api-no-login-no-phone-number-2p66</guid>
      <description>&lt;p&gt;Every guide to scraping Telegram starts the same way: register an application at my.telegram.org, get an &lt;code&gt;api_id&lt;/code&gt; and &lt;code&gt;api_hash&lt;/code&gt;, log in with your phone number, manage a session string with Telethon or Pyrogram, and hope your account doesn't get limited.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;public channels&lt;/strong&gt;, none of that is necessary. Telegram publishes a full web preview of every public channel that anyone — including a plain HTTP client — can read. Here's how it works, and where its limits are.&lt;/p&gt;

&lt;h2&gt;
  
  
  The t.me/s/ trick
&lt;/h2&gt;

&lt;p&gt;Every public channel has a server-rendered preview at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://t.me/s/{channel_name}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;https://t.me/s/telegram&lt;/code&gt; in a browser with JavaScript disabled — the messages are still there. It's plain HTML, served to any User-Agent, with &lt;strong&gt;no login, no cookies, no API key, and no bot token&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Even better: it paginates. Each page shows ~20 messages, and appending &lt;code&gt;?before={message_id}&lt;/code&gt; walks backward through history:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;UA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UA&lt;/span&gt;

&lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://t.me/s/durov&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
&lt;span class="n"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data-post=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[^/]+/(\d+)&lt;/span&gt;&lt;span class="sh"&gt;"'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="n"&gt;older&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://t.me/s/durov?before=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;  &lt;span class="c1"&gt;# next 20, older
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's actually in the preview
&lt;/h2&gt;

&lt;p&gt;More than most people assume. Each &lt;code&gt;tgme_widget_message&lt;/code&gt; block contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full message text&lt;/strong&gt; (with formatting markup you can strip or keep)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timestamp&lt;/strong&gt; — a proper ISO 8601 &lt;code&gt;datetime&lt;/code&gt; attribute&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;View count&lt;/strong&gt; ("1.1M" style, abbreviated)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reactions&lt;/strong&gt; — per-emoji counts, &lt;em&gt;including paid Star reactions&lt;/em&gt;. This surprises people; most scrapers never parse the reactions footer at all&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Media&lt;/strong&gt; — direct CDN URLs for photos and video files (&lt;code&gt;cdn*.telesco.pe&lt;/code&gt;), plus thumbnails&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Links and link previews&lt;/strong&gt;, forwarded-from attribution, reply context, post author&lt;/li&gt;
&lt;li&gt;Channel-level: title, description, &lt;strong&gt;subscriber count&lt;/strong&gt;, avatar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What's &lt;em&gt;not&lt;/em&gt; there: discussion-group comments, poll internals, subscriber-only content, and anything from &lt;strong&gt;private&lt;/strong&gt; channels or groups — those genuinely require the real API and an account. Individual user accounts and bots have no preview either.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gotchas (why "just regex it" grows teeth)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Non-channels return HTTP 200 with an empty preview&lt;/strong&gt;, not a 404 — so "no messages" and "not a public channel" look identical unless you also check for channel metadata.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;message text div ends at the first &lt;code&gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/strong&gt; — greedy patterns swallow the footer, reactions, and "VIEW IN TELEGRAM" boilerplate into your text.&lt;/li&gt;
&lt;li&gt;Counts come abbreviated (&lt;code&gt;10.2K&lt;/code&gt;, &lt;code&gt;1.1M&lt;/code&gt;) and need normalizing; custom emoji reactions are &lt;code&gt;&amp;lt;tg-emoji&amp;gt;&lt;/code&gt; elements with IDs, not characters.&lt;/li&gt;
&lt;li&gt;Albums, voice notes, stickers, and documents each have their own markup variants.&lt;/li&gt;
&lt;li&gt;At volume you'll want polite pacing and IP rotation — Telegram is tolerant, but not infinitely.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The maintained shortcut
&lt;/h2&gt;

&lt;p&gt;I packaged all of the above as an Apify Actor: &lt;a href="https://apify.com/silentshadow55/telegram-channel-actor" rel="noopener noreferrer"&gt;&lt;strong&gt;Telegram Channel Scraper&lt;/strong&gt;&lt;/a&gt;. Give it channel names in any form (&lt;code&gt;durov&lt;/code&gt;, &lt;code&gt;@durov&lt;/code&gt;, &lt;code&gt;t.me/durov&lt;/code&gt;), get structured JSON/CSV:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"channel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"durov"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;527&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"datetime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-06-15T12:04:11+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"🏆 Telegram is launching a $200,000 contest for content creators."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"views"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;21900000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total_reactions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;514000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reactions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"emoji"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"⭐paid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;24900&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"media_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"video"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"videos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"https://cdn4.telesco.pe/file/...mp4"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://t.me/durov/527"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It handles the pagination, the empty-preview trap, reaction parsing, media extraction, and keyword filtering — priced per message (about a tenth of a cent), runs on a schedule for monitoring, and plugs into Sheets/webhooks/LLM pipelines via the Apify API and MCP. A 50-message pull across two channels takes about six seconds.&lt;/p&gt;

&lt;p&gt;Typical uses: monitoring crypto/announcement channels, OSINT with timestamps and reach metrics, competitor watching, and turning channels into clean text datasets for RAG.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is this against Telegram's rules?&lt;/strong&gt; It reads content Telegram itself publishes to any anonymous web visitor. No authentication is bypassed and no private data is touched.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I get comments or poll results?&lt;/strong&gt; No — those aren't in the public preview. That's real-API territory (Telethon/Pyrogram with an account).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How far back can I scrape?&lt;/strong&gt; The &lt;code&gt;?before=&lt;/code&gt; pagination walks to the channel's first message if you let it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why not just use the Bot API?&lt;/strong&gt; Bots must be &lt;em&gt;added&lt;/em&gt; to a channel by an admin. The preview needs nothing.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you hit a channel that parses oddly, &lt;a href="https://apify.com/silentshadow55/telegram-channel-actor" rel="noopener noreferrer"&gt;open an issue on the actor&lt;/a&gt; — real examples are how coverage improves.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>webscraping</category>
      <category>tutorial</category>
      <category>api</category>
    </item>
    <item>
      <title>How to Download Every Ad a Competitor Runs on Google (and Why Most Scrapers Return Empty)</title>
      <dc:creator>Esteban Ortega</dc:creator>
      <pubDate>Wed, 29 Jul 2026 22:00:24 +0000</pubDate>
      <link>https://dev.to/esteban_ortega/how-to-download-every-ad-a-competitor-runs-on-google-and-why-most-scrapers-return-empty-3i7g</link>
      <guid>https://dev.to/esteban_ortega/how-to-download-every-ad-a-competitor-runs-on-google-and-why-most-scrapers-return-empty-3i7g</guid>
      <description>&lt;p&gt;Google publishes every ad an advertiser is currently running. Not leaked, not scraped from private data — published, on purpose, at &lt;a href="https://adstransparency.google.com" rel="noopener noreferrer"&gt;adstransparency.google.com&lt;/a&gt;, as part of its ads-accountability program. Search any brand and you'll see their whole live creative library: every image ad, every HTML5 unit, when each first and last ran, and in which countries.&lt;/p&gt;

&lt;p&gt;For anyone doing competitive ad research, that's the primary source. The catch is what's &lt;em&gt;not&lt;/em&gt; there: an export button. You can look at ads one at a time in a web UI, and that's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Transparency Center actually gives you
&lt;/h2&gt;

&lt;p&gt;Three ways to find an advertiser:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;By name&lt;/strong&gt; — resolved through a suggestion service (type "Nike", get candidate advertisers).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;By advertiser ID&lt;/strong&gt; — every advertiser has a stable &lt;code&gt;AR…&lt;/code&gt; identifier, visible in the page URL. This is the deterministic option.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;By domain&lt;/strong&gt; — with a quirk: Google exposes &lt;em&gt;match counts&lt;/em&gt; for a domain but not a clean domain→advertiser mapping, so some domains only resolve to candidates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Per creative, the underlying data includes the format (image / HTML5), preview URLs, first-shown and last-shown timestamps, and — one level deeper — per-region last-shown dates and every render variant of the creative.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why DIY scrapers (and several paid ones) silently return nothing
&lt;/h2&gt;

&lt;p&gt;The site is a JavaScript app talking to internal RPC endpoints, and if you build against them yourself, four landmines are waiting:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The payload shape drifts.&lt;/strong&gt; Google periodically reshuffles the request format. A scraper built on last quarter's payload doesn't error — it receives an empty &lt;code&gt;{}&lt;/code&gt; and happily reports "this advertiser runs zero ads." That's the worst possible failure mode for research data: it looks like an answer. It's the main reason the most-installed Transparency scraper on Apify sits at ~2.2★ — check its reviews and you'll see "returns empty" over and over.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pagination is a token chain.&lt;/strong&gt; Results come in pages linked by continuation tokens. Stop after the first page — which many tools quietly do — and a 900-creative advertiser looks like a 100-creative advertiser. No error, just silently wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An empty response is genuinely ambiguous.&lt;/strong&gt; Zero results can mean a dormant advertiser, a soft-block on your IP, &lt;em&gt;or&lt;/em&gt; payload drift. Telling those apart requires cross-checking Google's own advertiser-level ad counts — most tools don't bother.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The asset URLs expire.&lt;/strong&gt; The &lt;code&gt;simgad&lt;/code&gt; image links die quickly. A swipe file of URLs rots in weeks unless you persist the actual bytes at collection time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of this is hard to handle once — it's hard to &lt;em&gt;keep&lt;/em&gt; handled, because the drift is ongoing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The maintained option
&lt;/h2&gt;

&lt;p&gt;I run enough of this research that I packaged the whole thing as an Apify actor: &lt;a href="https://apify.com/silentshadow55/google-ads-transparency-actor" rel="noopener noreferrer"&gt;&lt;strong&gt;Google Ads Transparency Scraper&lt;/strong&gt;&lt;/a&gt;. Design goals, in order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Complete captures or a loud error — never a silent empty.&lt;/strong&gt; Payload drift, soft-blocks, and mid-pagination truncation all produce explicit error/notice records, and a truncated run is marked &lt;code&gt;partial: true&lt;/code&gt; on the advertiser summary. A genuine zero (Google's own counts confirm no ads) is reported as a clean zero.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full token pagination.&lt;/strong&gt; Every creative, not the first page. Each record carries the creative ID, format, preview/image URL, first/last shown, and a deep link back to the Transparency Center so anyone can verify.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optional depth.&lt;/strong&gt; Per-region last-shown dates and render variants (one enrichment call per creative), and optional &lt;strong&gt;asset rehosting&lt;/strong&gt; that stores the actual image bytes before the URLs expire.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The advertiser summary tells you if you got everything.&lt;/strong&gt; It carries both the count you scraped &lt;em&gt;and&lt;/em&gt; Google's own reported totals, so a capped or truncated run is visible instead of invisible.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://api.apify.com/v2/acts/silentshadow55~google-ads-transparency-actor/run-sync-get-dataset-items?token=YOUR_TOKEN"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"advertiserName": "Nike", "region": "US", "maxCreativesPerAdvertiser": 300}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pricing is per creative (about a tenth of a cent), which for context is a different shape than SerpApi's metered Transparency API (~$25/mo floor) or ad-spy dashboards like AdSpy/BigSpy ($9–459/mo seats): you pay for the advertisers you actually check, and the raw JSON is yours to pipe into Sheets, a dashboard, n8n, or an AI agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is this okay to do?
&lt;/h2&gt;

&lt;p&gt;The Ads Transparency Center is Google's own public accountability database — the same pages it shows any visitor, with no login, no gate, and no terms click-through. Reading it politely (rate-limited, no CAPTCHA or WAF circumvention — this actor does neither) is reading public data that exists specifically so ads can be scrutinized. Use it for competitive research, not for anything the transparency program wasn't built for.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I build data tools on Apify. If an advertiser, region, or format misbehaves, &lt;a href="https://apify.com/silentshadow55/google-ads-transparency-actor" rel="noopener noreferrer"&gt;open an issue on the actor&lt;/a&gt; — loud errors are the product, so bugs get fixed fast.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>marketing</category>
      <category>google</category>
      <category>datascience</category>
    </item>
    <item>
      <title>pytrends Is Dead. Here's How to Get Google Trends Data in 2026</title>
      <dc:creator>Esteban Ortega</dc:creator>
      <pubDate>Tue, 28 Jul 2026 23:25:38 +0000</pubDate>
      <link>https://dev.to/esteban_ortega/pytrends-is-dead-heres-how-to-get-google-trends-data-in-2026-1a18</link>
      <guid>https://dev.to/esteban_ortega/pytrends-is-dead-heres-how-to-get-google-trends-data-in-2026-1a18</guid>
      <description>&lt;p&gt;If you've landed here, you probably just discovered one of two things: &lt;strong&gt;pytrends — the unofficial Google Trends library the entire Python ecosystem relied on for a decade — was archived in April 2025&lt;/strong&gt; and no longer works reliably, or Google's long-promised official Trends API is &lt;em&gt;still&lt;/em&gt; in limited-access alpha and you can't get in.&lt;/p&gt;

&lt;p&gt;Meanwhile you have a dashboard, a research pipeline, or an SEO report that needs interest-over-time data. Here's the honest state of your options in 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually powers Google Trends
&lt;/h2&gt;

&lt;p&gt;There's no public API, but the Trends website itself is a JavaScript app fed by JSON endpoints — the same data you see in the charts, served as structured JSON. The flow is a two-step "token dance":&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Call &lt;code&gt;/trends/api/explore&lt;/code&gt; with your keywords, geo, and timeframe. It returns a set of &lt;strong&gt;widgets&lt;/strong&gt;, each with a token — one for the timeline, one for the region map, one each for related queries and topics.&lt;/li&gt;
&lt;li&gt;Call the matching widgetdata endpoint (&lt;code&gt;multiline&lt;/code&gt;, &lt;code&gt;comparedgeo&lt;/code&gt;, &lt;code&gt;relatedsearches&lt;/code&gt;) with that token to get the actual data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Two quirks to know: every response starts with an anti-XSSI prefix (&lt;code&gt;)]}'&lt;/code&gt;) you must strip before parsing, and you need a session cookie from an initial page visit — amusingly, Google can answer that first HTML request with HTTP 429 while &lt;em&gt;still&lt;/em&gt; setting the cookie and serving the JSON API just fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 1: Build it yourself
&lt;/h2&gt;

&lt;p&gt;A minimal working example (Python, plain &lt;code&gt;requests&lt;/code&gt;, no browser):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mozilla/5.0 ...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Accept-Language&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en-US,en;q=0.9&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://trends.google.com/trends/explore&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# cookie warm-up (ignore the status)
&lt;/span&gt;
&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;comparisonItem&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;keyword&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;standing desk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;geo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;US&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;time&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;today 12-m&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
       &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;category&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;property&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://trends.google.com/trends/api/explore&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en-US&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tz&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;360&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;req&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)})&lt;/span&gt;
&lt;span class="n"&gt;widgets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;widgets&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# strip )]}' prefix
&lt;/span&gt;
&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;widgets&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TIMESERIES&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://trends.google.com/trends/api/widgetdata/multiline&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en-US&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tz&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;360&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;req&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;request&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]})&lt;/span&gt;
&lt;span class="n"&gt;timeline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timelineData&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works today. The part that stops being fun is everything around it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rate limits.&lt;/strong&gt; Google 429s aggressively and unpredictably per IP. Any real workload needs pacing, exponential backoff, cookie-session rotation, and a proxy pool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance.&lt;/strong&gt; These are internal endpoints; Google reshuffles them occasionally (that's part of what killed pytrends once its maintainers stopped chasing).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edge cases.&lt;/strong&gt; Example: the region-map widget accepts a &lt;code&gt;resolution&lt;/code&gt; override, but forcing a resolution that doesn't fit the geo (say, &lt;code&gt;REGION&lt;/code&gt; on a worldwide query) makes Google return HTTP 500. There are a dozen little landmines like this.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you need Trends data once, copy the snippet above and enjoy. If you need it &lt;em&gt;every day&lt;/em&gt;, you've just adopted a small maintenance hobby.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 2: Use it as a maintained API
&lt;/h2&gt;

&lt;p&gt;I got tired of maintaining exactly that hobby, so I packaged the whole thing as an Apify Actor: &lt;a href="https://apify.com/silentshadow55/google-trends-actor" rel="noopener noreferrer"&gt;&lt;strong&gt;Google Trends Scraper&lt;/strong&gt;&lt;/a&gt;. One call gets you, per term:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Interest over time&lt;/strong&gt; at Google's finest granularity for your window, with partial-period flags plus computed average and peak&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interest by region&lt;/strong&gt; (countries, states, cities, or US metro areas)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Related queries and topics&lt;/strong&gt; — top and &lt;strong&gt;rising&lt;/strong&gt;, with "Breakout" markers (the early-signal goldmine for SEO and product research)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;True compare mode&lt;/strong&gt; — up to 5 terms normalized on one shared 0–100 scale, like the Trends UI comparison&lt;/li&gt;
&lt;li&gt;All five properties: &lt;strong&gt;Web, YouTube, News, Images, Shopping&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No browser anywhere in the stack — it's the same plain-HTTP flow described above, plus the unglamorous production parts: pacing, backoff, session rotation, per-session proxy identities, and per-term error reporting so one flaky keyword doesn't kill a 50-term run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://api.apify.com/v2/acts/silentshadow55~google-trends-actor/run-sync-get-dataset-items?token=YOUR_TOKEN"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"searchTerms": ["standing desk", "walking pad"], "geo": "US", "compareAllTerms": true}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Results export as JSON/CSV/Excel, run on a schedule for daily tracking, and plug into Google Sheets, Make, Zapier, LangChain — or AI agents via Apify's MCP server ("is interest in X growing?" answered with real data). Pricing is per term (about half a cent), with free platform credits covering a trial.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What do the 0–100 numbers mean?&lt;/strong&gt; Relative popularity within &lt;em&gt;your&lt;/em&gt; request's timeframe/geo/term set — 100 is the peak of that window, not an absolute volume. To compare terms against each other, they must be requested together (that's what compare mode does).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which timeframes work?&lt;/strong&gt; &lt;code&gt;now 1-H&lt;/code&gt; through &lt;code&gt;today 5-y&lt;/code&gt;, &lt;code&gt;all&lt;/code&gt;, and custom ranges like &lt;code&gt;2025-01-01 2025-06-30&lt;/code&gt;. Shorter windows return finer granularity (hourly/daily vs weekly).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is Google's official API coming?&lt;/strong&gt; It was announced in 2025 and remains limited-access alpha. When it ships broadly, use it — until then, the website's endpoints are the only public source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is scraping Trends legal?&lt;/strong&gt; It reads the same public, aggregated, anonymized statistics Google shows every visitor at trends.google.com.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I build data tools on Apify — this one exists because I needed it. If a term, geo, or timeframe misbehaves, &lt;a href="https://apify.com/silentshadow55/google-trends-actor" rel="noopener noreferrer"&gt;open an issue on the actor&lt;/a&gt; and it'll get fixed fast.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>webscraping</category>
      <category>seo</category>
      <category>datascience</category>
    </item>
  </channel>
</rss>
