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

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.