> ## Documentation Index
> Fetch the complete documentation index at: https://developers.benchmarkemail.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

# Errors

The Benchmark Email API uses standard HTTP status codes and returns structured error responses to help you diagnose and handle problems.

## Error Response Format

All errors return a JSON body with an `errors` array:

```json theme={null}
{
  "errors": [
    {
      "errorType": "ForbiddenError",
      "message": "API key missing required scope: contacts:write"
    }
  ]
}
```

Each error object contains:

| Field       | Type   | Description                                                                           |
| ----------- | ------ | ------------------------------------------------------------------------------------- |
| `errorType` | string | The error class name (e.g., `UnauthorizedError`, `ForbiddenError`, `ValidationError`) |
| `message`   | string | A human-readable description of what went wrong                                       |

The `errors` array typically contains a single error object. Validation errors may include additional fields like `field` or `fieldId` to identify the problematic input.

## HTTP Status Codes

| Status | Meaning           | When It Happens                                                                      |
| ------ | ----------------- | ------------------------------------------------------------------------------------ |
| `400`  | Bad Request       | Invalid request body, missing required fields, validation failures, duplicate values |
| `401`  | Unauthorized      | Invalid, expired, or inactive API key                                                |
| `403`  | Forbidden         | Valid key but missing required scope, or account not in good standing                |
| `404`  | Not Found         | Requested resource does not exist                                                    |
| `429`  | Too Many Requests | Hourly rate limit or monthly quota exceeded, or IP temporarily blocked               |

## Common Error Scenarios

### Invalid API Key (401)

Returned when the API key is malformed, does not exist, or has been deleted.

```bash theme={null}
curl -H "X-API-Key: bme_invalid_key_value" \
  https://api-us1-1.benchmarkemail.com/api/contact
```

```
HTTP/1.1 401 Unauthorized
```

```json theme={null}
{
  "errors": [
    {
      "errorType": "UnauthorizedError",
      "message": "Invalid API key"
    }
  ]
}
```

For security reasons, the error message does not distinguish between a malformed key, a nonexistent key, or a deleted key.

### Expired API Key (401)

Returned when the API key's expiration date has passed.

```
HTTP/1.1 401 Unauthorized
```

```json theme={null}
{
  "errors": [
    {
      "errorType": "UnauthorizedError",
      "message": "API key expired"
    }
  ]
}
```

To resolve this, create a new API key or regenerate the expired one from the API Keys page.

### Inactive API Key (401)

Returned when the API key has been deactivated by the account owner.

```
HTTP/1.1 401 Unauthorized
```

```json theme={null}
{
  "errors": [
    {
      "errorType": "UnauthorizedError",
      "message": "Invalid API key"
    }
  ]
}
```

Inactive keys return the same generic message as invalid keys. Reactivate the key from the API Keys page to restore access.

### Missing Required Scope (403)

Returned when the API key is valid but does not have the scope required for the requested operation. The error message tells you which scope is needed.

```bash theme={null}
# Attempting to create a contact with a read-only key
curl -X POST \
  -H "X-API-Key: bme_abc123def456ghi789jkl012mno345pqr678stu90v" \
  -H "Content-Type: application/json" \
  -d '{"email": "test@example.com"}' \
  https://api-us1-1.benchmarkemail.com/api/contact
```

```
HTTP/1.1 403 Forbidden
```

```json theme={null}
{
  "errors": [
    {
      "errorType": "ForbiddenError",
      "message": "API key missing required scope: contacts:write"
    }
  ]
}
```

To resolve this, either:

* Edit the key's scopes from the API Keys page to add the required permission.
* Create a new key with the appropriate scopes.

### Endpoint Not Accessible via API Key (403)

Returned when the endpoint is not available for API key access (e.g., billing, user management, or admin endpoints).

```
HTTP/1.1 403 Forbidden
```

```json theme={null}
{
  "errors": [
    {
      "errorType": "ForbiddenError",
      "message": "This endpoint is not accessible via API key"
    }
  ]
}
```

Only endpoints listed in the [API Reference](./README.md#available-resources) and [OpenAPI spec](./openapi.json) are accessible via API key.

### Account Not in Good Standing (403)

Returned when the API key is valid but the account status does not allow API access (e.g., suspended, past due, or terminated accounts).

```
HTTP/1.1 403 Forbidden
```

```json theme={null}
{
  "errors": [
    {
      "errorType": "ForbiddenError",
      "message": "Account does not have API access"
    }
  ]
}
```

API keys work when the account status is `open` or `pending_cancel`. See [Authentication](./authentication.md#account-standing) for details.

### Resource Not Found (404)

Returned when the requested resource does not exist.

```bash theme={null}
curl -H "X-API-Key: bme_abc123def456ghi789jkl012mno345pqr678stu90v" \
  https://api-us1-1.benchmarkemail.com/api/contact/000000000000000000000000
```

```
HTTP/1.1 404 Not Found
```

```json theme={null}
{
  "errors": [
    {
      "errorType": "RecordNotFound",
      "message": "Contact not found"
    }
  ]
}
```

### Validation Error (400)

Returned when the request body fails validation (e.g., missing required fields, invalid field values).

```bash theme={null}
curl -X POST \
  -H "X-API-Key: bme_abc123def456ghi789jkl012mno345pqr678stu90v" \
  -H "Content-Type: application/json" \
  -d '{"key": "test@example.com"}' \
  https://api-us1-1.benchmarkemail.com/api/contact
```

```
HTTP/1.1 400 Bad Request
```

```json theme={null}
{
  "errors": [
    {
      "errorType": "ValidationError",
      "message": "contactStructureId is required",
      "field": "contactStructureId"
    }
  ]
}
```

### Duplicate Field (400)

Returned when the operation conflicts with existing data (e.g., creating an API key with a name that already exists).

```
HTTP/1.1 400 Bad Request
```

```json theme={null}
{
  "errors": [
    {
      "errorType": "DuplicateFieldError",
      "message": "API key name is already in use"
    }
  ]
}
```

### Rate Limit Exceeded (429)

Returned when the hourly rate limit or monthly quota is exceeded. See [Rate Limits](./rate-limits.md) for full details.

**Hourly limit exceeded:**

```
HTTP/1.1 429 Too Many Requests
Retry-After: 45
X-RateLimit-Limit: 3600
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1711828800
```

```json theme={null}
{
  "errors": [
    {
      "errorType": "TooManyRequestsError",
      "message": "Rate limit exceeded. Retry after 45 seconds."
    }
  ]
}
```

**Monthly quota exceeded:**

```
HTTP/1.1 429 Too Many Requests
Retry-After: 86400
```

```json theme={null}
{
  "errors": [
    {
      "errorType": "TooManyRequestsError",
      "message": "Monthly API quota exceeded."
    }
  ]
}
```

The `Retry-After` header indicates the number of seconds to wait before retrying. For hourly limits, this is the time until the current window resets. For monthly quotas, this is the time until the billing period resets.

### IP Temporarily Blocked (429)

Returned when your IP address has been temporarily blocked due to too many failed authentication attempts.

```
HTTP/1.1 429 Too Many Requests
Retry-After: 900
```

```json theme={null}
{
  "errors": [
    {
      "errorType": "TooManyRequestsError",
      "message": "Too many failed authentication attempts. Please try again later."
    }
  ]
}
```

Stop retrying and verify your API key is correct. The block will automatically expire after the time indicated in `Retry-After`.

## Error Handling Best Practices

1. **Always check the status code first.** Use the HTTP status to determine the category of error before parsing the response body.

2. **Retry on 429 only.** Use the `Retry-After` header to determine when to retry. Do not retry `401` or `403` errors -- they will not resolve without configuration changes.

3. **Log the full error response.** Include the `errorType` and `message` in your logs for debugging.

4. **Handle scope errors proactively.** If you receive a `403` with a scope message, update your API key's permissions in Settings rather than retrying the request.

5. **Use exponential backoff for rate limits.** See the [Rate Limits](./rate-limits.md#handling-rate-limits) guide for retry strategies.

## Next Steps

* [Rate Limits](./rate-limits.md) -- detailed rate limiting information
* [Authentication](./authentication.md) -- API key setup and scopes
* [API Reference](./README.md) -- explore available resources
