Errors & Rate Limits
Understanding API errors, status codes, and rate limiting behavior.
Error Response Format
When an error occurs, the API returns a JSON response with details about what went wrong. The response structure is consistent across all error types.
Standard Error Response
{
"detail": "Authentication credentials were not provided."
}
Validation Error Response
Field-level validation errors include the field name:
{
"email": ["Enter a valid email address."],
"quantity": ["Ensure this value is greater than or equal to 1."]
}
Detailed Error Response
Some errors include additional context:
{
"detail": "Not found.",
"code": "not_found"
}
Error Codes
Spwig uses structured error codes to help you identify and handle specific error conditions programmatically. Error codes follow a consistent pattern.
Error Code Format
Error codes use a hierarchical format: {category}_{action}_{reason}
| Code | Description | HTTP Status |
|---|---|---|
authentication_failed |
Invalid or missing authentication credentials | 401 |
not_authenticated |
Request requires authentication but none provided | 401 |
permission_denied |
User lacks required permissions for this action | 403 |
not_found |
The requested resource does not exist | 404 |
method_not_allowed |
HTTP method not supported for this endpoint | 405 |
throttled |
Rate limit exceeded, retry after waiting | 429 |
invalid |
Validation failed for one or more fields | 400 |
parse_error |
Malformed request body (invalid JSON, etc.) | 400 |
Reference IDs
For server errors (5xx), the API includes a unique reference ID in the format ERR-XXXXXX. Include this ID when contacting support to help identify the specific error in server logs.
{
"detail": "An unexpected error occurred.",
"reference": "ERR-A1B2C3"
}
HTTP Status Codes
The API uses standard HTTP status codes to indicate the result of a request:
| Code | Name | Description |
|---|---|---|
| 200 | OK | Request succeeded |
| 201 | Created | Resource created successfully |
| 204 | No Content | Request succeeded, no content to return (DELETE) |
| 400 | Bad Request | Invalid request syntax or parameters |
| 401 | Unauthorized | Missing or invalid authentication |
| 403 | Forbidden | Authenticated but lacking permission |
| 404 | Not Found | Resource doesn't exist |
| 405 | Method Not Allowed | HTTP method not supported for this endpoint |
| 409 | Conflict | Resource state conflict (e.g., duplicate entry) |
| 422 | Unprocessable Entity | Valid syntax but semantic errors |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Unexpected server error |
| 502 | Bad Gateway | Upstream service error |
| 503 | Service Unavailable | Server temporarily unavailable |
Common Errors
401 Authentication Failed
{"detail": "Invalid token."}
Cause: The API key is invalid, expired, or revoked.
Solution: Verify your API key is correct and active in the admin panel.
403 Permission Denied
{"detail": "You do not have permission to perform this action."}
Cause: Your API key or user account lacks the required permissions.
Solution: Check the endpoint's required permissions. Admin-only endpoints require admin credentials.
404 Not Found
{"detail": "Not found."}
Cause: The requested resource doesn't exist or the URL is incorrect.
Solution: Verify the resource ID and endpoint URL are correct.
400 Validation Error
{"quantity": ["Ensure this value is greater than or equal to 1."]}
Cause: One or more fields failed validation.
Solution: Check the field names in the response and correct the invalid values.
409 Conflict
{"detail": "A product with this SKU already exists."}
Cause: The request conflicts with existing data (e.g., duplicate unique field).
Solution: Use a different value or update the existing resource instead.
Rate Limiting
To ensure fair usage and protect the platform, the API enforces rate limits on requests. Rate limiting uses a dual-tier system with both burst (short-term) and sustained (long-term) limits. When you exceed either limit, you'll receive a 429 Too Many Requests response.
Rate Limit Tiers
| Request Type | Burst Limit | Sustained Limit |
|---|---|---|
| Anonymous (unauthenticated) | 60 requests/minute | 1,000 requests/hour |
| Authenticated (API key/token) | 120 requests/minute | 5,000 requests/hour |
| Admin authentication attempts | 5 requests/minute | — |
Dual-tier throttling: Both limits apply simultaneously. For example, an authenticated user can make 120 requests in 1 minute, but cannot exceed 5,000 in an hour—even if they stay under the burst limit.
Endpoint-Specific Limits
Some endpoints have additional rate limits to prevent abuse:
| Endpoint | Limit | Purpose |
|---|---|---|
/api/v1/vouchers/validate/ |
10/minute | Prevent voucher code guessing |
/api/v1/referrals/ |
5/minute | Prevent referral spam |
/api/v1/geoip/ |
30/minute | GeoIP lookups are resource-intensive |
Rate Limit Headers
Every response includes headers to help you track your usage:
| Header | Description |
|---|---|
X-RateLimit-Limit |
Maximum requests allowed in the window |
X-RateLimit-Remaining |
Requests remaining in current window |
X-RateLimit-Reset |
Unix timestamp when the window resets |
Retry-After |
Seconds to wait before retrying (on 429) |
Rate Limit Response
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704067200
Retry-After: 45
{
"detail": "Request was throttled. Expected available in 45 seconds."
}
Tip: Implement exponential backoff when you receive a 429 response. Start with the Retry-After value and double the wait time for subsequent retries.
Error Handling Best Practices
1. Check Status Codes First
Always check the HTTP status code before parsing the response body:
response = requests.get(url, headers=headers)
if response.status_code == 200:
data = response.json()
elif response.status_code == 429:
retry_after = int(response.headers.get('Retry-After', 60))
time.sleep(retry_after)
# Retry the request
elif response.status_code >= 400:
error = response.json()
handle_error(response.status_code, error)
2. Implement Retry Logic
For transient errors (5xx, 429), implement retry with exponential backoff:
import time
from functools import wraps
def retry_with_backoff(max_retries=3, base_delay=1):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_retries):
try:
response = func(*args, **kwargs)
if response.status_code < 500:
return response
except requests.exceptions.RequestException:
pass
delay = base_delay * (2 ** attempt)
time.sleep(delay)
raise Exception(f"Failed after {max_retries} retries")
return wrapper
return decorator
3. Log Errors with Context
Include relevant context when logging errors:
import logging
logger = logging.getLogger(__name__)
def handle_api_error(response, endpoint):
logger.error(
"API Error",
extra={
"status_code": response.status_code,
"endpoint": endpoint,
"response_body": response.text[:500], # Truncate for safety
"request_id": response.headers.get("X-Request-ID"),
}
)
4. Handle Validation Errors Gracefully
Parse field-level errors to provide helpful feedback:
def format_validation_errors(error_response):
"""Convert API validation errors to user-friendly messages."""
messages = []
for field, errors in error_response.items():
for error in errors:
messages.append(f"{field}: {error}")
return messages
Deprecation Policy
We strive to maintain backward compatibility. When breaking changes are necessary, we follow a structured deprecation process.
Deprecation Timeline
- Announcement: Deprecated features are announced in release notes and API documentation at least 6 months before removal.
- Warning Headers: Deprecated endpoints return a
Deprecationheader with the sunset date. - Sunset: After the deprecation period, endpoints return
410 Gonewith migration guidance.
Deprecation Headers
HTTP/1.1 200 OK
Deprecation: Sun, 01 Jan 2025 00:00:00 GMT
Sunset: Sun, 01 Jul 2025 00:00:00 GMT
Link: </api/v1/products-v2/>; rel="successor-version"
Monitor for deprecation headers: We recommend logging Deprecation headers in production to catch deprecated endpoint usage before sunset dates.
Version Support
The Spwig API does not use URL-based versioning (e.g., /v1/, /v2/). Instead, the platform version is reflected in the OpenAPI specification. Each release maintains backward compatibility within its major version.
Next Steps: Explore the API Reference or learn about Webhooks.