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

11 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages