# EarlyWave API — full documentation > EarlyWave reads news from tens of thousands of sources and groups articles into stories (trends). Its read-only API gives developers and AI agents the trends detected across every source — filtered by the country a story is about, the countries of its publishers, category, stage and reach — with their articles and coverage by country, plus the account's own monitors, feeds, folders, articles and saved items. REST: https://api.earlywave.ai/api/v1 (Bearer key with the `rest` scope). MCP: https://api.earlywave.ai/mcp (Streamable HTTP, stateless, Bearer key with the `mcp` scope). Included with Pro and Max; 60 or 300 requests a minute and 10,000 or 50,000 a month by plan. OpenAPI 3.1: https://api.earlywave.ai/api/v1/openapi.json ## Operations Base URL https://api.earlywave.ai/api/v1. Every operation is a GET and also an MCP tool on https://api.earlywave.ai/mcp. - GET /trends · MCP `list_trends` · List trends. Stories EarlyWave detects across every source it reads, strongest signal first. Filter by category, stage, the country a story is about, the country of its publishers, reach, or how recently it started. Parameters: `query`, `category`, `stage`, `country`, `publisher_country`, `reach`, `since_hours`, `sort`, `limit`, `offset`. Returns TrendList. - GET /trends/{trend_id} · MCP `get_trend` · Get a trend. A trend with its summary, countries, languages, entities, keywords and its 10 latest articles. Parameters: `trend_id` (required). Returns TrendDetail. - GET /trends/{trend_id}/articles · MCP `get_trend_articles` · List a trend's articles. Every article of a trend with its source (title, site, country), newest or oldest first. Parameters: `trend_id` (required), `sort`, `limit`, `offset`. Returns TrendArticleList. - GET /trends/{trend_id}/coverage · MCP `get_trend_coverage` · Get where a trend is covered. Publisher countries and languages of a trend (sources = distinct sites) and its last 7 days hour by hour. Parameters: `trend_id` (required). Returns TrendCoverage. - GET /monitors · MCP `list_monitors` · List your monitors. Your monitors (archived ones excluded), with their counts and next check. Parameters: —. Returns MonitorList. - GET /monitors/{monitor_id}/results · MCP `get_monitor_results` · Get a monitor's results. What a monitor found over the last days (newest first), with its sources and the current answers to its questions. Parameters: `monitor_id` (required), `days`, `query`, `limit`. Returns MonitorResults. - GET /feeds · MCP `find_feeds` · List your subscribed feeds. Lists the RSS feeds the account is subscribed to, optionally filtered by a keyword (title, description, address) or a domain. Parameters: `query`, `domain`, `limit`, `offset`. Returns FeedList. - GET /feeds/discover · MCP `discover_feeds` · Search the feed catalogue. Searches every feed EarlyWave reads (not only your subscriptions), most-followed first. Parameters: `query` (required), `language`, `country`, `limit`, `offset`. Returns FeedDiscoveryList. - GET /feeds/{feed_id} · MCP `get_feed` · Get one of your feeds. Details of a feed you are subscribed to, with its 24-hour and 7-day article counts. Parameters: `feed_id` (required). Returns FeedDetail. - GET /feeds/{feed_id}/items · MCP `get_feed_items` · List a feed's articles. Articles of one of your feeds, newest first. Parameters: `feed_id` (required), `limit`, `offset`. Returns FeedItemList. - GET /categories · MCP `list_categories` · List your folders. Your feed folders, with the number of feeds in each. Parameters: —. Returns FolderList. - GET /categories/{category_id}/items · MCP `get_category_items` · List a folder's articles. Articles from the feeds of one of your folders, newest first. Parameters: `category_id` (required), `limit`, `offset`. Returns FolderItemList. - GET /items · MCP `get_all_items` · List all your articles. Articles from all your feeds, newest first. Parameters: `limit`, `offset`. Returns ItemList. - GET /articles/search · MCP `search_articles` · Search your articles. Articles of your feeds whose title, description, summary or keywords contain the query, newest first. Parameters: `query` (required), `limit`, `offset`. Returns ArticleSearchList. - GET /articles/{article_id} · MCP `get_article` · Get an article. One article from your feeds, with its text (up to 10,000 characters), summary and keywords. Parameters: `article_id` (required). Returns Article. - GET /saved · MCP `list_saved` · List your saved items. Articles, pages and searches you saved, newest first. Parameters: `type`, `limit`, `offset`. Returns SavedList. - GET /discover · MCP `discover_items` · Search all recent articles. Recent articles from every source EarlyWave reads (not only your feeds), by keywords or latest first. Parameters: `query`, `language`, `hours_back`, `limit`, `offset`. Returns DiscoverItemList. - GET /discover/keywords · MCP `discover_top_keywords` · Top keywords. The most frequent keywords, entities, themes or places in recent articles. Parameters: `type`, `hours_back`, `limit`. Returns KeywordList. --- # EarlyWave API EarlyWave reads news from tens of thousands of sources and groups the articles into stories, called trends. The API gives you read access to those trends and to your own data: monitors, feeds, folders, articles and saved items. There are two ways in, over the same 18 read-only operations: - **REST** — `https://api.earlywave.ai/api/v1`, JSON over HTTPS, one `GET` per operation. - **MCP** — `https://api.earlywave.ai/mcp`, the same operations as tools for Claude, Cursor, VS Code and any other MCP client. Both use the same keys and the same two counters (a minute window and a monthly quota by plan: 10,000 requests a month on Pro, 50,000 on Max) and return the same data. ## Start here 1. [Quickstart](https://www.earlywave.ai/developers/docs/quickstart) — a key, a first request and an MCP client in five minutes. 2. [Authentication](https://www.earlywave.ai/developers/docs/authentication) — keys, scopes and access. 3. [Errors, limits and paging](https://www.earlywave.ai/developers/docs/errors-and-limits) — what every response has in common. 4. [MCP server](https://www.earlywave.ai/developers/docs/mcp) — client setup and the list of tools. 5. [API reference](https://www.earlywave.ai/developers/reference) — every operation, parameter and field. ## What you can read | Area | REST | MCP tool | Returns | |---|---|---|---| | Trends | [`GET /trends`](https://www.earlywave.ai/developers/reference#listTrends) | `list_trends` | List trends | | Trends | [`GET /trends/{trend_id}`](https://www.earlywave.ai/developers/reference#getTrend) | `get_trend` | Get a trend | | Trends | [`GET /trends/{trend_id}/articles`](https://www.earlywave.ai/developers/reference#listTrendArticles) | `get_trend_articles` | List a trend's articles | | Trends | [`GET /trends/{trend_id}/coverage`](https://www.earlywave.ai/developers/reference#getTrendCoverage) | `get_trend_coverage` | Get where a trend is covered | | Monitors | [`GET /monitors`](https://www.earlywave.ai/developers/reference#listMonitors) | `list_monitors` | List your monitors | | Monitors | [`GET /monitors/{monitor_id}/results`](https://www.earlywave.ai/developers/reference#getMonitorResults) | `get_monitor_results` | Get a monitor's results | | Feeds | [`GET /feeds`](https://www.earlywave.ai/developers/reference#listFeeds) | `find_feeds` | List your subscribed feeds | | Feeds | [`GET /feeds/discover`](https://www.earlywave.ai/developers/reference#discoverFeeds) | `discover_feeds` | Search the feed catalogue | | Feeds | [`GET /feeds/{feed_id}`](https://www.earlywave.ai/developers/reference#getFeed) | `get_feed` | Get one of your feeds | | Feeds | [`GET /feeds/{feed_id}/items`](https://www.earlywave.ai/developers/reference#listFeedItems) | `get_feed_items` | List a feed's articles | | Folders | [`GET /categories`](https://www.earlywave.ai/developers/reference#listFolders) | `list_categories` | List your folders | | Folders | [`GET /categories/{category_id}/items`](https://www.earlywave.ai/developers/reference#listFolderItems) | `get_category_items` | List a folder's articles | | Folders | [`GET /items`](https://www.earlywave.ai/developers/reference#listItems) | `get_all_items` | List all your articles | | Articles | [`GET /articles/search`](https://www.earlywave.ai/developers/reference#searchArticles) | `search_articles` | Search your articles | | Articles | [`GET /articles/{article_id}`](https://www.earlywave.ai/developers/reference#getArticle) | `get_article` | Get an article | | Saved | [`GET /saved`](https://www.earlywave.ai/developers/reference#listSaved) | `list_saved` | List your saved items | | Discover | [`GET /discover`](https://www.earlywave.ai/developers/reference#discoverItems) | `discover_items` | Search all recent articles | | Discover | [`GET /discover/keywords`](https://www.earlywave.ai/developers/reference#discoverKeywords) | `discover_top_keywords` | Top keywords | Trends are EarlyWave's catalogue: every key sees the same trends, whatever its account follows. Everything else — monitors, feeds, folders, articles, saved items — belongs to the account that owns the key. ## Access The REST API and the MCP server are included with Pro (10,000 requests a month) and Max (50,000). See [plans](https://www.earlywave.ai/pricing). Create keys in EarlyWave under **Settings › API & MCP** ([open it](https://www.earlywave.ai/settings/developers)). ## Conventions - JSON in, JSON out. Field and parameter names are `snake_case`, the same on REST and MCP. - Timestamps are ISO 8601 in UTC, for example `2026-10-19T08:55:31.000Z`. - Countries are ISO 3166-1 alpha-2 codes (`FR`, `US`). Languages are short codes such as `fr` or `en`. - Every list takes `limit` (1–100, default 20) and `offset`, and answers `has_more`. - Every error is `{"error": {"type", "code", "message"}}` with a stable `type`. - The API is read-only. Monitors, feeds and folders are created in the app or with Ask EarlyWave. ## Machine-readable - OpenAPI 3.1: [`https://api.earlywave.ai/api/v1/openapi.json`](https://api.earlywave.ai/api/v1/openapi.json) — the contract. Each operation carries its MCP tool name in `x-mcp-tool`. - [`/llms.txt`](https://www.earlywave.ai/llms.txt) and [`/llms-full.txt`](https://www.earlywave.ai/llms-full.txt) — these docs for coding agents. - Every docs page is also served as Markdown: add `.md` to its address, for example [`/developers/docs/quickstart.md`](https://www.earlywave.ai/developers/docs/quickstart.md). Source: https://www.earlywave.ai/developers/docs --- # Quickstart Create a key, list the trends about France, then plug the same data into your AI assistant. It takes about five minutes. You need an EarlyWave account on a plan that includes the API (Pro or Max, see [plans](https://www.earlywave.ai/pricing)). ## 1. Create a key 1. Open EarlyWave › **Settings › API & MCP** ([open it](https://www.earlywave.ai/settings/developers)). 2. Create a key and give it a name you will recognise later, such as `laptop` or `slack-bot`. 3. Pick its scopes: `rest` for the REST API, `mcp` for the MCP server, or both. 4. Copy the key. It starts with `ew_live_` and is shown only once. Keep it in an environment variable rather than in your code: ```bash export EARLYWAVE_API_KEY="ew_live_..." ``` ## 2. Make a first request List the three strongest stories about France right now: ```bash curl "https://api.earlywave.ai/api/v1/trends?country=FR&limit=3" \ -H "Authorization: Bearer $EARLYWAVE_API_KEY" ``` The answer is a page of trends (shortened here to one): ```json { "trends": [ { "id": 48213, "title": "Rail strike disrupts travel across France", "url": "https://www.earlywave.ai/trends/trend/48213", "status": "active", "stage": "accelerating", "category": "society", "reach": "national", "subject_countries": ["FR"], "publisher_countries": ["FR", "BE", "CH"], "item_count": 142, "source_count": 61, "signal_score": 74.2, "velocity_1h": 9, "velocity_24h": 118, "started_at": "2026-10-18T05:42:00.000Z", "earliest_item_at": "2026-10-18T05:42:00.000Z", "first_seen_at": "2026-10-18T06:10:12.000Z", "last_activity_at": "2026-10-19T08:55:31.000Z" } ], "total": 37, "limit": 3, "offset": 0, "has_more": true } ``` `has_more` is `true`: ask for the next page with `offset=3`. Every response also carries `X-RateLimit-Remaining` and an `X-Request-Id`. Open one trend, then see where it is covered: ```bash curl "https://api.earlywave.ai/api/v1/trends/48213" \ -H "Authorization: Bearer $EARLYWAVE_API_KEY" curl "https://api.earlywave.ai/api/v1/trends/48213/coverage" \ -H "Authorization: Bearer $EARLYWAVE_API_KEY" ``` The [Trends guide](https://www.earlywave.ai/developers/docs/trends) walks through every filter and field. ### The same request in code JavaScript (Node 18+, Deno, Bun): ```js const res = await fetch("https://api.earlywave.ai/api/v1/trends?country=FR&limit=3", { headers: { Authorization: `Bearer ${process.env.EARLYWAVE_API_KEY}` }, }); const body = await res.json(); if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message}`); for (const trend of body.trends) console.log(trend.source_count, trend.title); ``` Python: ```python import os import requests res = requests.get( "https://api.earlywave.ai/api/v1/trends", params={"country": "FR", "limit": 3}, headers={"Authorization": f"Bearer {os.environ['EARLYWAVE_API_KEY']}"}, timeout=30, ) body = res.json() if not res.ok: raise RuntimeError(f"{body['error']['code']}: {body['error']['message']}") for trend in body["trends"]: print(trend["source_count"], trend["title"]) ``` ## 3. Connect an MCP client With a key that has the `mcp` scope, add EarlyWave to Claude Code in one line: ```bash claude mcp add --transport http earlywave https://api.earlywave.ai/mcp \ --header "Authorization: Bearer YOUR_KEY" ``` Then ask, for example: *"What are the breakout stories about Germany since yesterday, and which countries cover the first one?"* The assistant calls `list_trends` and `get_trend_coverage` for you. Cursor, VS Code, Claude Desktop and other clients: see [MCP server](https://www.earlywave.ai/developers/docs/mcp). ## Next - [Authentication](https://www.earlywave.ai/developers/docs/authentication) — scopes, legacy keys, access errors. - [Errors, limits and paging](https://www.earlywave.ai/developers/docs/errors-and-limits) — what to do with a `429`. - [API reference](https://www.earlywave.ai/developers/reference) — every operation. Source: https://www.earlywave.ai/developers/docs/quickstart --- # Authentication Every request carries an API key in the `Authorization` header, as a Bearer token. There is no other way to authenticate: keys in the query string are refused, so they never land in access logs. ```http GET /api/v1/trends HTTP/1.1 Host: api.earlywave.ai Authorization: Bearer ew_live_... ``` The OpenAPI document (`/api/v1/openapi.json`) is the only public address: it needs no key. ## Keys Create and revoke keys in EarlyWave › **Settings › API & MCP** ([open it](https://www.earlywave.ai/settings/developers)). - A key looks like `ew_live_` followed by 32 letters and digits. - It is shown **once**, when you create it. EarlyWave stores only a hash of it and cannot show it again. Lost it? Create a new one and revoke the old one. - Give each key a name (the machine or the integration that uses it), so you know which one to revoke. - Settings shows when each key was last used and how many requests your keys made each day. - Keys created before October 2026 (`sk-smrz_…`) keep working. New keys use the `ew_live_` format. Treat a key like a password. Keep it on a server or in an environment variable; never ship it in a web page or a mobile app. ## Scopes A key carries one or both scopes: | Scope | Opens | Used by | |---|---|---| | `rest` | `https://api.earlywave.ai/api/v1/*` | your code, scripts, notebooks | | `mcp` | `https://api.earlywave.ai/mcp` | Claude, Cursor, VS Code and other MCP clients | A key without the right scope is refused: `403` with code `missing_scope` on the REST API, `401` on the MCP server. One key per integration, with only the scope it needs, keeps a leak small. ## Access The REST API and the MCP server are included with Pro and Max ([plans](https://www.earlywave.ai/pricing)). Access is checked on every request, not only when the key is created: if the account's plan ends, its keys stop working until it is renewed. All keys of an account share one rate limit and one monthly quota, REST and MCP together: 60 requests a minute and 10,000 a month on Pro, 300 and 50,000 on Max. See [Errors, limits and paging](https://www.earlywave.ai/developers/docs/errors-and-limits). ## What a key can read - **Trends** (`/trends…`) are EarlyWave's catalogue, built from every source it reads. Any key sees the same trends, whatever its account follows. - **Everything else** is the account's own data: its monitors, the feeds it follows, its folders, the articles of those feeds, its saved items. A feed, article, folder or monitor of another account answers `404`. - **Discover** (`/discover`, `/feeds/discover`) searches every source EarlyWave reads, not only the account's feeds. ## Authentication errors | Status | `error.type` | `error.code` | Meaning | |---|---|---|---| | 401 | `authentication` | `missing_api_key` | No `Authorization: Bearer …` header. | | 401 | `authentication` | `invalid_api_key` | Unknown or revoked key. | | 403 | `permission` | `missing_scope` | The key has no `rest` scope. | | 403 | `permission` | `feature_not_available` | The account's plan does not include API access. | | 403 | `permission` | `account_inactive` | The account is inactive. | | 500 | `server` | `auth_unavailable` | The key could not be checked. Retry shortly. | Example: ```json { "error": { "type": "authentication", "code": "invalid_api_key", "message": "Unknown or revoked API key." } } ``` The MCP server answers authentication failures before the MCP session starts, with a plain JSON body: `401` for a missing or unknown key or a key without the `mcp` scope, `403` when the account's plan does not include MCP or the account is inactive. Source: https://www.earlywave.ai/developers/docs/authentication --- # Errors, limits and paging What every response of the REST API has in common: one error shape, a rate limit with its headers, the same paging on every list, and a request ID. ## Errors A failed request answers an HTTP error status and this body: ```json { "error": { "type": "not_found", "code": "trend_not_found", "message": "Trend not found." } } ``` - `type` is the stable family. Branch on it. - `code` is the precise reason. New codes may appear; treat an unknown one by its `type`. - `message` is for people and logs. Its wording may change. - `details` is present on some errors, such as invalid parameters. | `type` | Status | When | |---|---|---| | `invalid_request` | 400 | A parameter is missing, malformed or out of range. | | `authentication` | 401 | No key, or an unknown or revoked key. | | `permission` | 403 | The key lacks the scope, or the account lacks API access. | | `not_found` | 404 | No such trend, or the feed, article, folder or monitor is not the account's. Also an unknown path. | | `rate_limit` | 429 | Over the per-minute limit, or the month's quota is spent. | | `server` | 500 | Something failed on EarlyWave's side. | ### Codes you will meet | `code` | `type` | |---|---| | `invalid_parameters` | `invalid_request` | | `invalid_category`, `invalid_stage`, `invalid_reach` | `invalid_request` | | `invalid_country`, `invalid_publisher_country` | `invalid_request` | | `missing_api_key`, `invalid_api_key` | `authentication` | | `missing_scope`, `feature_not_available`, `account_inactive` | `permission` | | `trend_not_found`, `feed_not_found`, `article_not_found`, `category_not_found`, `monitor_not_found` | `not_found` | | `route_not_found` | `not_found` | | `rate_limit_exceeded` | `rate_limit` | | `quota_exceeded` | `rate_limit` | | `internal_error`, `auth_unavailable` | `server` | ### Invalid parameters Parameters are checked before anything runs. `details` lists every problem, one per parameter: ```bash curl "https://api.earlywave.ai/api/v1/trends?limit=500&sort=popular" \ -H "Authorization: Bearer $EARLYWAVE_API_KEY" ``` ```json { "error": { "type": "invalid_request", "code": "invalid_parameters", "message": "sort: Invalid option: expected one of \"signal\"|\"recent\"|\"sources\"; limit: Too big: expected number to be <=100", "details": [ { "param": "sort", "message": "sort: Invalid option: expected one of \"signal\"|\"recent\"|\"sources\"" }, { "param": "limit", "message": "limit: Too big: expected number to be <=100" } ] } } ``` A few rules make parameters forgiving: - An empty value (`?category=`) counts as absent. - Unknown parameters are ignored. - Numbers may be sent as strings (`limit=5`); they are converted. - Comma-separated lists (`category=politics,economy`) are checked value by value: an unknown value answers `invalid_category` and lists the allowed ones. ## Rate limit and monthly quota Each plan sets two counters, shared by all the account's keys and by REST and MCP together: one REST call or one MCP tool call counts as one request. | Plan | Per minute | Per calendar month | |---|---|---| | Pro | 60 requests | 10,000 requests | | Max | 300 requests | 50,000 requests | Minute windows are fixed: each one is a calendar minute, and the counter resets at the start of the next minute. The monthly quota resets on the 1st at 00:00 UTC. A refused request counts against neither. Every authenticated response carries: | Header | Meaning | |---|---| | `X-RateLimit-Limit` | Requests allowed per minute. | | `X-RateLimit-Remaining` | Requests left in the current minute. | | `X-RateLimit-Reset` | Unix time, in seconds, when the minute window resets. | | `X-Quota-Limit` | Requests allowed this calendar month (`-1` on a contract without a monthly limit). | | `X-Quota-Remaining` | Requests left this month. | | `X-Quota-Reset` | Unix time, in seconds, of the 1st of next month. | Over either limit, the API answers `429` with `error.type = "rate_limit"` and a `Retry-After` header in seconds. The code says which counter refused: `rate_limit_exceeded` for the minute, `quota_exceeded` for the month. ```http HTTP/1.1 429 Too Many Requests Retry-After: 23 X-RateLimit-Limit: 60 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1792400460 X-Quota-Limit: 10000 X-Quota-Remaining: 8211 X-Quota-Reset: 1793577600 ``` Wait `Retry-After` seconds, then retry; a spent month waits for the 1st, or a higher plan. On MCP, a tool call over a limit returns a tool error that names the counter; the assistant can retry after the minute ends. ## Paging Every list takes the same two parameters: | Parameter | Default | Range | |---|---|---| | `limit` | 20 | 1–100 | | `offset` | 0 | 0 and up | and answers `limit`, `offset` and `has_more`. Most lists also answer `total`, the number of matching results. ```json { "trends": [ … ], "total": 37, "limit": 20, "offset": 0, "has_more": true } ``` To read everything, call again with `offset + limit` while `has_more` is `true`: ```js let offset = 0; const all = []; for (;;) { const res = await fetch(`https://api.earlywave.ai/api/v1/trends?country=FR&limit=100&offset=${offset}`, { headers: { Authorization: `Bearer ${process.env.EARLYWAVE_API_KEY}` }, }); const page = await res.json(); all.push(...page.trends); if (!page.has_more) break; offset += page.limit; } ``` A few operations differ: - `GET /monitors` and `GET /categories` return everything at once, with `total`. - `GET /monitors/{monitor_id}/results` takes `limit` but no `offset`. Narrow the period (`days`) or the text (`query`) instead. - `GET /discover/keywords` takes `limit` only. Results can move between two calls: a trend gains articles and its rank changes. When you need one complete pass, narrow the filters so that a page of 100 holds everything, or page right away. ## Request ID Every response, success or error, carries an `X-Request-Id` header. Quote it when you report a problem: it points to that exact request in EarlyWave's logs. ## Retrying | Status | Retry? | |---|---| | 400, 401, 403, 404 | No. Fix the request, the key or the plan first. | | 429 | Yes, after `Retry-After` seconds. | | 500 | Yes, with a growing delay (1 s, 2 s, 4 s…), a few times at most. | Every operation is a read, so retrying a request never changes anything. Source: https://www.earlywave.ai/developers/docs/errors-and-limits --- # MCP server The Model Context Protocol (MCP) lets an AI assistant call tools on your behalf. EarlyWave's MCP server gives Claude, Cursor, VS Code and other MCP clients the same 18 read-only operations as the REST API: trends across every source, coverage by country, your monitors, feeds, folders, articles and saved items. | Setting | Value | |---|---| | Address | `https://api.earlywave.ai/mcp` | | Transport | Streamable HTTP, stateless, `POST` only | | Authentication | `Authorization: Bearer `, a key with the `mcp` scope | | Server name | `earlywave` | | Limit | 60 tool calls per minute and 10,000 a month on Pro (300 and 50,000 on Max), shared with the REST API | Create a key with the `mcp` scope in EarlyWave › **Settings › API & MCP** ([open it](https://www.earlywave.ai/settings/developers)). In the snippets below, replace `YOUR_KEY` with it. ## Client setup ### Claude Code ```bash claude mcp add --transport http earlywave https://api.earlywave.ai/mcp \ --header "Authorization: Bearer YOUR_KEY" ``` Check it with `claude mcp list`. Add `--scope user` to use it in every project. ### Cursor In `.cursor/mcp.json` (this project) or `~/.cursor/mcp.json` (every project): ```json { "mcpServers": { "earlywave": { "url": "https://api.earlywave.ai/mcp", "headers": { "Authorization": "Bearer YOUR_KEY" } } } } ``` ### VS Code In `.vscode/mcp.json`: ```json { "servers": { "earlywave": { "type": "http", "url": "https://api.earlywave.ai/mcp", "headers": { "Authorization": "Bearer YOUR_KEY" } } } } ``` The tools appear in Copilot Chat in agent mode. ### Claude Desktop Claude Desktop starts local servers, so it reaches EarlyWave through the `mcp-remote` bridge (Node.js required). In `claude_desktop_config.json` (Settings › Developer › Edit Config): ```json { "mcpServers": { "earlywave": { "command": "npx", "args": [ "-y", "mcp-remote", "https://api.earlywave.ai/mcp", "--header", "Authorization: Bearer ${EARLYWAVE_API_KEY}" ], "env": { "EARLYWAVE_API_KEY": "YOUR_KEY" } } } } ``` Restart Claude Desktop after saving. ### Windsurf and other clients Any client that speaks Streamable HTTP needs two things: the address `https://api.earlywave.ai/mcp` and the header `Authorization: Bearer YOUR_KEY`. A client that only starts local servers can use the `mcp-remote` bridge shown for Claude Desktop. ## Tools Each tool is one operation of the [REST API](https://www.earlywave.ai/developers/reference), with the same arguments and the same JSON answer, returned as text. | Tool | What it returns | Arguments | REST twin | |---|---|---|---| | `list_trends` | List trends. Stories EarlyWave detects across every source it reads, strongest signal first. Filter by category, stage, the country a story is about, the country of its publishers, reach, or how recently it started. | `query`, `category`, `stage`, `country`, `publisher_country`, `reach`, `since_hours`, `sort`, `limit`, `offset` | `GET /trends` | | `get_trend` | Get a trend. A trend with its summary, countries, languages, entities, keywords and its 10 latest articles. | `trend_id` (required) | `GET /trends/{trend_id}` | | `get_trend_articles` | List a trend's articles. Every article of a trend with its source (title, site, country), newest or oldest first. | `trend_id` (required), `sort`, `limit`, `offset` | `GET /trends/{trend_id}/articles` | | `get_trend_coverage` | Get where a trend is covered. Publisher countries and languages of a trend (sources = distinct sites) and its last 7 days hour by hour. | `trend_id` (required) | `GET /trends/{trend_id}/coverage` | | `list_monitors` | List your monitors. Your monitors (archived ones excluded), with their counts and next check. | — | `GET /monitors` | | `get_monitor_results` | Get a monitor's results. What a monitor found over the last days (newest first), with its sources and the current answers to its questions. | `monitor_id` (required), `days`, `query`, `limit` | `GET /monitors/{monitor_id}/results` | | `find_feeds` | List your subscribed feeds. Lists the RSS feeds the account is subscribed to, optionally filtered by a keyword (title, description, address) or a domain. | `query`, `domain`, `limit`, `offset` | `GET /feeds` | | `discover_feeds` | Search the feed catalogue. Searches every feed EarlyWave reads (not only your subscriptions), most-followed first. | `query` (required), `language`, `country`, `limit`, `offset` | `GET /feeds/discover` | | `get_feed` | Get one of your feeds. Details of a feed you are subscribed to, with its 24-hour and 7-day article counts. | `feed_id` (required) | `GET /feeds/{feed_id}` | | `get_feed_items` | List a feed's articles. Articles of one of your feeds, newest first. | `feed_id` (required), `limit`, `offset` | `GET /feeds/{feed_id}/items` | | `list_categories` | List your folders. Your feed folders, with the number of feeds in each. | — | `GET /categories` | | `get_category_items` | List a folder's articles. Articles from the feeds of one of your folders, newest first. | `category_id` (required), `limit`, `offset` | `GET /categories/{category_id}/items` | | `get_all_items` | List all your articles. Articles from all your feeds, newest first. | `limit`, `offset` | `GET /items` | | `search_articles` | Search your articles. Articles of your feeds whose title, description, summary or keywords contain the query, newest first. | `query` (required), `limit`, `offset` | `GET /articles/search` | | `get_article` | Get an article. One article from your feeds, with its text (up to 10,000 characters), summary and keywords. | `article_id` (required) | `GET /articles/{article_id}` | | `list_saved` | List your saved items. Articles, pages and searches you saved, newest first. | `type`, `limit`, `offset` | `GET /saved` | | `discover_items` | Search all recent articles. Recent articles from every source EarlyWave reads (not only your feeds), by keywords or latest first. | `query`, `language`, `hours_back`, `limit`, `offset` | `GET /discover` | | `discover_top_keywords` | Top keywords. The most frequent keywords, entities, themes or places in recent articles. | `type`, `hours_back`, `limit` | `GET /discover/keywords` | Lists take `limit` (up to 100) and `offset`, and answer `has_more`. Countries are ISO alpha-2 codes (`FR`). Answers longer than 50,000 characters are cut; ask for a smaller `limit`. ## Example prompts - *"What are the breakout stories in tech since yesterday?"* → `list_trends` with `category=tech`, `stage=breakout`, `since_hours=24`. - *"Which stories about France are covered abroad?"* → `list_trends` with `country=FR` and `reach=international,global`. - *"Who reported the rail strike first, and from which country?"* → `get_trend_articles` with `sort=oldest`. - *"In which countries and languages is this story covered, and when did it peak?"* → `get_trend_coverage`. - *"Summarise what my monitors found this week, with sources."* → `list_monitors`, then `get_monitor_results` with `days=7`. - *"Find French feeds about climate I don't follow yet."* → `discover_feeds` with `query=climate`, `country=FR`. - *"What did I save this week?"* → `list_saved`. The server also sends the assistant a short guide to its tools, so plain questions work: you rarely need to name a tool. ## Test it without a client The server is stateless: each `POST` stands alone, with no session to open first. List the tools with curl: ```bash curl https://api.earlywave.ai/mcp \ -H "Authorization: Bearer $EARLYWAVE_API_KEY" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' ``` Call one: ```bash curl https://api.earlywave.ai/mcp \ -H "Authorization: Bearer $EARLYWAVE_API_KEY" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"list_trends","arguments":{"country":"FR","limit":3}}}' ``` The tool's JSON answer is in `result.content[0].text`. ## Troubleshooting | Symptom | Cause | |---|---| | `401` | Missing or unknown key, or a key without the `mcp` scope. | | `403` | The account's plan does not include MCP, or the account is inactive. | | `405` on `GET` or `DELETE` | The server is stateless: it only answers `POST`. Use the Streamable HTTP transport, not SSE. | | `406` | The request must accept both `application/json` and `text/event-stream`. Clients do this for you. | | Tool error "Rate limit exceeded" | 60 calls per minute per account on Pro (300 on Max), REST included. Wait for the next minute. | | Tool error "Monthly quota exceeded" | 10,000 calls a month on Pro (50,000 on Max), REST included. It resets on the 1st; Max raises it. | | Tool error naming a parameter | A wrong argument, for example a three-letter country code. The message says which. | | No tools listed | Restart the client after editing its configuration, and check the key has the `mcp` scope. | Source: https://www.earlywave.ai/developers/docs/mcp --- # Trends A trend is a story: the articles that many sources publish about the same event, grouped by EarlyWave as they arrive. Trends are built from every source EarlyWave reads, so every key sees the same trends, whatever its account follows. Four operations cover them: | REST | MCP tool | Returns | |---|---|---| | `GET /trends` | `list_trends` | Trends, filtered and sorted. | | `GET /trends/{trend_id}` | `get_trend` | One trend with its summary, entities, keywords and 10 latest articles. | | `GET /trends/{trend_id}/articles` | `get_trend_articles` | Every article of a trend, with its source. | | `GET /trends/{trend_id}/coverage` | `get_trend_coverage` | Where a trend is covered: countries, languages, hour by hour. | ## List trends ```bash curl "https://api.earlywave.ai/api/v1/trends?country=FR&since_hours=24&limit=5" \ -H "Authorization: Bearer $EARLYWAVE_API_KEY" ``` The list holds active stories only. As on the Trends page, stories EarlyWave judged low quality and recurring formats (horoscopes, exchange rates, daily deals, lottery results) are left out. ### Filters All filters are optional and combine with AND. Lists of values are comma-separated and match any of them. | Parameter | Example | Keeps the trends… | |---|---|---| | `country` | `FR` | about this country (`subject_countries` contains it). | | `publisher_country` | `DE` | covered by at least 2 sources from this country (`publisher_countries`). | | `category` | `politics,economy` | in one of these categories. | | `stage` | `breakout` | at one of these stages: `emerging`, `accelerating`, `breakout`. | | `reach` | `international,global` | with one of these reaches: `local`, `national`, `international`, `global`. | | `since_hours` | `24` | that started within this many hours (1–720). | | `query` | `strike` | whose title contains this text (case-insensitive). | Categories: `ai`, `tech`, `business`, `economy`, `crypto`, `cyber`, `geopolitics`, `politics`, `energy`, `climate`, `health`, `science`, `space`, `society`, `sports`, `cinema`, `people`, `music`, `world`. Countries are ISO 3166-1 alpha-2 codes. A three-letter code or an unknown category answers `400` with the allowed values in the message. ### Sorting | `sort` | Order | |---|---| | `signal` (default) | Strongest signal first (`signal_score`). | | `recent` | Latest activity first (`last_activity_at`). | | `sources` | Most distinct sources first (`source_count`). | ### The fields of a trend ```json { "id": 48213, "title": "Rail strike disrupts travel across France", "url": "https://www.earlywave.ai/trends/trend/48213", "status": "active", "stage": "accelerating", "category": "society", "reach": "national", "subject_countries": ["FR"], "publisher_countries": ["FR", "BE", "CH"], "item_count": 142, "source_count": 61, "signal_score": 74.2, "velocity_1h": 9, "velocity_24h": 118, "started_at": "2026-10-18T05:42:00.000Z", "earliest_item_at": "2026-10-18T05:42:00.000Z", "first_seen_at": "2026-10-18T06:10:12.000Z", "last_activity_at": "2026-10-19T08:55:31.000Z" } ``` | Field | Meaning | |---|---| | `url` | The trend's page on earlywave.ai, to link people to. | | `status` | `active` in lists. `get_trend` can also return older stories. | | `stage` | The story's momentum: `emerging`, `accelerating`, `breakout`. | | `reach` | How far the coverage spreads, from the publishers' countries and languages: `local`, `national`, `international`, `global`. `null` until computed. | | `subject_countries` | Countries the story is about. Empty for a story about no country in particular. | | `publisher_countries` | Countries with at least 2 sources covering the story, most sources first. | | `item_count` / `source_count` | Articles, and distinct sources (sites) that published them. | | `signal_score` | EarlyWave's composite score, 0–100. | | `velocity_1h` / `velocity_24h` | Articles in the last hour / 24 hours. | | `started_at` | When the story started: its earliest trusted article. Use this one. | | `first_seen_at` | When EarlyWave grouped the articles into a story, usually a little later. | | `last_activity_at` | The latest article. | `subject_countries` and `publisher_countries` answer different questions. A story about France covered by German media has `subject_countries: ["FR"]` and `"DE"` in `publisher_countries`: `country=FR` and `publisher_country=DE` both find it. ## Get one trend ```bash curl "https://api.earlywave.ai/api/v1/trends/48213" \ -H "Authorization: Bearer $EARLYWAVE_API_KEY" ``` The answer has every field of the list, plus: ```json { "summary": "Rail unions began a 48-hour strike over pay. Two trains in three were cancelled on Monday morning.", "regions": ["FR", "BE"], "publisher_languages": ["fr", "en"], "top_entities": [{ "name": "SNCF", "type": "ORGANIZATION" }], "top_keywords": [{ "term": "rail strike", "aliases": ["grève"], "kind": "concept" }], "sources": [ { "id": 9917305, "title": "Rail strike: two trains in three cancelled on Monday", "url": "https://news.example.fr/rail-strike-monday", "published": "2026-10-19T08:41:00.000Z" } ] } ``` - `summary` is written by EarlyWave from the articles; `null` until then. - `publisher_languages` lists the languages with at least 2 sources. `regions` is an older list of country codes; prefer `subject_countries` and `publisher_countries`. - `top_entities` holds up to 5 `{ "name", "type" }` (`PERSON`, `ORGANIZATION`, `LOCATION`, `EVENT`, `PRODUCT`); `top_keywords` holds `{ "term", "aliases", "kind" }`. Their shape is typed loosely in the schema: read them defensively. - `sources` holds the 10 most recently added articles. For all of them, use the next operation. An unknown ID answers `404` with code `trend_not_found`. ## List a trend's articles ```bash curl "https://api.earlywave.ai/api/v1/trends/48213/articles?sort=oldest&limit=1" \ -H "Authorization: Bearer $EARLYWAVE_API_KEY" ``` ```json { "trend_id": 48213, "articles": [ { "id": 9911842, "title": "Rail unions call a 48-hour strike from Saturday", "url": "https://news.example.fr/rail-unions-strike", "preview": "The four main rail unions called a strike over the pay offer made on Thursday…", "image_url": null, "author": null, "published": "2026-10-18T05:42:00.000Z", "feed_id": 1207, "language": "en", "source": { "feed_id": 1207, "title": "Example News", "website_url": "https://news.example.fr", "country": "FR" } } ], "total": 142, "limit": 1, "offset": 0, "has_more": true } ``` `sort=latest` (default) gives the newest first, `sort=oldest` the first coverage first: with `limit=1`, who broke the story. `source.country` is the publisher's country. ## Where a trend is covered ```bash curl "https://api.earlywave.ai/api/v1/trends/48213/coverage" \ -H "Authorization: Bearer $EARLYWAVE_API_KEY" ``` ```json { "trend_id": 48213, "total_articles": 142, "total_sources": 61, "first_article_at": "2026-10-18T05:42:00.000Z", "peak_at": "2026-10-19T07:00:00.000Z", "countries": [ { "country": "FR", "sources": 52, "articles": 126, "first_article_at": "2026-10-18T05:42:00.000Z", "peak_hour": "2026-10-19T07:00:00.000Z" }, { "country": "BE", "sources": 5, "articles": 9, "first_article_at": "2026-10-18T09:15:00.000Z", "peak_hour": "2026-10-19T08:00:00.000Z" }, { "country": "CH", "sources": 4, "articles": 7, "first_article_at": "2026-10-18T11:02:00.000Z", "peak_hour": "2026-10-19T08:00:00.000Z" } ], "languages": [ { "language": "fr", "sources": 57, "articles": 134 }, { "language": "en", "sources": 4, "articles": 8 } ], "series": { "start": "2026-10-12T10:00:00.000Z", "step_minutes": 60, "articles": [0, 0, 0, "…", 14, 9], "sources": [0, 0, 0, "…", 11, 7], "new_sources": [0, 0, 0, "…", 3, 1] } } ``` This is the same data as the "Where it is covered" card of a trend page. - `countries` lists every publisher country, most sources first, with the first article and the busiest hour in each. A source is a distinct site. - `series` covers the last 7 days in 168 hourly buckets starting at `start`. `articles` counts articles per hour, `sources` the distinct sources publishing that hour, `new_sources` those publishing on the story for the first time. (The arrays above are shortened; the real ones hold 168 numbers.) ## Recipes | Question | Request | |---|---| | What is France talking about today? | `GET /trends?country=FR&since_hours=24&sort=sources` | | Which stories about France are covered abroad? | `GET /trends?country=FR&reach=international,global` | | What do German media cover most right now? | `GET /trends?publisher_country=DE` | | Which tech stories are breaking out? | `GET /trends?category=tech,ai&stage=breakout` | | Who broke this story? | `GET /trends/{trend_id}/articles?sort=oldest&limit=1` | | Has it crossed borders, and when? | `GET /trends/{trend_id}/coverage`, then `countries[].first_article_at` | | What is new in the last hour? | `GET /trends?since_hours=1&sort=recent` | Polling for new stories? Once every few minutes is plenty, and stays far below the [rate limit](https://www.earlywave.ai/developers/docs/errors-and-limits#rate-limit). Source: https://www.earlywave.ai/developers/docs/trends --- # Monitors A monitor watches a subject for you: it checks EarlyWave's sources on a schedule, keeps what matches (events, mentions or a briefing), extracts the fields you asked for and answers your standing questions. You create and tune monitors in the app, on the Monitors page or by asking Ask EarlyWave. The API reads them. | REST | MCP tool | Returns | |---|---|---| | `GET /monitors` | `list_monitors` | The account's monitors. | | `GET /monitors/{monitor_id}/results` | `get_monitor_results` | What one monitor found, with sources and current answers. | ## List your monitors ```bash curl "https://api.earlywave.ai/api/v1/monitors" \ -H "Authorization: Bearer $EARLYWAVE_API_KEY" ``` ```json { "monitors": [ { "id": "0b7c6a52-4c1e-4e0b-9a51-2f8d3c1e7a90", "name": "Battery plants in Europe", "form": "events", "status": "active", "icon": "🔋", "results_count": 23, "articles_count": 88, "last_check_at": "2026-10-19T08:00:12.000Z", "next_check_at": "2026-10-19T09:00:00.000Z", "last_result_at": "2026-10-18T16:20:45.000Z", "created_at": "2026-10-02T09:14:03.000Z", "updated_at": "2026-10-19T08:00:12.000Z", "conversation_ref": "agent-5d1fa0c2-8e44-4b7a-9c3e-71a0b2f4d6e8", "cadence": { "check_every_hours": 1 }, "delivery_mode": "realtime", "calm_since": null } ], "total": 1 } ``` The list returns every monitor at once (no paging). Archived monitors are left out. | Field | Meaning | |---|---| | `id` | The monitor's ID, a UUID. Pass it to the results operation. | | `form` | `events` (dated events with fields), `mentions` (articles that mention the subject) or `briefing` (a periodic digest). | | `status` | `draft`, `active`, `paused` or `calm`. A monitor turns `calm` after 14 days without a result; `calm_since` says since when. | | `results_count` / `articles_count` | Results kept so far, and the articles behind them. | | `last_check_at` / `next_check_at` | The latest check and the next one. | | `cadence` | How often it checks: `{ "check_every_hours": 1 }` (1, 3, 6, 12 or 24). | | `delivery_mode` | How results are delivered: as they come (`realtime`) or on a schedule (`scheduled`). | | `conversation_ref` | The conversation that created and tunes the monitor. | ## Get a monitor's results ```bash curl "https://api.earlywave.ai/api/v1/monitors/0b7c6a52-4c1e-4e0b-9a51-2f8d3c1e7a90/results?days=7" \ -H "Authorization: Bearer $EARLYWAVE_API_KEY" ``` | Parameter | Default | Meaning | |---|---|---| | `days` | 30 | The period, 1–90 days back from now. | | `query` | — | Only results containing this text. | | `limit` | 20 | Results to return, 1–100. | ```json { "monitor": { "id": "0b7c6a52-4c1e-4e0b-9a51-2f8d3c1e7a90", "name": "Battery plants in Europe", "…": "…" }, "period_days": 7, "total": 4, "limit": 20, "has_more": false, "results": [ { "id": "c1d7e0f4-2b9a-4f61-8d0e-5a3b7c9e1f20", "kind": "event", "type": "NEW_PLANT", "title": "Battery maker confirms a second plant in northern France", "summary": "The plant would open in 2028 with 30 GWh of yearly capacity.", "event_date": "2026-10-17T00:00:00.000Z", "fields": { "company": "Example Cells", "country": "FR", "capacity_gwh": 30 }, "confidence": 0.86, "cluster_id": 48517, "sources_count": 3, "first_seen_at": "2026-10-17T16:20:45.000Z", "updated_at": "2026-10-18T07:02:11.000Z", "hidden_at": null, "feedback": null, "sources": [ { "item_id": 9920113, "url": "https://news.example.fr/battery-plant", "title": "A second battery plant for the north", "publisher": "Example News", "published_at": "2026-10-17T15:58:00.000Z", "excerpt": "The company confirmed on Friday…", "provenance": "feeds" } ] } ], "answers": [ { "question": "Which countries announced a new plant this month?", "answer": "France and Hungary, one plant each.", "changed_at": "2026-10-17T16:20:45.000Z" } ] } ``` - `results` are newest first. Results you hid in the app are left out. - `kind` follows the monitor's form: `event`, `mention` or `digest`. - `fields` holds the values the monitor extracts, under the keys you defined for it. `type` is the value of its main choice field, when it has one. - `confidence` is between 0 and 1. The app folds results under 0.6 as uncertain; do the same if you show them to people. - `cluster_id` is the trend the result belongs to, when there is one: open it with `GET /trends/{trend_id}`. - `sources[].provenance` says where the source was found: `feeds` (your feeds), `library` (EarlyWave's sources), `story` (a trend's articles) or `web`. - `answers` are the monitor's standing questions with their current answers, and when each answer last changed. This operation has no `offset`. When `has_more` is `true`, narrow the period with `days`, filter with `query`, or raise `limit` up to 100. An ID that is not one of the account's monitors answers `404` with code `monitor_not_found`. ## A weekly digest in a few lines ```python import os import requests API = "https://api.earlywave.ai/api/v1" HEADERS = {"Authorization": f"Bearer {os.environ['EARLYWAVE_API_KEY']}"} monitors = requests.get(f"{API}/monitors", headers=HEADERS, timeout=30).json()["monitors"] for monitor in monitors: page = requests.get( f"{API}/monitors/{monitor['id']}/results", params={"days": 7, "limit": 100}, headers=HEADERS, timeout=30, ).json() print(f"\n{monitor['name']}: {page['total']} new") for result in page["results"]: source = result["sources"][0]["url"] if result["sources"] else "" print(f"- {result['title']} {source}") ``` Over MCP, the same question is one sentence: *"Summarise what my monitors found this week, with sources."* Source: https://www.earlywave.ai/developers/docs/monitors --- # Changelog Changes to the REST API and the MCP server. Version 1 only grows: new operations, parameters and fields are added; nothing is removed or renamed without a new version and a notice here first. The version is in the OpenAPI document (`info.version`) and in the MCP server's `serverInfo`. ## 2026-10-21 — monthly quota, the Max plan The API now has two counters per account, shared by every key, REST and MCP together: the one-minute window it already had and a calendar-month quota. Both come from the plan. ### Added with the monthly quota - **Monthly quota** by plan: 10,000 requests a month on Pro, 50,000 on Max (UTC calendar month; a contract may have no monthly limit). A refused request counts against neither counter. - **`X-Quota-Limit`, `X-Quota-Remaining`, `X-Quota-Reset`** on every authenticated response, next to the `X-RateLimit-*` headers of the minute window. - **`quota_exceeded`** as `error.code` on a `429` whose `error.type` is `rate_limit`, with `Retry-After` set to the 1st of next month; the minute window keeps `rate_limit_exceeded`. MCP tool errors name the counter that refused. - **EarlyWave Max**: 300 requests a minute and 50,000 a month; Pro stays at 60 a minute and now 10,000 a month. Settings › API & MCP shows the month's count and reset date. ### Changed with the monthly quota - The rate limit is set by the plan (60 a minute on Pro, 300 on Max) rather than one default for every account. ## 2026-10-19 — v1.1.0 The contract is now declared once and published as OpenAPI 3.1 at [`https://api.earlywave.ai/api/v1/openapi.json`](https://api.earlywave.ai/api/v1/openapi.json). REST and MCP serve the same operations. ### Added - **18 operations**, each a REST `GET` and an MCP tool of the same name as its `x-mcp-tool`. - **Trend filters** on `GET /trends` (`list_trends`): `country` (the story is about it), `publisher_country`, `category`, `stage`, `reach`, `since_hours`, `query`, and `sort` (`signal`, `recent`, `sources`). - **`GET /trends/{trend_id}/articles`** (`get_trend_articles`): every article of a trend with its source and country, newest or oldest first. - **`GET /trends/{trend_id}/coverage`** (`get_trend_coverage`): publisher countries and languages, and the last 7 days hour by hour. - New trend fields: `url` (the trend page), `reach`, `subject_countries`, `publisher_countries`; on one trend, `publisher_languages`. - **Named keys with scopes**: several keys per account, each with a name and the scopes `rest` and/or `mcp`, created and revoked in Settings › API & MCP. Keys are stored hashed and shown once (`ew_live_…`). - `X-Request-Id` on every response; `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` on every authenticated one; `Retry-After` on `429`. ### Changed - **Errors** are now `{"error": {"type", "code", "message"}}` with a stable `type` and the matching HTTP status. Invalid parameters answer `400` with every problem in `details`. - **Paging is the same on every list**: `limit` from 1 to 100 (it was capped at 20), default 20, `offset`, and `has_more` in the answer. - **Trends are the whole catalogue**: `list_trends` no longer depends on the account's feeds, and its `total` is the number of matching trends. - **MCP**: the server is named `earlywave` and is stateless — each `POST /mcp` stands alone, so there is no session to keep. `GET` and `DELETE` answer `405`. Tools now receive their arguments and advertise them in their input schema. - The rate limit (60 requests per minute per account by default) is counted once across REST and MCP and across all servers. ### Compatibility - Keys created before this release (`sk-smrz_…`) keep working, with the scope of the surface they were made for. - Every tool name of the previous MCP server is still there. - If you parsed `error` as a string, read `error.message` instead. - In `get_trend`, each of `sources` now has `url` (it was `canonical_url`). Source: https://www.earlywave.ai/developers/docs/changelog