Get started
Errors, limits and paging
View as MarkdownOn this page
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:
{
"error": {
"type": "not_found",
"code": "trend_not_found",
"message": "Trend not found."
}
}typeis the stable family. Branch on it.codeis the precise reason. New codes may appear; treat an unknown one by itstype.messageis for people and logs. Its wording may change.detailsis 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:
curl "https://api.earlywave.ai/api/v1/trends?limit=500&sort=popular" \
-H "Authorization: Bearer $EARLYWAVE_API_KEY"{
"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 answersinvalid_categoryand 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/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: 1793577600Wait 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.
{ "trends": [ … ], "total": 37, "limit": 20, "offset": 0, "has_more": true }To read everything, call again with offset + limit while has_more is true:
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 /monitorsandGET /categoriesreturn everything at once, withtotal.GET /monitors/{monitor_id}/resultstakeslimitbut nooffset. Narrow the period (days) or the text (query) instead.GET /discover/keywordstakeslimitonly.
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.