Skip to content

Rate Limiting

Outcome
Handle 429 responses without creating retry storms.
Prerequisites
An API client that can inspect status and response headers.
You'll use
Retry-After, bounded retries, caching, and webhooks.

The Space Invoices API enforces rate limits to ensure fair usage and protect service stability. When you exceed a rate limit, the API returns a 429 Too Many Requests response.

How It Works

General limits use fixed per-minute buckets. Bearer-authenticated traffic is limited per API token, with a higher shared-IP backstop; anonymous traffic uses an IP bucket. Authentication endpoints have a separate, stricter IP limit.

TrafficDefault limit
Bearer-authenticated token850 requests/minute per token
Authenticated shared-IP backstop3,400 requests/minute per IP
Anonymous/global IP traffic850 requests/minute per IP
Login, signup, and other auth-sensitive routes10 requests/minute per IP

These are service defaults and may be adjusted operationally. Always treat the returned 429 response and Retry-After header as authoritative.

If a request is rate limited, the response includes a Retry-After header indicating how many seconds to wait before retrying.

429 Response

When the rate limit is exceeded:

{
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded"
}

Headers:

HeaderDescription
Retry-AfterSeconds to wait before making another request

Handling Rate Limits

Handling curlbash
# title: Handling 429 responses
# Check for rate limit headers
curl -i "https://eu.spaceinvoices.com/invoices" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "X-Entity-Id: ent_123"

# Response headers include:
# Retry-After: 60
#
# If you receive HTTP 429, wait for the Retry-After duration before retrying.

Retry with Exponential Backoff

The recommended approach is to catch 429 responses and wait for the Retry-After duration:

Retry sdktypescript
// title: Retry with backoff
async function withRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch(error: any) {
      if (error.status === 429 && attempt < maxRetries - 1) {
        const retryAfter = Number(error.headers?.["retry-after"]) || 60;
        await new Promise((r) => setTimeout(r, retryAfter * 1000));
        continue;
      }
      throw error;
    }
  }
  throw new Error("Max retries exceeded");
}

// Usage
const _invoices = await withRetry(() => sdk.invoices.list());

Best Practices

  • Respect Retry-After — always wait the indicated duration before retrying
  • Batch operations — combine multiple items into a single request where the API supports it (e.g., bulk endpoints)
  • Cache responses — avoid re-fetching data that hasn’t changed
  • Use webhooks — instead of polling for changes, register webhooks to receive push notifications
  • Paginate efficiently — request only the data you need using appropriate limit values

Email Rate Limits

Email sending has separate per-document hourly/monthly controls, plus a sandbox account/day limit. If an email request is limited, use its Retry-After response instead of applying the general API figures above.