Skip to content

Repository files navigation

Trendflow logo

Trendflow

PyPI version

A type-safe Python library for querying, streaming, and exporting Google Trends data

Features

  • Type-safe API: regions, timeframes, resolutions, and export formats use enums instead of raw strings.
  • Rich queries: interest over time, regional breakdown, live trending searches, and related queries, with dataclass results.
  • Exports: JSON, CSV, or load results into a pandas DataFrame.

Usage

import trendflow
from trendflow import Region, Timeframe, Resolution, SearchProperty, ExportFormat

# Initialize client (optional API config)
tf = trendflow.Client(language="en", timeout=10)

# --- Enums for type safety ---
# Region.US, Region.GB, Region.DE ...             (or any code: "US-CA", "807")
# Timeframe.PAST_HOUR ... PAST_5_YEARS, ALL_TIME  (or "2023-01-01 2023-06-30")
# Resolution.COUNTRY, Resolution.REGION, Resolution.CITY
# SearchProperty.WEB, IMAGES, NEWS, YOUTUBE, SHOPPING

# Fetch interest over time
data = tf.interest_over_time(
    keywords=["Python", "JavaScript", "Rust"],
    timeframe=Timeframe.PAST_YEAR,
    region=Region.US,
)

# Dataclass-backed results
print(data.keywords)        # ["Python", "JavaScript", "Rust"]
print(data.granularity)     # "weekly"
print(data.points)          # list of TrendPoint(date, scores: dict)

# Get regional breakdown (region defaults to Region.US)
regional = tf.interest_by_region(
    keyword="Python",
    resolution=Resolution.COUNTRY,
)

# Trending searches right now
trending = tf.trending_now(region=Region.US)
for item in trending.results:
    print(item.title, item.traffic, item.articles)  # TrendingItem dataclass

# Related queries — returns RelatedResult dataclass (region defaults to worldwide)
related = tf.related_queries("machine learning", region=Region.GB)
for query in related.top:
    print(query.term, query.value)    # RelatedQuery(term, value)
for query in related.rising:
    print(query.term, query.breakout) # RelatedQuery(term, breakout%)

# --- Narrowing a query ---
# Every query method takes an optional category and search property, and any of them
# accepts a custom date range and a sub-region or metro code in place of the named values.

# "jaguar" the car, on YouTube, in California, over the first half of 2023
jaguar = tf.interest_over_time(
    keywords=["jaguar"],
    timeframe="2023-01-01 2023-06-30",
    region="US-CA",
    category=47,  # Autos & Vehicles — disambiguates without needing a topic id
    search_property=SearchProperty.YOUTUBE,
)

# --- Exports ---
data.export(ExportFormat.CSV,  path="trends.csv")
data.export(ExportFormat.JSON, path="trends.json")
data.to_dataframe()  # pandas DataFrame

Feature Parity

Trendflow also ships as a JavaScript/TypeScript library: trendflow-js (npm: trendflow).

Current: trendflow-py 0.3.0 · trendflow 0.3.0. Versions are independent; each changelog cross-references the sibling release.

Feature Python — trendflow-py JS — trendflow
Interest over time ✅ ✅
Interest by region ✅ ✅
Trending now ✅ ✅
Trending growth % and volume ✅ ✅
Trending for any country code ✅ ✅
Trending news articles (RSS) ✅ ✅
Selectable trending backend ✅ ✅
Related queries ✅ ✅
Search suggestions ✅ suggestions() ✅ suggestions()
Query by topic (entity mid) ✅ ✅
Category filter ✅ ✅
Search property (YouTube, News, …) ✅ ✅
Custom date ranges ✅ ✅
Sub-regions and metro codes ✅ ✅
CSV / JSON export ✅ ✅
Rotating proxy pool ✅ ✅
Browser User-Agent by default ✅ ✅
Full geo hierarchy ✅ geo_list() ✅ geoList()
Overridable RPC ids ✅ ✅
pandas DataFrame ✅ to_dataframe() ❌ N/A
Plain-object rows ❌ N/A ✅ toArray()
ESM + CommonJS + types ❌ N/A ✅
CLI ✅ 🔜 planned

Trending now

Google retired the hottrends/visualize/internal/data endpoint, along with api/dailytrends and api/realtimetrends; all three now return HTTP 404. trending_now() therefore runs on the batchexecute RPC that trends.google.com itself uses, which returns more than the old endpoint did:

trending = tf.trending_now(Region.US)
for item in trending.results:
    print(item.title, item.growth, item.volume, item.traffic)
    # "fifa world cup 2026"  3650  6  "+3,650%"
  • growth is the percentage rise over the window, volume a relative search-volume index.
  • Any country code works, not a fixed list, and worldwide is now allowed (and the default).
  • articles is always empty — this endpoint carries no article links.
  • No cookie is needed, and the RPC answers on IPs that get a 429 from the widgetdata endpoints, so trending_now() often works where the other queries do not.

Pass window=TRENDING_WINDOW_TOP for the highest-volume searches instead of the fastest-growing ones. window is an undocumented Google parameter; other integers between 4 and 12 also return data over varying recency windows.

Trending backends: RPC and RSS

Google exposes trending searches two ways. They are not interchangeable, so backend lets you pick:

"rpc" (batchexecute) "rss" (feed)
items 50 10
payload ~2 KB JSON ~21 KB XML
growth % and volume ✅ ❌ — buckets like "2000+"
news articles ❌ ✅
window selection ✅ ignored by Google
worldwide ✅ ❌ country only
rss = tf.trending_now(Region.US, backend="rss")
rss.source  # "rss"
rss.results[0].articles
# [TrendingArticle(title='...', url='https://...', source='Buffalo News', picture='https://...')]

"auto" (the default) tries the RPC and falls back to the feed. The RPC comes first deliberately: it returns five times the items with real growth figures, so defaulting to RSS would quietly degrade results. Reach for "rss" when you want the articles — that is the one thing the RPC cannot give you — or as a second opinion if the RPC id ever goes stale.

Note that the feed is not a lighter path despite being a feed, and Google ignores hours, sort and count on it: it always returns the same 10 entries.

Topics and search suggestions

Google distinguishes a search term (the literal string) from a topic (the entity, in every spelling and language). suggestions() finds the topic; every query method already accepts one — pass the mid where you would pass a keyword.

topics = tf.suggestions("artificial intelligence")
# [TopicSuggestion(mid='/m/0mkz', title='Artificial intelligence', type='Professional field')]

data = tf.interest_over_time(
    keywords=[topics[0].mid, "artificial intelligence"],
    timeframe=Timeframe.PAST_YEAR,
    region=Region.US,
)
# {'/m/0mkz': 62, 'artificial intelligence': 1}

That gap is the point: the topic scores 62 where the literal phrase scores 1, because it aggregates every phrasing and translation people actually search.

suggestions() needs no cookie and no proxy — it answers on IPs the widgetdata endpoints reject with 429, same as trending_now(). type disambiguates same-name entities ("Nike" returns both the company and the goddess) and is None when Google omits it.

Rate limits

Google Trends aggressively rate-limits datacenter and shared IPs, so 429 is common even on your first request of the day. Two things matter:

  1. User-Agent. Google returns 429 to the default agent strings Python HTTP clients send, no matter how few requests you have made. This library sends a browser User-Agent by default for exactly that reason.
  2. IP reputation. Once an IP is flagged, every request gets 429 regardless of headers. Route through a residential proxy to recover.

Using a proxy pool

Pass a list of proxy URLs and the client rotates through them automatically, moving to the next one whenever a query is refused:

import trendflow
from trendflow import Region

tf = trendflow.Client(
    proxies=[
        "http://user:pass@gate.decodo.com:7000",
        "http://user:pass@gate.decodo.com:7000",
    ],
    max_proxy_attempts=3,  # defaults to the pool size, capped at 5
    on_proxy_rotate=lambda attempt, error: print(f"rotated after {attempt}: {error!r}"),
)

trending = tf.trending_now(Region.US)
print(tf.current_proxy)  # the proxy that answered

Entries are just URLs, so a pool can mix providers. Repeating one rotating gateway also works: each entry gets its own connection, so it lands on a fresh exit IP.

Rotation happens per query, not per request — this matters. Google binds the NID cookie and the widget token to the IP that requested them, so a single query must complete on one exit IP; sending the follow-up widgetdata call from a different IP earns an instant 429. The pool pins one proxy for the whole query and advances only on failure, re-seeding the cookie jar each time. For the same reason, point the pool at sticky sessions rather than per-request rotating endpoints if your provider offers the choice.

Rotation is skipped for errors a different IP cannot fix, such as a 404 or a renamed RPC.

Where to get proxies

Residential proxies are what actually clears Google's 429. Verified against this library:

Decodo

Provider Notes Endpoint format
Decodo (formerly Smartproxy) Cheapest entry tier; pay-as-you-go available. Used to verify this library's live tests. http://user:pass@gate.decodo.com:7000

Ask for sticky sessions when you sign up — per-request rotating endpoints break the cookie/token binding described above. Note that a shared residential pool can be exhausted for Google Trends specifically, in which case even a valid proxy returns 429; that is what max_proxy_attempts is for.

If Google renames an RPC

The batchexecute RPC identifiers are pinned constants; they are not discoverable at runtime. If Google renames one, calls raise UnknownRpcError naming the identifier, and you can patch it without waiting for a release by passing rpc_ids to trendflow._trends_http.batchexecute.BatchExecuteClient.

Documentation

Documentation is built with Zensical and deployed to GitHub Pages.

API documentation is auto-generated from docstrings using mkdocstrings.

Docs deploy automatically on push to master or main via GitHub Actions.

Development

To set up for local development:

# Clone your fork
git clone git@github.com:dariomory/trendflow.git
cd trendflow

# Install in editable mode with live updates
uv tool install --editable .

This installs the CLI globally but with live updates - any changes you make to the source code are immediately available when you run trendflow.

Run tests:

uv run pytest

Run quality checks (format, lint, type check, test):

just qa

Author

Trendflow was created in 2026 by Dario Mory

About

Type-safe Python library for querying, streaming, and exporting Google Trends data — interest over time, by region, trending now, related queries. pandas export, proxy rotation.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages