TrendFlow

dev.mory由 mory.dev 發布官方發布者v0.1.0更新於 Oct 24, 2026

Google Trends data: interest over time, by region, related queries, and trending now.

已驗證Streamable HTTP可網頁執行網頁搜尋與抓取資料與分析

概覽

AI 產生的概覽

遠端 MCP 伺服器,讓助理取得 Google Trends 資料:隨時間變化的搜尋熱度、地區分布、相關查詢與目前熱門搜尋。

可以做什麼
  • 列出 Google Trends 支援的地理代碼,可依國家及下層區域查詢
  • 將名稱解析為 Google Trends 主題 ID,並區分同名實體
  • 取得一個或多個詞彙隨時間變化的相對搜尋熱度,並依地區細分
  • 找出與關鍵字相關的熱門與上升查詢,用於關鍵字與 SEO 研究
  • 列出某國目前暴增的搜尋,含成長與熱度數據或新聞文章
  • 對單一詞彙做完整研究,或一次呼叫研究最多十個主題
適用情境
適合回答某事物有多熱門、熱度在上升還是下降、多個詞彙如何比較、在哪些地區較受歡迎,以及現在什麼正在流行。也適用於關鍵字挖掘、SEO 與內容研究。
執行需求
需要一個遠端 streamable HTTP 端點;未宣告任何套件、環境變數或請求標頭。伺服器代你查詢 Google Trends,結果受 Google 限流影響。
安裝前請注意
查詢會經由提供方的端點送往 Google Trends,你詢問的搜尋詞會離開本機。Google 限流嚴格,請求可能回傳 429 錯誤。批次研究每次最多十個主題,且每個主題依各自尺度計分,彼此不可直接比較。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 TrendFlow,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Web executable,透過 Streamable HTTP。 遠端服務在工作區中設定後即可從網頁執行環境執行。

交給代理程式安裝

把這段提示詞貼上到 Claude Code、Codex、Cursor 等代理程式,或 SourceWeft 對話中。代理程式會讀取該伺服器的安裝指南,先向你展示發布者、端點和登入方式,經你同意後再把它新增到你的客戶端。金鑰不會寫進任何命令。

閱讀 https://sourceweft.com/mcp/dev.mory%2Ftrendflow/install.md,並按其中說明把這個 MCP 伺服器接入我的客戶端。修改任何設定之前,先告訴我它的發布者、端點和登入方式,等我確認。不要把 API 金鑰寫進命令或我會提交的檔案;問我金鑰應從哪裡取得。

檢視代理程式所依據的安裝指南

其他 MCP 客戶端

把它新增到你客戶端的 mcpServers 設定中。

{
  "mcpServers": {
    "trendflow": {
      "type": "http",
      "url": "https://trendflow.mory.dev/mcp"
    }
  }
}

README

[Trendflow JS logo]

Trendflow JS

[npm version] [CI] [docs] [trendflow-js MCP server]

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.

Install

bash
npm install trendflow

Requires Node.js 18+ (uses the global fetch). Ships ESM and CommonJS with bundled type declarations.

Usage

ts
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 timeconst 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 2023const 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 DataFramesdata.toJSON();   // same rows with ISO 8601 date strings (also drives JSON.stringify)data.toCSV();    // CSV text
// Node.js only — writes UTF-8 to diskawait 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.

ts
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)
items5010
payload~2 KB JSON~21 KB XML
growth % and volume✅❌ — buckets like "2000+"
news articles❌✅
window selection✅ignored by Google
worldwide✅❌ country only
ts
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.

ts
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:

  1. User-Agent. Google returns 429 to 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 override headers, keep a realistic one.
  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:

ts
import { Client, Region, Timeframe } from "trendflow";
const tf = new Client({  proxies: [    "http://user:[email protected]:7000",    "http://user:[email protected]: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:

[Decodo]

ProviderNotesEndpoint format
Decodo (formerly Smartproxy)Cheapest entry tier; pay-as-you-go available. Used to verify this library's live tests.http://user:[email protected]:7000
ts
const tf = new Client({  proxies: ["http://user:[email protected]: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):

ts
import { ProxyAgent, fetch as undiciFetch } from "undici";
const agent = new ProxyAgent("http://user:[email protected]: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.

bash
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.

FeaturePython — trendflow-pyJS — 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:

ts
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. growth is the percentage rise over the window, volume a 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 429 from the widgetdata endpoints, so trendingNow() 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:

ts
import { TrendingWindow } from "trendflow";
await tf.trendingNow(Region.US, { window: TrendingWindow.RISING }); // default: fastest-growingawait 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

PythonJavaScript
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.
  • timeout is milliseconds (JS convention), not seconds.
  • Enums are as const objects, so Region.US is the string "US" and any valid string literal is accepted where the type is expected.
  • Results are plain typed objects. Only InterestOverTimeResult is a class, because it carries the conversion methods; the rest are interfaces.

Development

bash
git clone [email protected]:dariomory/trendflow-js.gitcd trendflow-jsnpm install
npm test        # vitest — 60 tests, fully offline against a stubbed fetchnpm run qa      # typecheck + test + build

The unit tests never touch the network. To check the real endpoints:

bash
npm run build && npm run smokeTRENDFLOW_PROXY_URL=http://user:pass@host:7000 npm run smoke   # via a proxy

Author

Trendflow JS was created in 2026 by Dario Mory.

來源:README.md,提交 a7ccd05

工具

8

list_regions

List regions

Returns the geo codes Google Trends accepts. With no argument it lists every country; given a country code it lists that country's sub-regions — US states, German states, UK nations — each as a code and a name. Any code returned here can be passed as the `region` argument of the other tools.

輸入參數country

search_topics

Search topics

Resolves a name — a company, product, person, technology, or concept — to its Google Trends topic id (`mid`), with a `type` field that distinguishes same-name entities such as Nike the company from Nike the goddess. A topic aggregates every spelling and translation of one concept, so it measures considerably more search activity than a literal phrase: the topic for "artificial intelligence" scores 62 where the literal string scores 1. The other TrendFlow tools accept a topic id anywhere they accept a keyword.

輸入參數query必填

get_interest_over_time

Interest over time

Get relative search interest for one or more terms over a historical period. Use this when the user asks how popular something is, whether it is rising or falling, or how several things compare — passing multiple keywords compares them on one scale. Values are normalized relative interest (0-100 within the result set), not absolute search volume.

輸入參數regioncategorykeywords必填timeframesearchProperty

get_interest_by_region

Interest by region

Break down search interest for one term by geography. Use this when the user asks where something is popular, or wants a regional or city-level comparison. Values are normalized relative interest (0-100 within the result set).

輸入參數regionkeyword必填categorytimeframeresolutionsearchProperty

research_trend

Research a trend

Returns a full picture of one term in a single call: interest over time, where it is most searched, and the queries people search alongside it. Suited to open-ended research on a topic rather than one narrow question. Each section is fetched independently, so a partial result is normal: any section that fails carries an `error` instead of data and the rest still returns.

輸入參數regionkeyword必填

research_bulk

Research many trends

Researches up to 10 topics in a single call, each with the same full picture as `research_trend`: interest over time, where it is most searched, and related queries. Each topic is looked up on its own scale, so they are not comparable to one another. Use this when you need data across many topics — a long or rich research pass — instead of one tool call per topic. Each section is fetched independently, so a partial result is normal: any section that fails carries an `error` instead of data and the rest still returns.

輸入參數regiontopics必填

版本歷史

1
  1. v0.1.0最新Sep 16, 2026

評分

已滿足適用檢查項的 100%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通必需25 分
  • 已宣告認證方式必需10 分
  • 已列出工具必需15 分
  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分