Skip to content
JARAI Developers

Rate Limiting

The JARAI Partner API enforces rate limits to ensure fair usage and system stability. All authenticated endpoints share a single per-partner request budget.

Rate Limit Tiers

Your partner tier determines your rate limits:

TierRequests per Minute (RPM)Requests per Day (RPD)Daily Productions per Account
Free101003
Standard602,00020
Enterprise300UnlimitedUnlimited

All endpoints share the same RPM counter — a single rolling 60-second window per partner. The RPD counter resets at midnight UTC.

Rate Limit Headers

Every 2xx response includes three rate limit headers:

HeaderDescriptionExample
X-RateLimit-LimitYour effective RPM ceiling.60
X-RateLimit-RemainingRequests remaining in the current 60-second window.47
X-RateLimit-ResetUnix timestamp (seconds) when the current window resets.1743681600

These headers are also included on 429 responses. They are omitted on other 4xx and 5xx errors.

curl -v https://apim-jarai-prd.azure-api.net/v1/accounts \
-H "X-API-Key: jarai_your_api_key_here" 2>&1 | grep -i "x-ratelimit"

# X-RateLimit-Limit: 60
# X-RateLimit-Remaining: 47
# X-RateLimit-Reset: 1743681600

429 Too Many Requests

When you exceed the rate limit, the API returns 429 with a retryAfterSeconds field:

{
  "error": "RATE_LIMIT_EXCEEDED",
  "message": "Request rate limit exceeded. Please retry after the indicated period.",
  "retryAfterSeconds": 42
}
  • RPM exceeded: retryAfterSeconds indicates seconds until the current 60-second window resets.
  • RPD exceeded: retryAfterSeconds indicates seconds until midnight UTC.
  • Daily production quota exceeded: retryAfterSeconds indicates seconds until midnight UTC.

Proactive Throttling Strategy

Rather than waiting for 429 responses, use the rate limit headers to throttle proactively:

import time

def make_request(url, headers):
  response = requests.get(url, headers=headers)

  # Check remaining budget
  remaining = int(response.headers.get("X-RateLimit-Remaining", 0))
  reset_at = int(response.headers.get("X-RateLimit-Reset", 0))

  if remaining <= 2:
      # Approaching limit — pause until window resets
      wait_seconds = max(0, reset_at - int(time.time())) + 1
      print(f"Rate limit approaching, waiting {wait_seconds}s")
      time.sleep(wait_seconds)

  if response.status_code == 429:
      retry_after = response.json().get("retryAfterSeconds", 60)
      time.sleep(retry_after)
      return make_request(url, headers)

  return response

Best Practices

  1. Read the headers — always check X-RateLimit-Remaining before making batch requests.
  2. Pause before zero — stop making requests when Remaining drops to 2 or fewer. The 2-request buffer accounts for in-flight requests.
  3. Respect retryAfterSeconds — never retry a 429 before the indicated time.
  4. Use webhooks — webhook delivery does not consume your RPM budget. Using webhooks instead of polling dramatically reduces your request count.
  5. Batch where possible — use the production list endpoint (GET /v1/accounts/{accountId}/productions) instead of individual status checks when monitoring multiple productions.

Webhook Ping Limits

Webhook ping requests (POST /v1/webhooks/{subscriptionId}/ping) have a separate rate limit: 1 ping per subscription per minute. Pings do not consume from your shared RPM counter.

Authentication Failures

Failed authentication attempts (invalid API keys) do not trigger 429 rate limiting. Instead, JARAI monitors for burst authentication failures (10 or more within 60 seconds from a single IP) as a key scanning indicator and raises an internal security alert.

Next Steps