AffiliateRail docs
API

Rate limits, errors and versioning

The operational contract: limits and their headers, idempotent retries, the error catalogue, and how the API versions.

Rate limits

Every key may make 25 requests per second, enforced as a token bucket that refills continuously, so short bursts above the rate are fine as long as the average holds. Every /v1 response reports where you stand:

HeaderMeaning
X-RateLimit-LimitThe bucket size (25)
X-RateLimit-RemainingRequests left right now
X-RateLimit-ResetUnix seconds until the bucket is full again

Past the limit you get a 429 with Retry-After in seconds. Honour it and back off; the bucket refills within a second under any normal load. Limits are per key, so a busy integration can have a key of its own.

Idempotent retries

Send an Idempotency-Key header (any string up to 255 characters) with a POST and the API replays the first response for 24 hours to any retry carrying the same key, marked with Idempotent-Replayed: true. Reusing a key with a different request is a 422, never a silent overwrite. Keys are scoped to your API key, so two systems using plain counters never collide.

Use it on anything that must not happen twice: creating sales, partners, or payout batches on a network you do not trust to deliver your response.

Errors

Every error is the same shape:

{
  "error": {
    "type": "invalid_request_error",
    "code": "parameter_invalid",
    "message": "limit must be an integer between 1 and 100",
    "param": "limit"
  }
}

param names the offending parameter when there is one. The nine types:

TypeStatusWhen
authentication_error401Missing, malformed, unknown or revoked key
permission_error403The key lacks the scope; the message names the one it needed
invalid_request_error400, 422A parameter or body that does not parse or validate; a reused idempotency key
not_found_error404No such object in your organization (resource_missing)
conflict_error409The write contradicts current state
rate_limit_error429Over 25 requests per second
plan_error402The account's plan has lapsed; the key is paused, nothing is deleted
not_implemented_error501A documented capability not switched on for this deployment
api_error500Our fault; safe to retry with the same idempotency key

An id that exists but belongs to another organization returns the same 404 as one that never existed.

Versioning

Every response carries X-Rail-Version with a date (currently 2026-08-21). Additive changes, new fields, new endpoints, new enum values, do not bump it, so build parsers that ignore what they do not recognise. A breaking change becomes a new version, announced ahead of time, with the old behaviour kept for existing keys.

Live and test mode

Every response carries X-Rail-Livemode (true or false) from the key that made the request. Test keys see the same organization; assert on the header in your staging environment and a production key in staging fails loudly instead of quietly polluting live data.