Browse documentation

Plans and reliability

Rate limits

Use response headers and Retry-After to control throughput without creating retry storms.

DevelopersAdvancedAvailable

What this guide covers

  • Read rate-limit headers before the remaining capacity reaches zero.
  • Back off according to Retry-After after a rate-limited response.
  • Use jitter and idempotency keys when retrying eligible operations.

Read the response headers

Protected Yanola API responses expose the active rate-limit window in standard response headers. Read them on successful requests as well as 429 responses so your workers can slow down before capacity is exhausted.

Rate-limit response headers
HeaderMeaning
X-RateLimit-LimitMaximum requests represented by the current effective window.
X-RateLimit-RemainingRequests remaining in that window after this request.
X-RateLimit-ResetUnix timestamp in seconds when the represented window resets.
Retry-AfterSeconds to wait after a 429 or temporarily unavailable response.

Understand rate-limit scopes

A request can consume more than one configured bucket. General API protection applies across the API key, Agent Project, and global scopes; Sender operations can also consume a Sender bucket. Webhook delivery throughput uses endpoint and global scopes.

Buckets include the key's environment, so Test Mode and Live Mode traffic are isolated. The returned headers represent the effective bucket selected for that response.

Test Mode message simulation has additional safety buckets: 30 sends per minute per key and 1,000 sends per Agent Project in each 31-day quota window. Exhausting either returns test_mode_quota_exceeded; it never consumes Live Mode capacity.

Rate-limit scopes
ScopeProtects
api_keyTraffic made with one API key.
agent_projectAggregate traffic for one Agent Project.
senderTraffic for one connected WhatsApp Sender.
webhook_endpointOutbound delivery traffic to one webhook endpoint.
globalBroader Developer Platform capacity.

Handle HTTP 429

When a standard bucket is exhausted, Yanola returns 429 with rate_limit_error, code rate_limit_exceeded, retryable: true, rate-limit headers, and Retry-After. A Test Mode simulated-send quota uses the same response shape with code test_mode_quota_exceeded.

  1. Stop issuing work for the affected scope.
  2. Wait at least Retry-After seconds; if it is absent, wait until X-RateLimit-Reset.
  3. Add jitter before releasing concurrent workers.
  4. Retry within a bounded budget and preserve the send idempotency key.
  5. Reduce concurrency or batch polling ranges if 429 responses continue.
Rate-limit error
json
{
  "error": {
    "type": "rate_limit_error",
    "code": "rate_limit_exceeded",
    "message": "Too many requests. Retry after the current rate-limit window resets.",
    "requestId": "req_...",
    "retryable": true,
    "details": {
      "scope": "api_key",
      "limit": 100,
      "remaining": 0,
      "reset": 1785319260
    }
  }
}

SDK behavior

WhatsAppClient automatically retries 429 responses within maxRetries and honors Retry-After. The defaults are two retries after the first attempt, with jittered backoff when Retry-After is unavailable.

If the SDK throws YanolaRateLimitError, its retry budget is exhausted. Schedule the operation through your own bounded queue instead of immediately calling the same method in a tight loop.

Coordinate an outer queue
ts
import {
  WhatsAppClient,
  YanolaRateLimitError,
} from "@yanola/whatsapp"

const apiKey = process.env.YANOLA_API_KEY
if (!apiKey) throw new Error("Missing YANOLA_API_KEY")

const yanola = new WhatsAppClient({
  apiKey,
  maxRetries: 2,
})

try {
  await yanola.messages.sendText(
    {
      from: "+15557654321",
      to: "+15551234567",
      body: "Your order is ready",
    },
    { idempotencyKey: "order-A-1042-ready-v1" }
  )
} catch (error) {
  if (error instanceof YanolaRateLimitError) {
    await enqueueForLater({
      operation: "order-A-1042-ready-v1",
      requestId: error.requestId,
    })
    return
  }
  throw error
}

Prevent retry storms

  1. Share concurrency budgets among workers that use the same key, project, or Sender.
  2. Use X-RateLimit-Remaining to reduce intake before a 429 response.
  3. Centralize Retry-After scheduling rather than letting every worker wake at the same instant.
  4. Add jitter and cap both retry attempts and maximum elapsed retry time.
  5. Use webhooks for lifecycle updates instead of high-frequency message polling.
  6. Keep Test Mode load tests bounded; environment isolation is not authorization for abusive traffic.

Temporary limiter unavailability

If Yanola cannot safely enforce the rate limit, it fails closed with 503 api_error, code rate_limit_unavailable, retryable: true, and Retry-After: 1.

The SDK treats 503 as retryable. If retries are exhausted, retain the operation and schedule it later rather than bypassing Yanola or switching credentials to evade the limit.