trendreq: a drop-in pytrends replacement
pytrends was archived in April 2025, and it now fails with 429 Too Many Requests errors for most people. trendreq keeps the same TrendReq interface, so your existing scripts keep working. The requests run on the CleanScrape Google Trends Actor on Apify, with proxies and retries on the server side.
# from pytrends.request import TrendReq # before
from trendreq import TrendReq # after
pytrends = TrendReq(hl="en-US", tz=360)
pytrends.build_payload(["iced coffee", "cold brew"], timeframe="today 12-m", geo="US")
df = pytrends.interest_over_time()
iced coffee cold brew isPartial
date
2026-09-20 47 51 False
2026-09-27 42 49 False
2026-10-04 32 33 True
Install
pip install trendreq
Then set your Apify API token. A free Apify account includes $5 of usage a month. Copy the token from Apify Console > Settings > API & Integrations.
export APIFY_TOKEN=your_token # macOS / Linux
setx APIFY_TOKEN your_token # Windows (open a new terminal afterwards)
You can also pass it in code with TrendReq(apify_token="..."). Keep the token out of shared notebooks and repositories.
Moving from pytrends
| pytrends method | trendreq | Notes |
|---|---|---|
build_payload(kw_list, cat, timeframe, geo, gprop) |
Same | Up to 5 keywords. gprop (YouTube, News, Images, Shopping) is not supported yet. |
interest_over_time() |
Same shape | Date index (UTC), one int column per keyword, isPartial. |
interest_by_region(resolution, inc_low_vol, inc_geo_code) |
Same shape | COUNTRY, REGION, CITY and DMA (US metro areas). City rows also get latitude and longitude, so towns with the same name stay apart. With a country chosen, COUNTRY gives its regions, as pytrends does. |
related_queries() |
Same shape | {keyword: {"top": DataFrame, "rising": DataFrame}} with query and value. |
related_topics() |
Same shape | Google currently returns no related topics for automated requests (pytrends gets the same), so this gives None with a warning. |
trending_searches(pn) |
Same shape | pn can be a pytrends country name ("united_kingdom") or a code ("GB"). |
today_searches(pn) |
Same | A Series of today's trending searches. |
realtime_trending_searches(pn) |
Similar | Columns: title, approxTraffic, pubDate, relatedNews. |
suggestions(), categories(), top_charts() |
Not available | They raise NotImplementedError with an explanation. |
Exceptions keep pytrends' names: ResponseError and TooManyRequestsError (raised only if Google still limits the request after the server-side retries). retries= re-runs a rate-limited data type.
Time ranges work as in pytrends: "today 5-y", "today 12-m", "today 3-m", "now 7-d", "now 1-d", "all", or exact dates such as "2024-01-01 2024-06-30". Regions take codes such as "US", "US-CA" or "DE".
Settings that only made sense for direct requests (proxies, timeout, backoff_factor, requests_args) are accepted and ignored, so you don't need to change your constructor call.
Extras pytrends doesn't have
One run for several data types. Each method call starts one Apify run. To collect several data types for the same keywords, call fetch() first. It saves the per-run start fee:
pytrends.build_payload(["bitcoin", "ethereum"], timeframe="today 5-y")
pytrends.fetch("interest_over_time", "related_queries", "interest_by_region")
over_time = pytrends.interest_over_time() # no new run
related = pytrends.related_queries() # no new run
Trending searches with context. pytrends.trending_now("US") returns rank, approximate traffic, publish time and related news headlines.
Spending cap. TrendReq(max_charge_usd=0.50) stops any single run at 50 cents.
What it costs
You pay Apify for the Actor's results, at the Actor's current price. At the base price that is $0.05 per run plus $0.003 per row, with discounts on paid Apify plans. Some examples at base prices:
| Job | Rows | Cost |
|---|---|---|
2 keywords, 12 months, weekly (interest_over_time) |
106 | about $0.37 |
1 keyword, US states (interest_by_region) |
51 | about $0.20 |
2 keywords, time series + states + related queries in one fetch() |
102 | about $0.36 |
| Today's trending searches for one country | 10 | about $0.08 |
The free $5 monthly credit covers roughly 15 to 60 typical calls.
Why pytrends fails with 429 errors
Google Trends has no official public API. pytrends sends requests from your own IP address, first to get a short-lived token and then to fetch each chart. Google limits how often one address can do that, so scripts that loop over keywords quickly hit 429 Too Many Requests. Waiting, rotating proxies by hand and lowering the request rate only helps for a while. trendreq moves those requests to Apify, where the Actor rotates residential proxies and retries for you.
Limits
- Values are relative interest from 0 to 100 within your comparison, the same as on the Google Trends website. They are not search counts.
- Results can differ slightly between runs, as they do on the website, because Google samples its data.
- Each method call is one Apify run and takes about 5 to 30 seconds. Use
fetch()to collect several data types at once. - Related topics are currently empty, because Google returns none for automated requests.
Testing
python -m unittest discover -s tests
The tests run offline against real rows saved from the Actor, so they need no token.
About
trendreq is maintained by CleanScrape. It is not affiliated with Google or with the pytrends project. MIT licence.
Bugs and ideas: open an issue or email contact.cleanscrape@gmail.com.
Metadata
Release files for trendreq 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| trendreq-0.1.0.tar.gz | 35.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| trendreq-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 47.8 kB
Release files / trendreq-0.1.0.tar.gz
| Download URL | trendreq-0.1.0.tar.gz |
|---|---|
| Size | 35.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f29edb338edbb8e1777c5def7a6fe89deb3934b6c1ec13afdd5d90dd69d480dd
|
|
BLAKE2b-256 checksum How to use checksums |
3250813dce846754d65f2ef8a1fab36012b9bedb11ac4dc321ecbe6570ec2afb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.
Transparency logRelease files / trendreq-0.1.0-py3-none-any.whl
| Download URL | trendreq-0.1.0-py3-none-any.whl |
|---|---|
| Size | 12.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e99166a2f0f05d6894148a517bac9ec593380748802eb8ea10cb6f308e980435
|
|
BLAKE2b-256 checksum How to use checksums |
469a2c8a2b358334f0791ebec9c90bf28d320ee95c396f3c8d476fcf188714e1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.
Transparency log