Rate Limits
Last verified: 2026-05-03 (Task #334 — docs accuracy pass).
The Weather Intelligence API enforces four quota dimensions per calendar month (UTC). The dimensions are independent — exhausting one does not block the others.
| Dimension | What it counts |
|---|---|
requests | Successful, billable API calls. Calls that log billing_units = 0 (e.g. /v1/usage, pre-handler rate-limit rejections) do not count. |
timeline_items | Sum of items_analyzed across all /v1/weather/timeline calls in the period. |
route_calls | Successful /v1/weather/route calls. |
active_watches | Instantaneous count of weather_watches in status='active'. |
Limits are pinned to PRD section 22.1.
Plan tiers
| Plan | Requests / mo | Timeline items / mo | Route calls / mo | Active watches |
|---|---|---|---|---|
| Free | 1,000 | 100 | 0 | 0 |
| Starter | 25,000 | 10,000 | 1,000 | 500 |
| Pro | 250,000 | 100,000 | 25,000 | 10,000 |
| Enterprise | 250,000 | 100,000 | 25,000 | 10,000 |
Enterprise defaults to Pro and is expected to be tuned per-account via
developer_api_keys.rate_limit_overrides.
Note for Free and Starter callers: the route endpoint and watches endpoints are not just throttled — they're flatly excluded. A Free-tier call to
/v1/weather/routereturnsbilling_limit_reached(HTTP 402), notrate_limited.
Headers on every response
Every response — successful or not — includes the standard rate-limit headers describing the monthly request bucket for the calling key:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Cap for the requests dimension on this plan. |
X-RateLimit-Remaining | Requests left in the current monthly window (clamped to ≥ 0). |
X-RateLimit-Reset | Unix-seconds timestamp when the monthly bucket rolls over (next UTC month boundary). |
Other dimensions are not exposed via headers but are returned in full
by GET /v1/usage.
Throttling
When a quota cap is hit, the response is one of:
| Code | HTTP | When |
|---|---|---|
rate_limit_exceeded | 429 | The plan includes the feature, but the monthly cap was hit. Wait for the bucket to reset (or upgrade). |
billing_limit_reached | 402 | The plan flatly excludes the feature (limit is 0). Waiting won't help — the caller has to upgrade. |
Both come back in the standard ErrorEnvelope shape. The error
details block names the dimension that was over.
{
"error": {
"code": "rate_limit_exceeded",
"message": "Monthly request quota exceeded",
"details": { "dimension": "requests" },
"request_id": "7c0f3c2c-9e4a-4d29-b4a2-9c2b4e9f5c11"
},
"meta": {
"request_id": "7c0f3c2c-9e4a-4d29-b4a2-9c2b4e9f5c11",
"generated_at": "2026-04-25T18:34:00.000Z"
}
}
The Retry-After header is not currently emitted. Use
X-RateLimit-Reset to know when the monthly bucket rolls over.
Reading your usage
GET /v1/usage returns the full picture: plan, environment, period
boundaries, every limit, and current usage + remaining for every
dimension. Calling this endpoint never burns request quota and never
gets rate-limited (otherwise developers couldn't see why they're
throttled).
See endpoints.md#get-usage for the full
shape and an example.
Internal Travelmode calls
Travelmode's own service-to-service calls bypass rate-limit checks but
are still recorded via the same api_usage_events pipeline. The
headers you see on an internal-key response report what the cap
would have been if the key were external — useful for debugging
how a feature would behave for a real customer.