DailyAPI Documentationv1

Rate limits

Design clients for Daily's enforced per-key and per-IP edge limits.

View OpenAPI

Daily enforces rate limits in Nginx before the application:

LimitSustained rateBurst capacityKey
API key5 requests per second20 immediate excess requestsPublic key identifier
Client IP20 requests per second40 immediate excess requestsSource IP

Both limits apply. Requests within the burst are accepted without deliberate delay; once either bucket is exhausted, Nginx returns 429.

HTTP/2 429
Content-Type: application/json
Retry-After: 1
X-Request-ID: req_edgeratelimit01
Cache-Control: private, no-store
{
  "error": {
    "type": "rate_limit_error",
    "code": "rate_limit_exceeded",
    "message": "Too many requests."
  },
  "request_id": "req_edgeratelimit01"
}

The edge response uses Retry-After: 1. Temporary upstream failures use 503 service_unavailable with Retry-After: 5; treat them separately.

Retry strategy

Honor Retry-After, then use capped exponential backoff with random jitter. Retry 429 and transient 503 responses only. Do not retry authentication, permission, filter, or not-found errors automatically.

const apiKey = process.env.DAILY_API_KEY;
if (!apiKey) throw new Error('DAILY_API_KEY is required');

async function getWithBackoff(url: string, maxAttempts = 6): Promise<Response> {
  for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
    const response = await fetch(url, {
      headers: { Accept: 'application/json', Authorization: `Bearer ${apiKey}` },
    });
    if (response.status !== 429 && response.status !== 503) return response;
    if (attempt === maxAttempts - 1) return response;

    const retryAfter = Number(response.headers.get('retry-after'));
    const baseMs = Number.isFinite(retryAfter) && retryAfter >= 0
      ? retryAfter * 1_000
      : Math.min(30_000, 500 * 2 ** attempt);
    const jitterMs = Math.floor(Math.random() * Math.max(1, baseMs * 0.25));
    await new Promise((resolve) => setTimeout(resolve, baseMs + jitterMs));
  }
  throw new Error('Unreachable retry state');
}

const response = await getWithBackoff('https://api.godaily.co.il/v1/customers?limit=100');
const body = await response.json();
if (!response.ok) throw new Error(`Daily API ${response.status} (${body.request_id})`);
console.log(body.data.length);

What counts

Every request reaching the public /v1 Nginx location consumes capacity, including each pagination page, browser preflight, and request that later fails authentication, permission, filtering, or lookup. Production integrations should use server-to-server requests and avoid browser preflights.

Prefer limit=100, keep concurrency below the sustained limits, and coordinate workers sharing a key or NAT address.

On this page