Skip to main content
Every response carries IETF-standard RateLimit-* headers so you know your cap and when the current window resets. The limit is 300 requests / 10-second window, tracked as a separate bucket per org, per environment (LIVE or SANDBOX), with the same cap in both. All API keys under an org share the same environment bucket; creating more keys does not raise the limit. When you exceed it you get a 429; wait Retry-After seconds, then retry.

Rate-limit headers

RateLimit-Reset is a duration, not a timestamp. RateLimit-Reset: 8 means “the window resets in 8 seconds from now.” Compute your wait as RateLimit-Reset * 1000 milliseconds from when you received the response, not by comparing it against a clock. On a 429 or a 409 IDEMPOTENCY_IN_FLIGHT, prefer the Retry-After header (also seconds), the more specific, purpose-built signal for exactly when to retry.

Default limits

The limit is per org, per environment: bucketed by whichever X-App-Mode (LIVE or SANDBOX) the request used, shared across every API key under that org. LIVE and SANDBOX are separate buckets with the same cap; usage in one doesn’t count against the other.

Handling rate limits: pattern

Mint a fresh JWT and a fresh X-Request-Signature per attempt: do not capture once. JWTs are 60-second TTL and the signature is bound to the JWT’s nonce (Authentication). Retries across several attempts can add up to more than 60s of wall-clock time. A captured JWT will expire mid-loop and your next request returns 401 AUTH_TOKEN_EXPIRED (or a 401 AUTH_TOKEN_INVALID with details.reason: request_signature_invalid if you reused the signature). The pattern below takes a signRequest function that mints both on each attempt.

When you hit the limit

A 429 response uses the standard error envelope and includes a Retry-After header. Its details object is empty:
Headers on a 429:
Compute your wait as Retry-After * 1000 milliseconds (both Retry-After and RateLimit-Reset are a duration in seconds, not an epoch timestamp; prefer Retry-After, the more specific signal for exactly when to retry). The same Retry-After header is present on 409 IDEMPOTENCY_IN_FLIGHT too. See Idempotency.

Need a higher rate limit?

Email support@mail.openfx.com. Provide your org ID, whether you need it on LIVE or SANDBOX, and a sense of the burst pattern you need.

Common mistakes

  • Sleeping a fixed duration on 429 instead of waiting Retry-After seconds. A hard-coded sleep either thrashes the limit or wastes throughput. Compute the wait from Retry-After (or RateLimit-Reset as a fallback).
  • Treating RateLimit-Reset as an epoch timestamp. It’s a duration in seconds from the response, not a Unix timestamp: RateLimit-Reset * 1000 - Date.now() computes a nonsensical wait. Use RateLimit-Reset * 1000 directly.
  • Retrying a 429 indefinitely. A single reset-wait is not a license to retry forever. Cap retry attempts; on exhaustion, surface the error to the caller.
  • Capturing the JWT or signature outside the retry loop. JWTs are 60s TTL and the X-Request-Signature is bound to the JWT’s nonce; both must be re-minted per attempt. Pass a signRequest function so each attempt gets a fresh token AND a fresh signature. See the Warning above.
  • Polling near the rate-limit ceiling. A 300 req/10s ceiling and a 1-second poll across 30 background workers leaves zero headroom for actual user-driven requests. Pace well under the limit.
  • Assuming a support-granted limit increase applies to both environments. LIVE and SANDBOX are separate buckets; a higher ceiling granted on one doesn’t carry over to the other.

What’s next

Metadata & tracing

X-Trace-Id on every response.

Idempotency

In-flight requests and safe retries.