Errors & Rate Limiting
Standard error response format, HTTP status codes, and rate limiting policies across all ZapTicket API endpoints.
Error Response Format
All error responses from the ZapTicket API follow a consistent JSON structure. The HTTP status code indicates the error category, and the body provides a human-readable message.
{
"error": "A human-readable description of what went wrong"
}The error field is always a string. It's designed to be useful for debugging during development — do not display it directly to end users in production.
HTTP Status Codes
The API uses the following status codes consistently across all endpoints:
Success Codes
- 200 OK — Request succeeded. Response body contains the result.
- 201 Created — Resource created successfully. Response body contains the new resource.
Client Error Codes
- 400 Bad Request — Invalid request body, missing required fields, or validation failure.
- 401 Unauthorized — No token provided, token expired, or token invalid.
- 403 Forbidden — Token is valid but lacks permission for this action (wrong role, wrong workspace).
- 404 Not Found — Resource doesn't exist or doesn't belong to the current tenant.
- 409 Conflict — Action conflicts with current state (duplicate email, already converted, etc.).
- 429 Too Many Requests — Rate limit exceeded. Back off and retry.
Server Error Codes
- 500 Internal Server Error — Unexpected server failure. These are logged and monitored.
Error Examples
{
"error": "Field 'email' is required"
}{
"error": "Token has expired"
}{
"error": "Admin role required to access workspace settings"
}{
"error": "Conversation not found"
}{
"error": "A user with this email already exists"
}Rate Limiting
ZapTicket applies rate limiting at multiple levels to protect the service and ensure fair usage. When you exceed a limit, the API returns 429 Too Many Requests.
Rate Limit Policies
- Per-IP (general API) — 100 requests per minute
- Per-site-key (widget endpoints) — 60 requests per minute
- Auth endpoints (login/register) — 10 requests per 5 minutes per IP
429 Response
When rate limited, the response includes headers to help you determine when to retry:
HTTP/1.1 429 Too Many Requests
Retry-After: 32
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1705316832{
"error": "Rate limit exceeded. Try again in 32 seconds."
}Retry-After header contains the number of seconds to wait before retrying. The X-RateLimit-Reset header is a Unix timestamp for when the window resets.Best Practices
- Implement exponential backoff when receiving 429 responses
- Cache responses where possible (e.g. widget config doesn't change often)
- Use webhooks or SignalR for real-time updates instead of polling
- Batch operations where the API supports it