Error Handling
Understand API error responses and how to handle them
All Postkit API errors return a consistent JSON structure with a type, message, and code. Understanding each error type helps you handle failures gracefully and build resilient integrations.
Error response format
Every error response from the Postkit API follows the same structure:
{
"error": {
"type": "invalid_request",
"message": "The 'to' field must contain at least one recipient.",
"code": "missing_required_field"
}
}The three fields serve different purposes:
type-- The category of error. There are six possible types, each mapping to a specific HTTP status code. Use this for broad error handling (e.g., "retry all rate limit errors").message-- A human-readable explanation of what went wrong. Useful for logging and debugging, but do not parse this programmatically as the wording may change.code-- A machine-readable error code that identifies the specific problem. Use this for precise error handling (e.g., "show a specific message whenmissing_required_field").
Error types
invalid_request (400)
The request body is malformed, a required field is missing, or a field value is invalid.
{
"error": {
"type": "invalid_request",
"message": "The 'to' field must contain at least one recipient.",
"code": "missing_required_field"
}
}Common causes:
- Missing required fields (
from,to,subject) - Invalid email address format
- Field value exceeds maximum length
- Invalid JSON in the request body
- Unsupported content type
How to fix: Check your request payload against the API Reference. Validation errors include a descriptive message that points to the problematic field.
authentication_error (401)
The API key is missing, invalid, or does not have permission for the requested operation.
{
"error": {
"type": "authentication_error",
"message": "Invalid API key provided.",
"code": "invalid_api_key"
}
}Common causes:
- No
Authorizationheader included - API key is malformed or has been revoked
- Using a test key (
pk_test_) on a production endpoint that requires a live key - Key does not have the required scope for the endpoint
How to fix: Verify your API key starts with pk_live_ (for production) or pk_test_ (for testing) and is included in the Authorization header as Bearer pk_live_.... Check that the key has not been revoked in the Postkit dashboard.
not_found (404)
The requested resource does not exist, or it belongs to a different organization.
{
"error": {
"type": "not_found",
"message": "The requested resource does not exist.",
"code": "resource_not_found"
}
}Common causes:
- Incorrect resource ID (check the prefix:
em_for emails,dom_for domains,wh_for webhooks, etc.) - The resource was deleted
- The resource belongs to a different organization than the one authenticated by your API key
How to fix: Verify the resource ID and ensure it belongs to your organization. Resource IDs use typed prefixes -- if you are looking for an email, the ID should start with em_.
For security reasons, Postkit returns the same not_found error whether a resource does not exist or belongs to another organization. This prevents enumeration attacks.
rate_limit_error (429)
You have exceeded the API rate limit for your organization.
{
"error": {
"type": "rate_limit_error",
"message": "Rate limit exceeded. Retry after 1 second.",
"code": "rate_limit_exceeded"
}
}How to fix: Implement exponential backoff and respect the Retry-After header. See Rate Limits & Quotas for detailed guidance on handling rate limits and the specific limits per endpoint.
conflict (409)
The request conflicts with the current state of a resource. This typically occurs when you attempt an operation that is no longer valid.
{
"error": {
"type": "conflict",
"message": "Email has already been delivered and cannot be cancelled.",
"code": "email_already_delivered"
}
}Common causes:
- Canceling an email that has already been delivered
- Creating a resource that already exists (e.g., adding a domain that is already verified)
- Attempting a state transition that is not valid for the resource's current state
How to fix: Check the current state of the resource before attempting the operation. For email cancellation, verify the email status is still queued or scheduled before sending a cancel request.
internal_error (500)
An unexpected error occurred on the server side.
{
"error": {
"type": "internal_error",
"message": "An internal error occurred. Please try again.",
"code": "internal_server_error"
}
}How to fix: Retry with exponential backoff. Internal errors are usually transient. If the error persists across multiple retries, contact support with the request details and timestamps.
Best practices
-
Check the HTTP status code first. Use the status code for broad categorization (is this a client error or server error?), then parse the error body for specifics.
-
Implement retry logic for 429 and 500 errors. Rate limit errors and internal errors are often transient. Use exponential backoff with the
Retry-Afterheader for 429 responses. -
Do not retry 400 and 401 errors. These indicate problems with your request that will not resolve on their own. Fix the request before retrying.
-
Log the full error response. Include the
type,message, andcodefields in your logs. This makes debugging much faster when issues arise. -
Use idempotency keys for safe retries. When retrying email sending operations, always include an
Idempotency-Keyheader to prevent duplicate sends. See Idempotency for details. -
Handle errors at the right level. Some errors (like authentication failures) should bubble up to your application's error handling. Others (like rate limits) should be handled automatically by your HTTP client with retry logic.
What's next?
- Rate Limits & Quotas -- understand rate limit headers and pagination
- API Reference -- endpoint-specific error documentation