Error Handling & Rate Limits
All API errors return consistent JSON responses. This page covers HTTP status codes, error response format, rate limiting, and recommended retry strategies.
Error Response Format
All error responses return a JSON object with an error field describing what went wrong and a timestamp. Validation errors include a properties map with per-field details.
{"error": "Only draft shipments can be submitted","timestamp": "2026-03-08T12:00:00.000Z"}
{"error": "Oops! Something's not right. Please check your entries and try again.","properties": {"name": "name must be a string","quantity": "quantity must be a positive number"},"timestamp": "2026-03-08T12:00:00.000Z"}
HTTP Status Codes
| Code | Meaning | What to Do |
|---|---|---|
200 | Success | Request completed successfully. |
201 | Created | Resource created successfully. |
204 | No Content | Delete completed. No response body. |
400 | Bad Request | Fix the request body or parameters. Check the error and properties fields for details. |
401 | Unauthorized | Token missing, invalid, or expired. Refresh your token. |
403 | Forbidden | Your token is missing a required OAuth scope for this endpoint. Re-authorize with the correct scopes. |
404 | Not Found | Resource doesn't exist or belongs to another organization. Check the ID. |
429 | Too Many Requests | Rate limited. Wait and retry with exponential backoff. See below. |
500 | Internal Server Error | Something went wrong on our end. Retry after a short delay. If persistent, contact support. |
Rate Limits
The API enforces rate limits to ensure fair usage and system stability. Limits are applied per OAuth client.
Sandbox
requests per hour
Production
Based on your plan. Contact us for details.
Rate Limit Headers
Every response includes headers to help you track your usage:
| Header | Description |
|---|---|
X-RateLimit-Limit | Max requests allowed in the current window |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Retry Strategy
For transient errors (429, 500, 502, 503), use exponential backoff with jitter:
async function apiRequest(url, options, maxRetries = 3) {for (let attempt = 0; attempt <= maxRetries; attempt++) {const res = await fetch(url, options);if (res.ok) return res.json();// Don't retry client errors (except 429)if (res.status < 500 && res.status !== 429) {throw new Error(`API error ${res.status}: ${(await res.json()).error}`);}if (attempt < maxRetries) {// Exponential backoff: 1s, 2s, 4s + random jitterconst delay = Math.pow(2, attempt) * 1000 + Math.random() * 1000;await new Promise(r => setTimeout(r, delay));}}throw new Error("Max retries exceeded");}
Common Error Scenarios
400Validation ErrorsMissing required fields, invalid field types, or business rule violations. Check the properties map for per-field errors. Fix the request and retry.
401Token ExpiredAccess tokens expire after 1 hour. Use your refresh token to get a new access token, then retry the original request.
429Rate LimitedYou've exceeded the request limit. Check X-RateLimit-Reset to know when you can resume. Use exponential backoff.
400Insufficient StockSPD submission failed because there aren't enough cartons in stock. Check available stock via the inventory endpoints before submitting.
Best practices
- Always check the
errorfield in error responses for details, andpropertiesfor per-field validation errors. - Implement automatic token refresh on 401 errors to avoid disrupting users.
- Monitor rate limit headers proactively to avoid hitting limits.
- Use idempotent request patterns where possible to make retries safe.
- Log error responses with request IDs for debugging and support tickets.