Skip to main content

Rate Limits

The Qarion API enforces rate limits to ensure fair usage and protect the platform's stability. Understanding these limits — and designing your integration to respect them — is important for building reliable automations that don't get throttled unexpectedly.

Default Limits

Rate limits are applied per API key and vary by subscription tier:

TierRequests/MinuteRequests/Hour
Free601,000
Standard30010,000
EnterpriseCustomCustom

These limits count all requests equally regardless of endpoint — a GET request to list products consumes the same quota as a POST request to create one. The per-minute limit provides burst protection, while the per-hour limit caps sustained throughput.

Response Headers

The API includes rate limit information in the headers of every response, allowing your application to monitor its usage in real time and throttle itself proactively before hitting the limit:

X-RateLimit-Limit: 300
X-RateLimit-Remaining: 250
X-RateLimit-Reset: 1706792400
HeaderDescription
X-RateLimit-LimitMax requests per window
X-RateLimit-RemainingRequests left in window
X-RateLimit-ResetUnix timestamp when window resets

By checking X-RateLimit-Remaining after each request, your application can slow down as it approaches the limit rather than waiting for a hard rejection.

Rate Limit Exceeded

When you exceed the limit, the API returns a 429 Too Many Requests response with a Retry-After header indicating how many seconds to wait before sending another request:

HTTP/1.1 429 Too Many Requests
Retry-After: 30
{
"detail": "Rate limit exceeded. Retry after 30 seconds."
}

Handling Rate Limits

The most robust way to handle rate limits is to build retry logic directly into your API client. Both examples below demonstrate a simple pattern that waits for the duration specified in the Retry-After header before retrying automatically.

Python Example

import time
import requests

class RateLimitedClient:
def __init__(self, api_key, base_url="https://api.qarion.com"):
self.api_key = api_key
self.base_url = base_url

def request(self, method, path, **kwargs):
headers = {"Authorization": f"Bearer {self.api_key}"}

while True:
response = requests.request(
method,
f"{self.base_url}{path}",
headers=headers,
**kwargs
)

if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 60))
print(f"Rate limited. Retrying in {retry_after}s...")
time.sleep(retry_after)
continue

return response

JavaScript Example

class QarionClient {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = 'https://api.qarion.com';
}

async request(method, path, options = {}) {
const url = `${this.baseUrl}${path}`;
const headers = {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
};

while (true) {
const response = await fetch(url, { method, headers, ...options });

if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
console.log(`Rate limited. Retrying in ${retryAfter}s...`);
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
continue;
}

return response;
}
}
}

Best Practices

Cache Responses

The most effective way to stay within rate limits is to avoid unnecessary requests entirely. Cache the results of GET requests locally when the underlying data doesn't change frequently — product metadata, space configurations, and user profiles are all good candidates for caching with a TTL of a few minutes.

from functools import lru_cache

@lru_cache(maxsize=100)
def get_product(product_id):
return client.get(f"/catalog/spaces/{space}/products/{product_id}")

Batch Requests

When you need to operate on multiple resources, use bulk endpoints where they are available. This dramatically reduces the number of API calls compared to making individual requests in a loop:

# Bad: Individual requests
for product_id in product_ids:
client.get(f"/products/{product_id}") # 100 requests

# Good: Batch request
client.post("/products/batch", json={"ids": product_ids}) # 1 request

Use Webhooks

If your integration polls the API periodically to check for updates — for example, monitoring for new issues or quality alert — consider using webhooks instead. Webhooks deliver events to your application as they occur, eliminating the need for polling entirely and significantly reducing your API usage.

Implement Backoff

When retrying after a rate limit or server error, use exponential backoff with jitter. This spreads out retry attempts and prevents a "thundering herd" scenario where many clients retry simultaneously:

def request_with_backoff(func, max_retries=5):
for attempt in range(max_retries):
try:
return func()
except RateLimitError:
wait = (2 ** attempt) + random.uniform(0, 1)
time.sleep(wait)
raise Exception("Max retries exceeded")

Endpoint-Specific Limits

Some endpoints have stricter limits than the general rate cap, because they trigger expensive operations (such as executing a quality check against a warehouse or syncing metadata from an external source):

EndpointLimit
POST /search30/min
POST /quality/checks/{id}/run10/min
POST /spaces/{slug}/source-systems/{id}/sync5/min

If your automation needs to trigger these operations frequently, consider batching them or spacing them out over time to stay within these tighter limits.

Increasing Limits

If the default rate limits are insufficient for your production workload, contact the Qarion support team to discuss higher rate limits for production use, dedicated API capacity for high-volume integrations, and custom SLAs with guaranteed throughput.