Skip to content
Core Concepts

Rate Limits

kirim.dev applies per-organisation, per-endpoint-class token-bucket rate limits. Limits are enforced atomically by a Redis Lua script — no race conditions across replicas, no surprise bursts.

ClassHTTP methodsExamples
writePOST, PUT, PATCH, DELETESend a message, attach a label, replay a webhook delivery
readGET, HEADList conversations, fetch a message, introspect via /v1/me

Each class has its own bucket. Sending 60 messages does not eat into your read budget, and vice versa.

PlanWrite / minRead / min
Default (free)60600
Pro6006 000
Enterpriseconfigurableconfigurable

Upgrade your plan in the dashboard to lift limits. Enterprise plans get per-key overrides on request — contact support.

Every successful response and every 4xx error carries the same three headers so client code can self-throttle without parsing the body:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1716480000
HeaderMeaning
X-RateLimit-LimitBucket capacity for the class you just hit, in requests per minute.
X-RateLimit-RemainingTokens currently available (floored to integer).
X-RateLimit-ResetUnix epoch seconds when the bucket would be full at the current refill rate.

On a 429 there is one extra header:

Retry-After: 12

Retry-After is the number of seconds until at least one token becomes available. Sleep at least that long before retrying.

import { Kirim, RateLimitError } from '@kirimdev/sdk'
// The SDK retries 429 automatically with jittered exponential backoff.
// Configure the budget if your workload needs more headroom.
const kirim = new Kirim({
apiKey: process.env.KIRIM_KEY!,
maxRetries: 5, // default 3
retryBaseDelayMs: 500, // default 250
})
const phone = kirim.phoneNumbers('106540352242922')
try {
await phone.messages.send({
messaging_product: 'whatsapp',
to: '+628111222333',
type: 'text',
text: { body: 'Halo!' },
})
} catch (err) {
if (err instanceof RateLimitError) {
// Budget exhausted — persist for offline processing.
await queue.enqueue({ retryAfterMs: err.retryAfterMs })
}
throw err
}
  • Don’t fire requests as fast as Remaining allows. The bucket refills continuously, but bursting it to zero and immediately retrying just trades a 429 for another 429.
  • Don’t ignore Retry-After. It’s accurate. Retrying earlier guarantees a second 429 and consumes attempts you’ll need later.
  • Don’t sleep without jitter. A herd of retries waking up at the same instant will all trip the limiter together. Multiply Retry-After by 0.75–1.25 before sleeping.
  • Don’t shard for capacity by minting more API keys. Keys share the org bucket. To raise throughput, upgrade the plan or request an Enterprise override.

Idempotency

Retry without duplicating sends. Read →

Errors

Full envelope and code catalogue. Read →

Pagination

Opaque cursors and iteration recipes. Read →

API reference

Per-endpoint rate-limit class annotations. Browse →