Rate Limiting

All API requests are subject to rate limiting to protect the platform and ensure fair usage. Limits vary by endpoint type and authentication status.

General API Limits

TierBucket SizeRefill RateApplied Per
Unauthenticated10,000 requests100 per 5 minutesIP address
Authenticated50,000 requests500 per 2 minutesIP address

General API limits use a token bucket policy — each request consumes one token. Tokens refill at a steady rate. When the bucket is empty, requests are rejected with 429 Too Many Requests.

Authentication Endpoint Limits

Authentication endpoints use stricter sliding window limits to prevent brute-force attacks:

EndpointLimitWindowApplied Per
POST /api/auth (login)5 attempts1 minuteIP address
POST /api/auth/register10 attempts1 hourIP address
POST /api/auth/mfa/verify5 attempts1 minuteIP address
POST /api/auth/google10 attempts1 minuteIP address
POST /api/auth/passkey/login10 attempts1 minuteIP address
POST /api/auth/passkey/login/options10 attempts1 minuteIP address
POST /api/auth/forgot-password3 attempts15 minutesIP address
POST /api/auth/reset-password3 attempts15 minutesIP address

Other Endpoint Limits

EndpointLimitWindowApplied Per
POST /api/v1/contact3 submissions1 hourIP address
POST /api/v1/licensing/validate10 attempts1 hourIP address

ANAF Integration Limits

ANAF (Romanian Tax Authority) endpoints have dedicated rate limiters to comply with ANAF API restrictions:

OperationLimitWindowApplied Per
All ANAF calls (global)1,000 requests1 minuteSystem-wide
List messages1,500 requests1 dayCompany CIF
Download message10 requests1 dayMessage ID
Check upload status100 requests1 dayUpload ID
Upload response1,000 requests1 dayCompany CIF

Handling Rate Limit Responses

When a rate limit is exceeded, the API returns a 429 Too Many Requests response:

{
  "error": "Too many requests. Please try again later."
}

For ANAF endpoints, the response includes a Retry-After header and value:

{
  "error": "ANAF rate limit reached. Try again later.",
  "retryAfter": 45
}

Best Practices

  • Implement exponential backoff — when you receive a 429, wait before retrying. Double the wait time on each consecutive failure.
  • Cache responses — avoid unnecessary duplicate requests, especially for data that doesn't change frequently (exchange rates, company details).
  • Use webhooks — instead of polling for status changes, subscribe to webhook events for real-time updates.
  • Batch operations — where the API supports it, use list endpoints with filters instead of making individual requests.

Example: Retry with Backoff

async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.status !== 429) return response;

    const retryAfter = response.headers.get('Retry-After');
    const waitMs = retryAfter
      ? parseInt(retryAfter) * 1000
      : Math.pow(2, attempt) * 1000;

    await new Promise(resolve => setTimeout(resolve, waitMs));
  }

  throw new Error('Rate limit exceeded after retries');
}