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

# Error Handling

> Handle API errors gracefully across envelope, raw JSON, and file-stream endpoints

## Error Response Format

API-generated errors generally use a consistent envelope, even when the successful response for that endpoint is raw JSON or a file stream:

```json theme={null}
{
  "success": false,
  "message": "Human-readable error description",
  "errors": {
    "field_name": "Specific error details",
    "general": "General error information"
  }
}
```

## HTTP Status Codes

| Code  | Name                  | Description                                           |
| ----- | --------------------- | ----------------------------------------------------- |
| `200` | OK                    | Request succeeded                                     |
| `202` | Accepted              | Async job was accepted or queued                      |
| `400` | Bad Request           | Invalid parameters or request body                    |
| `401` | Unauthorized          | Missing or invalid API key                            |
| `403` | Forbidden             | Access denied or quota exceeded                       |
| `404` | Not Found             | Resource does not exist or is not owned by the caller |
| `409` | Conflict              | Download requested before a job is ready              |
| `429` | Too Many Requests     | Rate limit or plan limit exceeded                     |
| `500` | Internal Server Error | Server-side error                                     |
| `502` | Bad Gateway           | Upstream service unavailable                          |
| `503` | Service Unavailable   | Temporary maintenance or dependency outage            |

<Note>
  Do not assume every successful `2xx` response includes a `success` field. Search endpoints and most point lookups do, but job endpoints, autocomplete endpoints, manifests, scanner endpoints, and download endpoints may not.
</Note>

## Common Errors

### 400 Bad Request

```json theme={null}
{
  "success": false,
  "message": "Invalid request",
  "errors": {
    "q": ["This field is required."]
  }
}
```

Typical causes:

* missing required parameters
* invalid `filter` JSON
* filter nesting deeper than 2 levels
* more than 50 filter leaf conditions
* unsupported filter operator or field for that endpoint
* wildcard-like patterns that are too short
* unsupported scanner `query_config` keys
* malformed `filter_id`

### 401 Unauthorized

```json theme={null}
{
  "success": false,
  "message": "Invalid API key."
}
```

Typical causes:

* missing `x-api-key`
* revoked or invalid key

### 403 Forbidden

Typical causes:

* daily lookup quota exhausted
* endpoint requires a higher plan
* scanner quota exhausted
* file search, exports, bulk search, or AI filter not enabled on the current plan

### 404 Not Found

Typical causes:

* unknown `log_id`, `file_id`, `job_id`, `scanner_uid`, or `run_uid`
* job or scanner exists but is not owned by the authenticated caller

### 429 Too Many Requests

Typical causes:

* endpoint rate limit exceeded
* daily bulk-search limit reached
* scanner creation or test-delivery rate limits hit

## Client Handling Pattern

When writing a client:

1. trust HTTP status first
2. parse JSON when the endpoint is documented as JSON
3. only inspect `success` when the payload actually includes it
4. treat download endpoints as raw text or binary streams

## Example Client Logic

<CodeGroup>
  ```python Python theme={null}
  import requests

  class OathNetError(Exception):
      def __init__(self, message: str, status_code: int, errors: dict | None = None):
          super().__init__(message)
          self.status_code = status_code
          self.errors = errors or {}


  def handle_json_response(response: requests.Response) -> dict:
      payload = response.json()

      if response.status_code >= 400:
          raise OathNetError(
              payload.get("message", "Request failed"),
              response.status_code,
              payload.get("errors"),
          )

      if "success" in payload and not payload["success"]:
          raise OathNetError(
              payload.get("message", "Request failed"),
              response.status_code,
              payload.get("errors"),
          )

      return payload
  ```

  ```javascript Node.js theme={null}
  class OathNetError extends Error {
    constructor(message, statusCode, errors = null) {
      super(message);
      this.statusCode = statusCode;
      this.errors = errors;
    }
  }

  async function handleJsonResponse(response) {
    const payload = await response.json();

    if (!response.ok) {
      throw new OathNetError(
        payload.message || "Request failed",
        response.status,
        payload.errors || null
      );
    }

    if ("success" in payload && !payload.success) {
      throw new OathNetError(
        payload.message || "Request failed",
        response.status,
        payload.errors || null
      );
    }

    return payload;
  }
  ```
</CodeGroup>
