TL;DR
Google now has an official Trends API, but it is still a limited, application-based alpha. Public documentation describes five years of consistently scaled relative-interest data, but does not publish a self-serve endpoint, universal API-key flow, price, quota, or rate-limit table. If you need data today, choose between an accepted alpha account, an unofficial client, a managed provider, or schema-based extraction from a public Trends page. The tested Colab below implements the last option.
For years, developers asking for a Google Trends API got an awkward answer: use an unofficial library, buy access from a third party, or scrape the public site. That answer changed in July 2025, when Google announced an official API alpha. It did not change as much as the word "official" might suggest.
The alpha is real, but access is limited. Its public pages explain what the data can do, while operational details remain inside the tester program. Meanwhile, the best-known Python wrapper, pytrends, is archived. Those two facts make tool selection less obvious, not more.
This guide separates the official API from the alternatives and gives you a Python workflow that was run against the public Google Trends Trending Now page on August 6, 2026. The first request returned placeholders and failed validation. A bounded retry returned five supported rows, which the workflow checked again before writing a CSV.
Does Google Trends have an official API?
Yes. Google has an official Google Trends API alpha, announced on July 24, 2025. Access is limited and application based. It is not a public API that any developer can activate from a standard Google Cloud console today.
Google says the alpha offers a rolling five-year window, aggregation by day, week, month, or year, and geographic data by region and subregion. It also solves an old comparison problem: requests are consistently scaled, so separate keywords and time windows can be combined without normalizing each response yourself.
The values still represent relative search interest, not absolute query counts. A value tells you how interest changes within Google Trends' scale. It is not the number of searches recorded for that term.
Here is the public boundary as of August 6, 2026:
| Question | What Google publishes |
|---|---|
| Is there an official API? | Yes, in alpha |
| Can anyone enable it? | No, access is limited and application based |
| How much history is available? | A rolling five years, described as 1,800 days in the announcement |
| Which time intervals are supported? | Daily, weekly, monthly, and yearly |
| Are geographic breakdowns available? | Yes, including regions and subregions |
| Are values absolute search counts? | No, they are relative search interest |
| Is a public endpoint documented? | Not on the public overview |
| Is public pricing documented? | No |
| Are quotas and rate limits public? | No public table is provided |
That last group matters. Code copied from an unofficial endpoint is not suddenly official because an official alpha exists. If you have been accepted, use the invitation documentation and credentials Google supplied to your account.
Google Trends API pricing, keys, and rate limits
Google's public alpha documentation does not state a price, publish a self-serve API-key procedure, or list universal request quotas and rate limits. There is no responsible number to quote until Google publishes one or provides it in your tester agreement.
This also means "Google Trends API free" has two different interpretations:
- The public Google Trends website can be used without a paid API subscription.
- The official API alpha has no public pricing terms, so its cost cannot be labeled free from the available documentation.
The same caution applies to a Google Trends API key. Accepted testers should follow their private onboarding material. Everybody else should avoid tutorials that invent a generic Google Cloud setup or pass browser cookies off as official API credentials.
For production planning, treat access, price, quota, retention, and support as unresolved until they appear in your contract. Google's launch announcement is useful for the data model, but it is not a service-level agreement.
Which Google Trends data route should you use?
There is no single best route. The right choice depends on whether you need historical series, today's visible topics, a supported service, or control over the output schema.
| Route | Best for | Main limitation |
|---|---|---|
| Official Google Trends API alpha | Accepted testers who need comparable time series across keywords, regions, and intervals | Limited access, with no public endpoint, pricing, or quota table |
pytrends |
Experiments with existing Python code that you can afford to repair | Unofficial and archived, with no stability promise |
| Managed Trends providers | Teams that want a documented endpoint and vendor support now | Provider-specific schemas, pricing, and usage policies |
| ScrapeGraphAI public-page extraction | Typed snapshots from a named public Trends page, combined with other web sources | It reads the page you specify and is not a replacement for the official historical API |
Official alpha
If Google has accepted your application, the alpha is the direct route to consistently scaled historical data. That makes it suited to comparisons across separate requests and long-running series with controlled geographic and temporal aggregation.
Do not design the integration from guesses. Use the endpoint, authentication method, quota, and terms provided to your accepted account.
Pytrends
pytrends is an unofficial Python client for Google Trends. Its repository was archived on April 17, 2025 and is now read-only. The maintainers also warn that it can break when Google changes its backend.
For a disposable notebook or an existing internal script, pytrends may still be enough. Starting a new production dependency on an archived client is harder to justify. Budget for failures, monitor the returned shape, and do not describe its calls as the official Google Trends API.
Managed providers
Providers such as SerpAPI, Apify, and Bright Data expose their own Google Trends integrations. They can be a practical fit when you want vendor documentation and support without waiting for alpha admission.
Check the dataset before the price. A provider may return Trending Now topics, interest over time, related queries, or several of those objects. Those are not interchangeable. Pricing and limits also change, so use each provider's current product page rather than a number copied into an old comparison post.
Public-page extraction
Public-page extraction fits a narrower job: open a known Trends page, describe the rows you need, validate them, and store a snapshot. It works well when Trends is one input in a larger market research scraping workflow or when you want to join demand signals with Google SERP data.
The example below has one deliberately small contract: capture the first five visible US topics from Trending Now. It does not claim historical coverage, monthly search volume, or access to Google's private alpha.
A tested Google Trends API Python workflow
The companion notebook runs on Python 3.12 and pins every package involved in the result:
%pip install -q "scrapegraph-py==2.1.0" "pydantic==2.13.4" "pandas==2.3.3"Open the tested Google Colab notebook, enter your ScrapeGraphAI API key in the hidden prompt, and run the cells in order. The shared notebook contains no saved key and no saved execution output.
1. Load the client without exposing the key
import getpass
import os
import re
from datetime import datetime, timezone
from pathlib import Path
from typing import Literal
import pandas as pd
from pydantic import BaseModel, Field
from scrapegraph_py import FetchConfig, ScrapeGraphAI
api_key = os.getenv("SGAI_API_KEY") or getpass.getpass(
"ScrapeGraphAI API key: "
)
assert api_key, "A ScrapeGraphAI API key is required"getpass keeps the value out of the visible notebook cell. In a scheduled job, supply SGAI_API_KEY through your secret manager instead.
2. Define the page and typed response
SOURCE_URL = (
"https://trends.google.com/trending"
"?geo=US&hours=24&sort=search-volume"
)
OUTPUT_CSV = Path("google_trends_trending_now.csv")
CAPTURED_AT = datetime.now(timezone.utc).isoformat(timespec="seconds")
class TrendRow(BaseModel):
rank: int = Field(ge=1, le=5)
query: str = Field(min_length=1)
search_volume: str = Field(min_length=1)
started: str = Field(min_length=1)
trend_status: str = Field(min_length=1)
evidence: str = Field(min_length=20)
class TrendSnapshot(BaseModel):
geo: Literal["US"]
trends: list[TrendRow] = Field(min_length=5, max_length=5)The schema deliberately keeps search_volume as text. Trending Now displays bands such as 100K+ searches; converting that string to 100000 would add precision the source does not provide.
3. Ask for evidence, not just values
PROMPT = """
Read the visible Google Trends Trending now table at this page. Return
exactly the first five visible rows in displayed order. Copy query, search
volume, started time, and trend status exactly as displayed. Evidence must
be a single exact source string from that row that contains the query,
displayed search volume, started time, and status together. Do not shorten
evidence to only the query. Set geo to US. Do not infer or calculate values.
""".strip()The evidence field gives the validator something independent to check. A row cannot pass merely because it contains five non-empty strings.
4. Reject placeholders and unsupported fields
def compact(value: str) -> str:
return re.sub(r"\s+", "", value).casefold()
BLOCKED_VALUES = {
"no content available",
"not available",
"unknown",
"n/a",
}
def validate_snapshot(snapshot: TrendSnapshot) -> None:
ranks = [row.rank for row in snapshot.trends]
if ranks != list(range(1, 6)):
raise ValueError(f"Expected ranks 1 through 5, received {ranks}")
for row in snapshot.trends:
evidence = compact(row.evidence)
fields = {
"query": row.query,
"search_volume": row.search_volume,
"started": row.started,
"trend_status": row.trend_status,
}
for name, value in fields.items():
if value.casefold().strip() in BLOCKED_VALUES:
raise ValueError(
f"{name} contains a placeholder for rank {row.rank}"
)
if compact(value) not in evidence:
raise ValueError(
f"{name} is unsupported for rank {row.rank}: {value!r}"
)
if not re.fullmatch(
r"[0-9][0-9.,]*[KMB]?\+? searches",
row.search_volume,
flags=re.IGNORECASE,
):
raise ValueError(
f"Unexpected volume format for rank {row.rank}: "
f"{row.search_volume!r}"
)
if not re.fullmatch(r"[0-9]+[mhd] ago", row.started):
raise ValueError(
f"Unexpected start time for rank {row.rank}: {row.started!r}"
)Validation is the important part of this example. During the live test, a normal JavaScript fetch returned plausible-looking placeholder rows. An earlier, weaker check would have accepted them. The version above rejects known placeholders, requires every field to appear in the row evidence, checks the displayed volume format, and requires ranks one through five.
5. Fetch once, then make one bounded retry
def request_snapshot(stealth: bool = False) -> TrendSnapshot:
with ScrapeGraphAI(api_key=api_key) as client:
response = client.extract(
PROMPT,
url=SOURCE_URL,
schema=TrendSnapshot.model_json_schema(),
fetch_config=FetchConfig(
mode="js",
stealth=stealth,
wait=8000 if stealth else 5000,
country="us",
scrolls=2 if stealth else 1,
),
)
if response.status != "success":
raise RuntimeError(response.error or response.status)
result = TrendSnapshot.model_validate(response.data.json_data)
validate_snapshot(result)
return result
try:
snapshot = request_snapshot()
except Exception as first_error:
print(f"[retry] normal JavaScript fetch failed: {first_error}")
try:
snapshot = request_snapshot(stealth=True)
except Exception as retry_error:
raise RuntimeError(
"Google Trends failed normal and stealth extraction"
) from retry_error
print(
f"Validated {len(snapshot.trends)} rows captured at {CAPTURED_AT}"
)The retry is intentional and bounded. It changes the fetch settings once, then stops. A production worker should also record both attempts, apply backoff between scheduled runs, and alert when the page no longer satisfies the schema.
6. Export and read the CSV back
rows = []
for trend in snapshot.trends:
row = trend.model_dump()
row["captured_at"] = CAPTURED_AT
row["geo"] = snapshot.geo
row["source_url"] = SOURCE_URL
rows.append(row)
frame = pd.DataFrame(rows)
frame.to_csv(OUTPUT_CSV, index=False)
saved = pd.read_csv(OUTPUT_CSV)
assert len(saved) == 5
assert saved["rank"].tolist() == list(range(1, 6))
assert saved["evidence"].str.len().ge(20).all()
assert saved["source_url"].eq(SOURCE_URL).all()
print(f"Wrote {OUTPUT_CSV} with {len(saved)} validated rows")Reading the file back catches a different class of problem from response validation. It confirms that the exported artifact still has five ordered rows, evidence, and the expected source URL.
What the live test returned
The notebook was executed on August 6, 2026. The normal fetch was rejected because it returned placeholders. The single stealth retry passed all checks and produced this snapshot:
| Rank | Query | Displayed search volume | Started | Status |
|---|---|---|---|---|
| 1 | restaurant chain | 100K+ searches | 15h ago | Active |
| 2 | salmonella outbreak linked to eggs | 50K+ searches | 10h ago | Active |
| 3 | lafc vs guadalajara | 100K+ searches | 7h ago | Active |
| 4 | lafc - guadalajara | 100K+ searches | 7h ago | Active |
| 5 | inter miami vs san luis | 200K+ searches | 10h ago | Active |
These rows are test evidence, not evergreen trend recommendations. Trending Now changes continuously, so your run should return whatever is visible at that moment. The displayed bands also should not be confused with SEO-tool monthly keyword estimates or the official API's relative-interest series.
Using the same workflow from JavaScript or REST
The extraction contract is not tied to a notebook. A JavaScript service can send the same URL, prompt, JSON Schema, and fetch configuration to the Extract endpoint, then run equivalent validation with its schema library.
Keep the boundary clear:
- Your application defines the source page and desired schema.
- The extraction service fetches the rendered page and returns structured data.
- Your validator rejects missing, placeholder, or unsupported values.
- Your storage layer records the capture time, geography, and source URL.
The Python SDK packages those steps neatly for a tutorial. Direct REST is a better fit when you already have a TypeScript backend or do not want another runtime. Start with the current ScrapeGraphAI API documentation rather than translating an older SDK example line by line.
If your product needs rankings rather than demand signals, use a rank tracking API workflow. If it needs the organic links and page features for one query, read the Google SERP scraping guide. Trends data and SERP data answer different questions.
Production safeguards worth keeping
A notebook proves the shape of a workflow. It does not provide production guarantees. Before scheduling it, add controls around the parts most likely to change.
Keep the source contract narrow
Point at one named page, geography, and time window. Save the source URL with every row. A vague request such as "get Google Trends data" makes it harder to detect when the source or meaning has changed.
Store capture context
At minimum, store capture time, geography, source URL, fetch mode, response status, and validation result. If a topic disappears on the next run, you can distinguish a real change from a request that never passed validation.
Fail closed
Empty strings are not the only failure mode. A model can return helpful placeholders or infer a clean value from an incomplete page. Do not write a snapshot merely because it matches the JSON type. Require source evidence and reject values your downstream users could mistake for measurements.
Respect access and usage terms
Review Google's terms and the terms of every service in your stack. Do not use this workflow to access private data, bypass controls, or manufacture an official API relationship you do not have. For broader implementation choices, the web scraping API guide covers schema, retries, and monitoring in more depth.
Frequently asked questions
Is the Google Trends API official?
Yes. Google announced it in July 2025, although access remains limited and application based. Browser-facing endpoints used by third-party libraries are separate from that program.
Is the Google Trends API free?
No public price is listed. The Trends website is available without a paid API plan, but that does not establish the commercial terms of Google's official API.
How do I get a Google Trends API key?
Apply through Google's alpha page. If accepted, use the credentials and instructions provided for your account. The overview does not document a universal key-creation flow.
What is the Google Trends API rate limit?
Google has not put a universal quota table on the alpha overview. Accepted testers need to use their onboarding material or contract; alternative providers set their own unrelated limits.
Can I use Google Trends from Python?
Yes, through official alpha access if accepted, a managed provider, an unofficial client, or public-page extraction. The notebook in this guide uses scrapegraph-py==2.1.0 and validates a Trending Now snapshot with Pydantic before saving it.
Is pytrends the official Google Trends API?
No. pytrends describes itself as an unofficial client. Its GitHub repository was archived in April 2025, so new production projects should account for breakage and lack of maintenance.
Can Trends data replace keyword volume data?
No. Google Trends measures relative interest, while keyword tools estimate search volume with their own models. Use Trends for direction, seasonality, and comparisons within its scale. Use a suitable keyword source when you need estimated monthly searches.