QudraQudra Docs
Get API key

Rate limiting

The Qudra Partner API enforces a per-minute rate limit per API key. Limits scale with your plan.

Limits by plan

| Plan | Requests / minute | Monthly job quota | Bulk endpoint | Webhooks | |---------|-------------------|-------------------|---------------|----------| | Free | 100 | 100 | — | — | | Growth | 1,000 | 1,000 | ✓ (100/req) | ✓ | | Scale | 10,000 | Unlimited | ✓ (100/req) | ✓ |

Response headers

Every response includes three headers so you can plan your traffic shaping without hitting limits blindly:

X-RateLimit-Limit:     1000
X-RateLimit-Remaining: 873
X-RateLimit-Reset:     1747700400
FieldTypeDescription
X-RateLimit-LimitintegerTotal requests allowed in the current minute window.
X-RateLimit-RemainingintegerRequests left in the current window.
X-RateLimit-ResetintegerUnix timestamp (seconds) when the window resets.

Hitting the limit

When you exceed the limit, the API responds with 429 partner.rate_limit and a Retry-After header (in seconds):

HTTP/1.1 429 Too Many Requests
Retry-After: 17
Content-Type: application/json
 
{
  "error": {
    "code": "partner.rate_limit",
    "message": "Rate limit exceeded. Try again in 17 seconds.",
    "messageAr": "تم تجاوز حد الطلبات. أعد المحاولة بعد 17 ثانية.",
    "request_id": "req_01HXY…"
  }
}

Honor Retry-After

Always respect Retry-After instead of busy-looping. Our SDKs do this automatically with exponential backoff and jitter.

Backoff strategy

We recommend exponential backoff with full jitter, capped at 60 seconds:

async function withRetry<T>(fn: () => Promise<T>, max = 5): Promise<T> {
  for (let attempt = 0; attempt < max; attempt++) {
    try {
      return await fn();
    } catch (e) {
      if (!(e instanceof QudraError) || e.code !== 'partner.rate_limit') throw e;
      const base   = Math.min(60, 2 ** attempt);
      const jitter = Math.random() * base;
      await new Promise((r) => setTimeout(r, jitter * 1000));
    }
  }
  throw new Error('Rate limit retries exhausted');
}

Bulk endpoints

If you regularly need to post more than 50 jobs/minute, use the bulk endpoint — one request counts as one against your rate limit but creates up to 100 jobs.

Monthly job quota

The monthly_job_limit is a separate quota from the per-minute rate limit. When you hit it, you'll get 429 partner.plan_limit_exceeded (not rate_limit). The counter resets at the start of each calendar month (Asia/Riyadh time).