Rate Limiting
The Pixee PIM API enforces per-endpoint rate limits to ensure fair usage and platform stability. Limits are tracked in Redis, organized into 26 tiers, and persist across API instances.
Rate limit tiers
Endpoints are grouped into tiers by category, each with its own limit and sliding window. Limits below were raised significantly in v6.19 to accommodate large catalogs (100k+ products) — most read/write tiers increased 5–10×; AI batch got stricter (cost control).
Auth
| Tier | Limit | Endpoints |
|---|---|---|
AUTH_LOGIN | 5 / 15 min | POST /auth/login, POST /auth/login/json |
AUTH_PASSWORD_RESET | 3 / 15 min | POST /auth/forgot-password |
AUTH_REGISTER | 3 / 15 min | POST /auth/register |
AUTH_REFRESH | 30 / min | POST /auth/refresh |
AUTH_LOGOUT | 30 / min | POST /auth/logout |
AUTH_LOGIN, AUTH_PASSWORD_RESET, and AUTH_REGISTER use 15-minute windows — not 1 minute. After 5 failed login attempts the endpoint is blocked for the remainder of that window.
Read
| Tier | Limit | Endpoints |
|---|---|---|
READ_PUBLIC | 1 000 / min | Health checks, public routes |
READ_STANDARD | 1 000 / min | Standard GET endpoints (products, imports, etc.) |
READ_ADMIN | 1 000 / min | Admin panel reads |
READ_SENSITIVE | 50 / min | User data, audit logs, analytics/reports |
Write
| Tier | Limit | Endpoints |
|---|---|---|
WRITE_STANDARD | 100 / min | Standard POST / PUT / PATCH / DELETE |
WRITE_CONFIG | 100 / min | Configuration changes |
WRITE_UPLOAD | 100 / min | File upload endpoints |
WRITE_USER_MGMT | 100 / min | User / role management |
Bulk
| Tier | Limit | Endpoints |
|---|---|---|
BULK_IMPORT | 500 / min | Import batch jobs |
BULK_EXPORT | 500 / min | Export batch jobs |
BULK_CREATE | 500 / min | Bulk entity creation |
BULK_UPDATE | 500 / min | Bulk attribute updates |
BULK_DELETE | 500 / min | Bulk deletes |
AI
| Tier | Limit | Endpoints |
|---|---|---|
AI_SINGLE | 10 / min | Individual AI enrichment |
AI_BATCH | 1 / hour | Batch AI job creation/start (cost control) |
AI_BATCH is deliberately strict — starting a batch enrichment job is rate-limited to once per hour per account, independent of how many products the batch covers.
Export & data access
| Tier | Limit | Endpoints |
|---|---|---|
EXPORT_STANDARD | 50 / min | Standard export jobs |
EXPORT_STREAM | 50 / min | Streaming exports |
EXPORT_DOWNLOAD | 50 / min | File downloads |
EXPORT_SCHEDULE | 50 / min | Scheduled exports |
Public & health
| Tier | Limit | Endpoints |
|---|---|---|
HEALTH_CHECK | 1 000 / min | Health / status endpoints |
PROGRESS_POLL | 1 000 / min | Job progress polling |
Rate limit headers
X-RateLimit-* headers are injected only on authenticated endpoints accessed via API key. They are not present on every response.
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in the current window |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Example response headers (API key endpoints)
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 987
X-RateLimit-Reset: 1758100460
Handling rate limit errors
When you exceed the limit, the API returns 429 Too Many Requests. This response uses a distinct shape from the standard error envelope described in Errors — it comes from a separate rate-limiting layer that has not been unified onto it:
429 Response
{
"error": "rate_limit_exceeded",
"message": "Too many requests. Please try again later.",
"detail": "5 per 15 minute",
"retry_after": "Please wait before making more requests"
}
Here error is a string, not an object — unlike every other error response on this API. The retry_after field is also a human-readable string, not an integer. To determine the exact wait time, parse the detail field (e.g. "5 per 15 minute") or implement exponential backoff (see below).
Retry strategy
We recommend exponential backoff with jitter:
Exponential backoff (Python)
import time
import random
import httpx
def call_with_backoff(url, headers, max_retries=5):
for attempt in range(max_retries):
response = httpx.get(url, headers=headers)
if response.status_code == 429:
wait = (2 ** attempt) + random.uniform(0, 1)
time.sleep(wait)
continue
return response
raise Exception("Max retries exceeded")
For bulk operations (imports, exports, batch AI enrichment), schedule jobs during
off-peak hours. AI_BATCH (1/hour) and READ_SENSITIVE/EXPORT_* (50/min) have the
strictest limits.