Rate Limits
Terra applies rate limits on data-read endpoints (e.g /activity) to protect the platform from runaway loops and to keep response times consistent for everyone.
The limits operate per (user_id) — meaning each user you've connected has their own budget, separate from your other users.
The rules
There are two rules. A request must satisfy both to be accepted.
Per-request window size
1,825 days (5 years)
per request
Cumulative requested days
6,000 days
rolling 1-hour bucket per (user_id)
The "requested days" cost of a single call is end_date − start_date. Calls without start_date/end_date count as 1 day. Requests at exactly 1,825 days are allowed; only > 1,825 is rejected.
Why two rules? Most partners do small windows (≤ 30 days) and never come close to either limit. The per-request cap stops obviously-broken calls (e.g. requesting 20 years of data in one shot). The hourly cumulative cap stops loops that hammer the same user repeatedly with multi-year windows.
Detecting throttling
When a request is rejected, you'll receive an HTTP 429 Too Many Requests response with these headers:
X-Terra-RateLimit-Rule
Which rule fired: r1 (per-request) or r2 (cumulative)
Retry-After
For r2: seconds until the hourly bucket resets. For r1: not set (retrying the same request won't help).
Response body:
{
"detail": "rate limit exceeded"
}Successful responses include your remaining budget
Whenever a request is accepted and the cumulative rule (R2) was evaluated, we include three headers so you can pace yourself without guessing:
X-Terra-RateLimit-Limit
Your hourly limit in days (e.g. 6000)
X-Terra-RateLimit-Remaining
Days left in the current hour bucket
X-Terra-RateLimit-Reset-After
Seconds until the bucket resets
Example response:
If these headers are missing, no rate-limit budget was tracked for the request (e.g. an unauthenticated path) — you don't need to act on them.
Handling 429s
X-Terra-RateLimit-Rule: r1— yourstart_date/end_datewindow is wider than 5 years. Split it into shorter windows and retry. Don't retry the same request; it will fail again.X-Terra-RateLimit-Rule: r2— your code has issued requests for this user totalling more than 6,000 days in the current hour. WaitRetry-Afterseconds, then resume. Smaller windows won't help until the bucket resets.
A simple back-off:
Best practices
Cache what you've already fetched. The cumulative cap is wall-clock time, not cache misses — replays count too.
Chunk backfills. A 5-year backfill at 30-day chunks is 60 calls; spread them across a few hours and you'll never see a 429.
Watch the
X-Terra-RateLimit-Remainingheader in production logs. Drops toward zero predict throttling before it happens.One request per user at a time. Parallel calls for the same user share a budget; concurrency does not multiply your allowance.
Connections without a user (e.g. before authentication completes) aren't rate-limited. Once a connection ID exists, the budget starts.
FAQ
Does this apply to webhook deliveries? No. Rate limits only apply to inbound API requests on the data-read endpoints listed above.
Does the budget reset at the top of the hour or rolling from my first request? Fixed buckets aligned to UTC (e.g. 14:00–15:00 UTC). The Retry-After header tells you exactly when the current bucket resets.
Can I get a higher limit? Reach out to your account contact — limits can be raised per dev_id for legitimate use cases.
What counts as one "day" of cost? Whatever you pass as end_date − start_date. A request like ?start_date=2024-01-01&end_date=2024-01-31 costs 30 days, regardless of how much data the user actually has in that range.
Last updated
Was this helpful?