Rate limits
Darwin applies rate limiting per tenant, distinguishing between read and write API calls, and between on-chain capture events and outbound webhook deliveries. This page describes the per-tier quotas and the expected client behavior when approaching the limit.
Product tiers
Section titled “Product tiers”Current tiers. The numbers below are representative and apply per tenant, not per user.
| Tier | Requests / min | Requests / month | Capture events / month | Webhook deliveries / month |
|---|---|---|---|---|
| Sandbox (evaluation) | 60 | 100,000 | 5,000 | 25,000 |
| Starter (early production) | 300 | 1,000,000 | 50,000 | 250,000 |
| Growth (mid-scale) | 1,500 | 10,000,000 | 500,000 | 2,500,000 |
| Scale (high volume) | 7,500 | 100,000,000 | 5,000,000 | 25,000,000 |
| Enterprise | Custom quotas | Custom quotas | Custom quotas | Custom quotas |
Write calls (event prepare, custody transfers, recalls) consume
both request quota and the specific capture events quota. Read
calls (NFT queries, lineage, operational reports) only consume
request quota.
Response headers
Section titled “Response headers”Every API response includes headers with your current quota state:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Max requests per minute in your tier |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Retry-After | When 429 is returned, seconds to wait before retrying |
Behavior when limit is exceeded
Section titled “Behavior when limit is exceeded”When a request exceeds the per-minute limit, the API responds with HTTP 429 Too Many Requests and a JSON body describing which limit was hit:
{ "error": "rate_limit_exceeded", "scope": "requests_per_minute", "retryAfter": 12}The client should:
- Read
Retry-After(seconds) orX-RateLimit-Reset(timestamp). - Wait at least that long before retrying.
- Apply exponential backoff with jitter if 429s persist, to avoid thundering herd when the window expires.
Monthly quotas (requests, capture events, webhook deliveries) reset
on the first UTC day of the calendar month. If a monthly quota is
exhausted, the API returns 429 with
scope: "requests_per_month", scope: "events_per_month", or
scope: "webhooks_per_month" as appropriate.
Webhooks and rate limiting
Section titled “Webhooks and rate limiting”Outbound webhook deliveries do not consume your inbound request
quota. They have their own monthly quota per tier (the Webhook
deliveries / month column above). If your receiver endpoint replies
with 429, Tracium honors the Retry-After header and
retries within the 3-attempt exponential backoff policy.
How to upgrade tier
Section titled “How to upgrade tier”Moving from Sandbox to Starter, Starter to Growth, Growth to Scale, or any tier to Enterprise is arranged with the commercial team. Enterprise quotas are written into the contract and may include:
- Higher burst rates per minute.
- Monthly quotas with no cap or an agreed cap.
- Priority in the webhook queue.
- Support and availability SLAs.
Contact the team at
tech@darwinevolution.io
with your current tier, projected usage, and time horizon. We will
respond with the recommended quota and next steps.
Best practices
Section titled “Best practices”- Batch by idempotency: Captia uses client-generated idempotent UUIDs. Retrying a lost event creates no duplicates and consumes no extra quota if the server already processed it.
- Cache on read: NFT inventory, ancestry, and operational report queries support aggressive client-side caching. Canonical metadata does not change after on-chain anchoring.
- GraphQL for composed queries: a single well-formed GraphQL query consumes one request rather than several REST calls. Useful for dashboards.
- Respect
Retry-After: never retry before the indicated value. Premature retries count against your quota and extend the block window.