Skip to content

Rate limits

Per-store request budgets, rate limit headers, and the 429 response

Updated View as Markdown

The API enforces two rate limit buckets. Both are keyed by store, not by token, not by IP, and not by user.

Bucket Limit Applies to
General 60 requests per minute Every /v1 endpoint
Payments 30 requests per minute POST /v1/payments/charge and POST /v1/orders/{order_id}/upsell

Because the key is the store, all of a store’s tokens share one budget. Issuing a second token to a second integration does not buy more throughput; it splits the same 60/minute between them.


The two buckets stack

The two payment endpoints sit inside the general group and carry the stricter payment throttle, so a single charge consumes one slot in both buckets.

In practice:

  • 30 charges in a minute exhausts the payment bucket and leaves 30 general requests available for reads.
  • 60 reads in a minute exhausts the general bucket and blocks charges too, even though the payment bucket still has room.

Budget for the sum, not for either limit alone.


Windows

Each bucket is a fixed one-minute window. Once the window rolls over, the full allowance is restored at once — there is no leaky-bucket smoothing and no burst credit. Rather than pacing off the clock, read Retry-After from the 429 and sleep for exactly that long.


Headers

Every response — successful or not — carries the current bucket state:

Header Present on Meaning
X-RateLimit-Limit All responses Requests allowed in the current window
X-RateLimit-Remaining All responses Requests left in the current window
Retry-After 429 only Seconds until the window resets
X-RateLimit-Reset 429 only Unix timestamp when the window resets

On the two payment endpoints the headers report whichever bucket is more restrictive at that moment, so a charge response typically shows X-RateLimit-Limit: 30. Treat the headers as advisory for the request you just made rather than as a full picture of both buckets.


The 429 response

Exceeding either bucket returns 429 Too Many Requests:

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 37
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1786425600
{
    "message": "Too Many Attempts."
}

That is the whole body. There is no error key, no error code, and no detail about which bucket you hit — the only signal is X-RateLimit-Limit, which tells you whether it was the 60 or the 30 bucket.

Important: This JSON body only appears when the request carried Accept: application/json. Without that header you get an HTML error page with a 429 status. See Errors.

Some published FirearmCart material describes a rate_limit_exceeded error envelope with a retry_after field in the body. No such envelope exists. The 429 body is exactly the single-key object above, and the retry delay is only ever in the Retry-After header.


Handling 429 in your client

#!/usr/bin/env bash
# Retry once on 429, honouring Retry-After.
response=$(curl -s -D /tmp/h -o /tmp/b -w '%{http_code}' \
  "https://api.firearmcart.com/v1/orders" \
  -H "Authorization: Bearer $FIREARMCART_TOKEN" \
  -H "Accept: application/json")

if [ "$response" = "429" ]; then
  delay=$(grep -i '^retry-after:' /tmp/h | tr -d '\r' | awk '{print $2}')
  sleep "${delay:-60}"
  # ... reissue the request
fi

Rules that keep integrations healthy:

  • Always honour Retry-After. Retrying sooner just burns another slot and extends the window.
  • Never retry a 429 on a payment endpoint blindly. POST /v1/payments/charge has no idempotency key, so a retry that races the original can double-charge. POST /v1/orders/{order_id}/upsell requires an idempotency_key and is safe to retry with the same key.
  • Page with the largest per_page you can use. per_page=100 costs one request instead of four. See Pagination.
  • Cache the slow-moving reads. GET /v1/shipping and GET /v1/taxation return small, rarely-changing payloads and are not paginated; fetch them on a schedule rather than per checkout.

Other buckets

The Theme Dev MCP server at https://api.firearmcart.com/mcp/themes uses its own, more generous limit of 240 requests per minute per store, because agent sessions make many small tool calls. It does not draw on the /v1 budget, and the /v1 budget does not draw on it — exhausting one leaves the other untouched. See MCP rate limits for the unauthenticated key and how requests map to tool calls.

Storefront and webhook traffic are limited separately and never consume your API budget.


Navigation

Type to search…

↑↓ navigate↵ selectEsc close