Rate limits
Each private key has its own limit, applied as a token bucket:
| Setting | Default |
|---|---|
| Sustained rate | 60 requests per minute |
| Burst | 20 requests |
The bucket holds up to 20 requests and refills at one per second, so short bursts are fine as long as the average stays under 60 a minute. If you need more, ask us; limits are set per key. A service-wide cap also protects the API as a whole.
When you hit the limit
Section titled “When you hit the limit”The API returns 429 with a Retry-After header (seconds) and {"code": "rate_limited", ...}. Wait at least that
long before the next request from that key.
# -i shows the Retry-After header on a 429curl -i https://api.nimbusmint.bot/partner/v1/health \ -H "Authorization: Bearer $NIMBUS_PRIVATE_KEY"async function nimbusGet(url, attempt = 0) { const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.NIMBUS_PRIVATE_KEY}` }, }); if (res.status === 429 && attempt < 3) { const wait = Number(res.headers.get('Retry-After') ?? '1'); await new Promise((r) => setTimeout(r, wait * 1000)); return nimbusGet(url, attempt + 1); } return res;}import time
def nimbus_get(url, params=None, attempt=0): res = requests.get(url, headers=HEADERS, params=params, timeout=10) if res.status_code == 429 and attempt < 3: time.sleep(int(res.headers.get("Retry-After", "1"))) return nimbus_get(url, params, attempt + 1) return resStay under the limit
Section titled “Stay under the limit”- Cache responses yourself.
/hitsreturnscache_ttl_s(currently 30): reuse the answer for that many seconds instead of calling again; the API caches for that long anyway. Every response is sent withCache-Control: no-store, so HTTP caches and proxies will not do this for you. - Key the cache by the question, not the user. Use
contract,chain_id,sinceanduntil(andtop) as the key. A thousand users asking about the same collection are one cache entry. - Coalesce concurrent requests. When ten commands arrive before the first answer comes back, send one request and give all ten the same result (a “single-flight” lock per cache key).
- Share one cache across your bot’s processes or shards, for example in Redis, so each process does not fetch the same answer separately.
- Do not poll
/hits. Call it when a user asks (for example on/gas), not on a timer per collection. - Sync
/mintsincrementally. Run it as a background job, not per user request: page withlimit=500, storenext_cursor, and resume from it on the next run instead of re-reading the whole window. Every few minutes is plenty. - Call
/healthrarely. Once at startup and once a day is enough to watch your key’s expiry. - Show something useful on a
429. Serve the last cached answer (and say how old it is) or your own data, rather than failing the command.
Retrying safely
Section titled “Retrying safely”- Retry only
429and503. Never retry400,401or403: they will fail the same way and still use your limit. - Wait at least
Retry-Afterseconds, add a little random jitter, and cap the number of attempts (three is plenty). Retrying immediately, or from every shard at once, keeps you limited for longer. - Log every
429with the endpoint and parameters. Repeated429s on the same question usually mean a missing cache.
Planning for growth
Section titled “Planning for growth”- Ask before you need it. Limits are set per key, and we can raise yours. Tell us before a launch, a big drop or a marketing push, with the requests per minute you expect after caching.
- Use separate keys for separate jobs (for example one for your bot and one for a
/mintssync), so a busy job cannot starve the other. A service-wide cap still applies to all keys together.
