Metadata-Version: 2.5
Name: trendreq
Version: 0.1.1
Summary: Drop-in pytrends replacement for the common TrendReq methods: Google Trends data through Apify, with managed proxies and retries.
Project-URL: Homepage, https://github.com/CleanScrape/trendreq
Project-URL: Documentation, https://github.com/CleanScrape/trendreq#readme
Project-URL: Issues, https://github.com/CleanScrape/trendreq/issues
Project-URL: Google Trends Actor, https://apify.com/cleanscrape/google-trends-scraper
Author-email: CleanScrape <contact.cleanscrape@gmail.com>
License: MIT
License-File: LICENSE
Keywords: 429,apify,google trends,google trends api,keyword research,pytrends,pytrends alternative,seo,trendreq
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Requires-Python: >=3.9
Requires-Dist: pandas>=1.3
Requires-Dist: requests>=2.25
Description-Content-Type: text/markdown

# trendreq: a drop-in pytrends replacement

pytrends was archived in April 2025, and scripts that use it often fail with `429 Too Many Requests`. `trendreq` keeps the `TrendReq` interface for the common methods (see the table below), so most scripts only need a new import. The requests run on the [CleanScrape Google Trends Actor](https://apify.com/cleanscrape/google-trends-scraper) on Apify, with proxies and retries on the server side.

```python
# 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

```bash
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](https://console.apify.com/settings/integrations).

```bash
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:

```python
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](https://apify.com/cleanscrape/google-trends-scraper/pricing). 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's official Trends API is an alpha with access by application, and pytrends does not use it. 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.
- Retries reduce rate-limit failures but cannot rule them out. If Google still limits a request, trendreq raises `TooManyRequestsError` instead of returning partial data.

## Testing

```bash
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](https://apify.com/cleanscrape). It is not affiliated with Google or with the pytrends project. MIT licence.

Bugs and ideas: [open an issue](https://github.com/CleanScrape/trendreq/issues) or email contact.cleanscrape@gmail.com.
