Skip to main content
The Shelfforce API enforces rate limits to ensure fair usage and platform stability. Limits are applied per API key over a rolling 60-second window.

Limits by plan

PlanAnalyses / 60sReads / 60sWrites / 60s
Free56020
Starter1012040
Growth3030080
Pro60600150
Scale1501,200300
AnalysesPOST /api/v1/analyses and POST /api/v1/analyses/batch (each image in a batch counts as one analysis request). Reads — All GET endpoints (analyses, products, tasks, places, reports, usage). Writes — All POST and PATCH endpoints except analysis submission (tasks, places, inventory, webhooks).

Rate limit headers

Every API response includes headers showing your current rate limit status:
HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the current window
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp (seconds) when the window resets
Example response headers:
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 22
X-RateLimit-Reset: 1740312120

Handling rate limits

When you exceed the rate limit, the API returns a 429 Too Many Requests response with a Retry-After header indicating how many seconds to wait:
HTTP/1.1 429 Too Many Requests
Retry-After: 12
Content-Type: application/json

{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Rate limit exceeded. Retry after 12 seconds.",
    "details": {
      "retryAfter": 12,
      "limit": 30,
      "window": "60s"
    }
  }
}

Best practices

Exponential backoff

When you receive a 429, wait for the duration specified in Retry-After, then retry. If the retry also fails, double the wait time on each subsequent attempt.

Use batch endpoints

Submit up to 20 images in a single batch request instead of making 20 individual calls. This consumes only 1 rate limit slot for writes.

Cache results

Analysis results are permanent. Cache responses locally to avoid redundant GET requests.

Use webhooks

Instead of polling for analysis status, register a webhook to receive notifications when analyses complete. This eliminates repeated GET requests.

Example: Exponential backoff

async function fetchWithBackoff(url: string, options: RequestInit, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options);

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

    const retryAfter = parseInt(response.headers.get("Retry-After") || "1", 10);
    const backoff = retryAfter * 1000 * Math.pow(2, attempt);
    console.log(`Rate limited. Retrying in ${backoff}ms...`);
    await new Promise((resolve) => setTimeout(resolve, backoff));
  }

  throw new Error("Max retries exceeded");
}
If you consistently hit rate limits, consider upgrading your plan. Contact hey@shelfforce.ai for custom rate limits on Enterprise plans.