Rate Limiting
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.
| Traffic | Default limit |
|---|---|
| Bearer-authenticated token | 850 requests/minute per token |
| Authenticated shared-IP backstop | 3,400 requests/minute per IP |
| Anonymous/global IP traffic | 850 requests/minute per IP |
| Login, signup, and other auth-sensitive routes | 10 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:
| Header | Description |
|---|---|
Retry-After | Seconds to wait before making another request |
Handling Rate Limits
# 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:
// 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
limitvalues
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.