Trendflow JS
MCP serverDev toolstrendflow gives your AI access to Google Trends data, so it can answer questions about what people are searching for. Once added, your AI can look up search interest over time, compare interest by region, find related queries, and see what is trending now.
Use Trendflow JS in Claude, ChatGPT or Ahel Desktop
Free. Sign in, add Trendflow JS and connect your AI. About a minute.
Also: Claude Code · Cursor · Codex
Then ask your AI: use Trendflow JS to list regions
No key or account is asked for. Not verified by us yet: Ahel has not run a call through it.
Details
Available today. Use it from your connected AI after setup.
No key or account is asked for. Not verified by us yet: Ahel has not run a call through it.
Add trendflow, then ask your AI about any topic, for example how search interest has changed over time or which regions search for it most.
What your AI can do with it
- Show how search interest in a topic changes over time
- Break down search interest by region
- Find related queries for a topic
- List what is trending now on Google Trends
Install Trendflow JS
The server’s own address, for the clients that take one directly. Or connect Ahel once and every client you use reads it from one address, with the account kept on Ahel rather than in each client’s config.
Claude Code
claude mcp add --transport http --scope user trendflow-js 'https://trendflow.mory.dev/mcp'Run it once in your project, then open /mcp to approve any sign-in the server asks for.
Claude Desktop
https://trendflow.mory.dev/mcpAdd a custom connector in Settings, paste this address, and approve the sign-in.
Cursor
cursor://anysphere.cursor-deeplink/mcp/install?name=trendflow-js&config=eyJ1cmwiOiJodHRwczovL3RyZW5kZmxvdy5tb3J5LmRldi9tY3AifQ==Open the link and Cursor adds the server at that address.
ChatGPT
https://trendflow.mory.dev/mcpIn Settings, enable Developer mode, create an MCP app, and paste this address. Your plan and workspace must allow custom apps.
Codex
codex mcp add trendflow-js --url 'https://trendflow.mory.dev/mcp'Run it once, then sign in with codex mcp login trendflow-js if the server asks for an account.
From the project's README
As published by dariomory/trendflow-js in README.md.
A type-safe JavaScript/TypeScript library for querying and exporting Google Trends data.
The JavaScript port of trendflow-py.
📖 Documentation: trendflow.mory.dev/docs/js — guides for both libraries, plus a hosted MCP server for ChatGPT, Claude, and Cursor.
- GitHub: https://github.com/dariomory/trendflow-js/
- npm package: https://www.npmjs.com/package/trendflow
- API reference: https://dariomory.github.io/trendflow-js/
- Python sibling: https://pypi.org/project/trendflow-py/
- Created by: Dario Mory | GitHub https://github.com/dariomory
- Free software: MIT License
Install
npm install trendflow
Requires Node.js 18+ (uses the global fetch). Ships ESM and CommonJS with bundled type declarations.
Usage
import { Client, Region, Timeframe, Resolution, SearchProperty, ExportFormat } from "trendflow";
// Initialize client (optional config)
const tf = new Client({ language: "en", timeout: 10_000 });
// --- Const objects 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
const data = await tf.interestOverTime(
["Python", "JavaScript", "Rust"],
Timeframe.PAST_YEAR,
Region.US,
);
console.log(data.keywords); // ["Python", "JavaScript", "Rust"]
console.log(data.granularity); // "weekly"
console.log(data.points); // TrendPoint[] — { date: Date, scores: Record<string, number> }
// Regional breakdown (region defaults to Region.US)
const regional = await tf.interestByRegion("Python", Resolution.COUNTRY);
for (const row of regional.rows) {
console.log(row.label, row.value);
}
// Trending searches right now (any country code, or omit for worldwide)
const trending = await tf.trendingNow(Region.US);
for (const item of trending.results) {
console.log(item.title, item.growth, item.volume, item.traffic);
// "fifa world cup 2026" 3650 6 "+3,650%"
}
// Related queries (region defaults to worldwide)
const related = await tf.relatedQueries("machine learning", { region: Region.GB });
for (const query of related.top) console.log(query.term, query.value);
for (const query of related.rising) console.log(query.term, query.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
const jaguar = await tf.interestOverTime(["jaguar"], "2023-01-01 2023-06-30", "US-CA", {
category: 47, // Autos & Vehicles — disambiguates without needing a topic id
searchProperty: SearchProperty.YOUTUBE,
});
// --- Exports ---
data.toArray(); // [{ date: Date, Python: 80, ... }] — plain objects, the JS answer to DataFrames
data.toJSON(); // same rows with ISO 8601 date strings (also drives JSON.stringify)
data.toCSV(); // CSV text
// Node.js only — writes UTF-8 to disk
await data.export(ExportFormat.CSV, "trends.csv");
await data.export(ExportFormat.JSON, "trends.json");
Errors
Failed requests throw ResponseError, or TooManyRequestsError (a subclass) on HTTP 429.
Both carry .status and the raw .response.
import { TooManyRequestsError } from "trendflow";
try {
await tf.interestOverTime(["Python"], Timeframe.PAST_YEAR, Region.US);
} catch (error) {
if (error instanceof TooManyRequestsError) {
// Google is rate-limiting this IP — back off and retry later.
}
}
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 |
const rss = await tf.trendingNow(Region.US, { backend: "rss" });
rss.source; // "rss"
rss.results[0].articles;
// [{ 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.
const topics = await tf.suggestions("artificial intelligence");
// [{ mid: "/m/0mkz", title: "Artificial intelligence", type: "Professional field" }]
const data = await tf.interestOverTime(
[topics[0].mid, "artificial intelligence"],
Timeframe.PAST_YEAR,
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 trendingNow(). type disambiguates same-name entities
("Nike" returns both the company and the goddess) and is null 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:
- User-Agent. Google returns
429to the default agent strings Node HTTP clients send, no matter how few requests you have made. This library sends a browser User-Agent by default for exactly that reason — if you overrideheaders, keep a realistic one. - IP reputation. Once an IP is flagged, every request gets
429regardless 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 { Client, Region, Timeframe } from "trendflow";
const tf = new Client({
proxies: [
"http://user:pass@gate.decodo.com:7000",
"http://user:pass@gate.decodo.com:7000",
],
maxProxyAttempts: 3, // defaults to the pool size, capped at 5
onProxyRotate: ({ attempt, error }) => console.warn(`rotated after ${attempt}:`, error),
});
const data = await tf.interestOverTime(["Python"], Timeframe.PAST_YEAR, Region.US);
console.log(tf.currentProxy); // the proxy that answered
Proxy support needs undici, an optional peer
dependency — npm install undici. 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 the
UnknownRpcError raised when Google renames a batchexecute RPC id.
Where to get proxies
Residential proxies are what actually clears Google's 429. Verified against this library:
| 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 |
const tf = new Client({
proxies: ["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
maxProxyAttempts is for.
Bringing your own client
For logging, caching, or custom routing, pass a fetch instead (mutually exclusive with
proxies — the library will tell you if you pass both):
import { ProxyAgent, fetch as undiciFetch } from "undici";
const agent = new ProxyAgent("http://user:pass@proxy.example.com:7000");
const tf = new Client({
fetch: ((input, init = {}) =>
undiciFetch(input, { ...init, dispatcher: agent })) as typeof globalThis.fetch,
});
Browser / Next.js
Every method except export() works anywhere fetch does, but Google Trends sends no CORS
headers — calls from browser JavaScript will be blocked. Use this library server-side
(Route Handlers, Server Actions, API routes) and pass results to the client.
MCP server
An MCP server ships alongside the library as trendflow-mcp, so agents can query
Google Trends directly. It's a separate package — the library keeps its zero runtime
dependencies.
claude mcp add trendflow -- npx -y trendflow-mcp
Six tools (search_topics, get_interest_over_time, get_interest_by_region,
get_related_queries, get_trending_now, research_trend) and two resources. See
mcp/README.md.
Feature Parity
Current: trendflow-py 0.2.0 · trendflow 0.1.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) | ✅ | ✅ |
| 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 | ✅ |
| MCP server | 🔜 planned | ✅ trendflow-mcp |
| 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. This library
calls the batchexecute RPC that trends.google.com itself uses instead — as does
trendflow-py from 0.2.0 — and it returns more
than the old endpoint did:
const trending = await tf.trendingNow(Region.US);
// { title: "fifa world cup 2026", growth: 3650, volume: 6, traffic: "+3,650%", articles: [] }
Three practical wins over the old endpoint:
- Growth and volume, not just titles.
growthis the percentage rise over the window,volumea relative search-volume index. - Any country code, not the 16 hardcoded names the old endpoint required — and worldwide works, which it previously refused.
- No cookie, and far looser rate limiting. This RPC answers on IPs that get a
429from the widgetdata endpoints, sotrendingNow()often works with no proxy at all.
articles is empty on this backend — the RPC carries no article links. Pass
{ backend: "rss" } to get the news articles behind each trend instead.
The window is selectable via TrendingWindow:
import { TrendingWindow } from "trendflow";
await tf.trendingNow(Region.US, { window: TrendingWindow.RISING }); // default: fastest-growing
await tf.trendingNow(Region.US, { window: TrendingWindow.TOP }); // highest-volume
window is an undocumented Google parameter. Only these two values have behaviour worth
naming; other integers between 4 and 12 also return data over varying recency windows, and
you can pass one as a raw number.
Not implemented: captcha-gated RPCs
The same batchexecute endpoint exposes a higher-precision timeseries (floating-point
values rather than the rounded 0-100 the public API returns) and keyword-scoped related
queries. Both require a reCAPTCHA Enterprise token and return an empty payload without one,
so this library does not implement them — that data remains available through
interestOverTime() and relatedQueries(), which use the documented widgetdata endpoints.
API mapping
| Python | JavaScript |
|---|---|
interest_over_time() | interestOverTime() |
interest_by_region() | interestByRegion() |
trending_now() | trendingNow() |
related_queries() | relatedQueries() |
to_dataframe() | toArray() |
export(fmt, path) | export(fmt, path) — Node only, plus toCSV() / toJSON() |
Notable differences:
- Everything is async. All four query methods return promises.
timeoutis milliseconds (JS convention), not seconds.- Enums are
as constobjects, soRegion.USis the string"US"and any valid string literal is accepted where the type is expected. - Results are plain typed objects. Only
InterestOverTimeResultis a class, because it carries the conversion methods; the rest are interfaces.
Development
git clone git@github.com:dariomory/trendflow-js.git
cd trendflow-js
npm install
npm test # vitest — 60 tests, fully offline against a stubbed fetch
npm run qa # typecheck + test + build
The unit tests never touch the network. To check the real endpoints:
npm run build && npm run smoke
TRENDFLOW_PROXY_URL=http://user:pass@host:7000 npm run smoke # via a proxy
Author
Trendflow JS was created in 2026 by Dario Mory.
Tools it offers (8)
What this server listed when Ahel dialed its public endpoint in Sep 2026, with no key and no account of yours. The names are the server’s own.
list_regionssearch_topicsget_interest_over_timeget_interest_by_regionget_related_queriesget_trending_nowresearch_trendresearch_bulk
Signals
- GitHub stars
- 2
- Last commit
- Sep 2026
Advanced
- Delivery
- trendflow MCP server → your Ahel connector (mcp.ahel.ai) → your AI.
- Item type
- mcp-server
- Key
dev-mory-trendflow- Source
- github.com/dariomory/trendflow-js
- Hosted endpoint
https://trendflow.mory.dev/mcp
github.com/dariomory/trendflow-js