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

# Error Handling

> Understand DualEntry Public API errors: standard HTTP status codes and a consistent JSON error format, with the meaning of each code.

The API uses standard HTTP status codes and returns errors in a consistent JSON format.

## HTTP status codes

| Status Code | Meaning                                |
| ----------- | -------------------------------------- |
| **200**     | Success                                |
| **201**     | Resource created                       |
| **400**     | Bad request (malformed request)        |
| **401**     | Missing API key                        |
| **402**     | Subscription required for this feature |
| **403**     | Invalid or revoked API key             |
| **404**     | Resource not found                     |
| **422**     | Validation error                       |
| **429**     | Rate limit exceeded                    |
| **500**     | Server error                           |
| **503**     | Service unavailable                    |

<Info>
  Create endpoints do not all return the same status code. Transaction and record endpoints (for example `POST /public/v2/bills/`) return **200**; master-data endpoints (for example `POST /public/v2/customers/`) return **201**. Check the specific endpoint in the [API reference](/developers/api/resources-v2/invoices/list-invoicev2-records) for the documented response code.
</Info>

## Error response format

All errors follow this structure:

```json theme={null}
{
  "success": false,
  "errors": {
    "<field_name|__all__>": ["error message"]
  }
}
```

* `errors.__all__`: General errors not specific to a field
* `errors.<field_name>`: Errors specific to a particular field

## Common errors

### Authentication error (403)

```json theme={null}
{
  "success": false,
  "errors": {
    "__all__": ["API key authentication failed"]
  }
}
```

### Subscription required (402)

Returned when your organization does not have the subscription required for the requested feature.

```json theme={null}
{
  "success": false,
  "errors": {
    "__all__": ["Subscription required for this feature"]
  }
}
```

### Validation error (422)

```json theme={null}
{
  "success": false,
  "errors": {
    "customer_id": ["Customer with this ID does not exist"],
    "date": ["Invalid date format. Use ISO 8601 format (YYYY-MM-DD)"],
    "lines": ["At least one line item is required"]
  }
}
```

### Not found (404)

```json theme={null}
{
  "success": false,
  "errors": {
    "__all__": ["Invoice matching query does not exist."]
  }
}
```

### Rate limit exceeded (429)

```json theme={null}
{
  "success": false,
  "errors": {
    "__all__": ["Rate limit exceeded. Please try again later."]
  }
}
```

## Handling errors

<Info>
  **Best Practices**:

  * Always check HTTP status codes
  * Parse the `errors` object for field-specific details
  * Implement retry logic for rate limits (429) and server errors (5xx)
  * Log error responses for debugging
</Info>

### Example

```python theme={null}
response = requests.post(url, headers=headers, json=data)

if response.status_code in (200, 201):
    # Success, 200 for record endpoints, 201 for master-data endpoints
    result = response.json()
elif response.status_code == 402:
    # Subscription not enabled for this feature
    print(response.json()["errors"]["__all__"])
elif response.status_code == 422:
    # Validation errors
    errors = response.json()["errors"]
    for field, messages in errors.items():
        print(f"{field}: {messages}")
elif response.status_code == 429:
    # Rate limited - wait and retry
    time.sleep(60)
    response = requests.post(url, headers=headers, json=data)
else:
    # Other error
    print(f"Error {response.status_code}: {response.json()}")
```

***

**Next:** [Explore the API Reference →](/developers/api/resources-v2/invoices/list-invoicev2-records)
