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 /connectors/spaces/{slug}/{connector_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.