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

# Errors

> HTTP statuses and envelope error codes

uAPI uses conventional HTTP statuses and a stable `error.code` and `error.message` inside the envelope. Always read the HTTP status, then inspect the JSON body. The SDKs surface these errors as typed exceptions while preserving the underlying envelope for debugging.

## Envelope examples

<CodeGroup>
  ```json 400 Bad Request theme={null}
  {
    "id": "req_400",
    "success": false,
    "data": null,
    "error": {
      "code": "INVALID_OR",
      "message": "Invalid or unreachable URL"
    },
    "uapi_version": "1.2.5",
    "schema_version": "v1",
    "request": {
      "endpoint": "/v1/extract",
      "method": "GET",
      "status_code": 400
    },
    "deprecation_warnings": []
  }
  ```

  ```json 401 Unauthorized theme={null}
  {
    "id": "req_401",
    "success": false,
    "data": null,
    "error": {
      "code": "INVALID_API",
      "message": "Invalid API Key."
    },
    "uapi_version": "1.2.5",
    "schema_version": "v1",
    "request": {
      "endpoint": "/v1/extract",
      "method": "GET",
      "status_code": 401
    },
    "deprecation_warnings": []
  }
  ```

  ```json 403 Forbidden (beta-only endpoint) theme={null}
  {
    "id": "req_403",
    "success": false,
    "data": null,
    "error": {
      "code": "BAD_CHANNEL",
      "message": "Your API key is on the release channel, but this endpoint is only available in the beta channel."
    },
    "uapi_version": "1.2.5",
    "schema_version": "v1",
    "request": {
      "endpoint": "/v1/perform",
      "method": "POST",
      "status_code": 403
    },
    "deprecation_warnings": []
  }
  ```

  ```json 503 Service Unavailable theme={null}
  {
    "id": "req_503",
    "success": false,
    "data": null,
    "error": {
      "code": "DATABASE_CONNECTION",
      "message": "Database connection not available."
    },
    "uapi_version": "1.2.5",
    "schema_version": "v1",
    "request": {
      "endpoint": "/v1/extract",
      "method": "GET",
      "status_code": 503
    },
    "deprecation_warnings": []
  }
  ```

  ```json 500 Internal Server Error theme={null}
  {
    "id": "req_500",
    "success": false,
    "data": null,
    "error": {
      "code": "AN_INTERNAL",
      "message": "An internal error occurred."
    },
    "uapi_version": "1.2.5",
    "schema_version": "v1",
    "request": {
      "endpoint": "/v1/extract",
      "method": "GET",
      "status_code": 500
    },
    "deprecation_warnings": []
  }
  ```
</CodeGroup>

<Note>
  `error.code` is stable and designed for programmatic handling. `error.message` is human-readable and safe to log.
</Note>

## Recommended: SDK error handling

Use the official SDKs to handle transport errors, non-2xx statuses, and uAPI-specific failures in a single place.

### Python (usdk)

The Python SDK raises typed exceptions that wrap the uAPI envelope.

```python theme={null}
import os
import uapi
from uapi import uAPI

client = uAPI(api_key=os.environ.get("UAPI_API_KEY"))

try:
    resp = client.extract(url="https://invalid")
    # On success, resp is a typed model with .data, .error, etc.
    print(resp.to_dict())
except uapi.APIConnectionError as e:
    # Network, DNS, TLS, or timeout issues
    print("uAPI connection error:", e)
except uapi.RateLimitError as e:
    # 429 Too Many Requests
    print("Rate limited, back off:", e)
except uapi.APIStatusError as e:
    # Any non-2xx with a uAPI envelope
    body = e.response.json() if e.response is not None else {}
    code = body.get("error", {}).get("code")
    rid = body.get("id")
    print(f"uAPI error {code} (status={e.status_code}, request_id={rid})")
except uapi.APIError as e:
    # Fallback for any other library-specific error
    print("Unexpected uAPI error:", e)
```

Behavior:
uAPI returns conventional HTTP statuses and envelope fields.
The SDK maps them to:
400-series and 500-series → subclasses of `uapi.APIStatusError` (e.g. `BadRequestError`, `AuthenticationError`, `PermissionDeniedError`, `RateLimitError`, `InternalServerError`).
Connection/timeout issues → `APIConnectionError`.
All share `uapi.APIError` as a common base type.

### TypeScript / Node.js (usdk-js)

The TypeScript SDK throws `APIError` subclasses for non-2xx and connection issues.

```typescript theme={null}
import 'dotenv/config'
import uAPI from 'usdk-js'

const client = new uAPI({
  apiKey: process.env['UAPI_API_KEY'],
})

try {
  const result = await client.extract({ url: 'https://invalid' })
  console.log(result)
} catch (err) {
  if (err instanceof uAPI.APIError) {
    // HTTP status when available
    console.error('status:', err.status)
    console.error('name:', err.name) // e.g. BadRequestError, AuthenticationError
    console.error('headers:', err.headers)

    // Envelope body (if returned) is available via err.body or err.message content
    // Use this to read error.code and request.id when needed.
  } else {
    // Non-uAPI error (e.g. coding bug)
    throw err
  }
}
```

The SDK:
Surfaces 4xx/5xx as typed `APIError` variants with `status`, `name`, and headers.
Retries connection and selected 4xx/5xx errors by default with backoff.
Keeps your application logic focused on handling known failure modes.

## Raw HTTP handling

If you are not using an SDK, always:
Check `res.status`.
Parse the JSON.
If `success` is false, use `error.code` for branching and `error.message` and `id` for logs and support.

<CodeGroup>
  ```python Python (raw) theme={null}
  import os, requests

  r = requests.get(
    "https://api.uapi.nl/v1/extract",
    params={"url": "https://invalid"},
    headers={"X-API-Key": os.environ["UAPI_API_KEY"]},
    timeout=30
  )

  body = r.json()
  if not r.ok or body.get("success") is False:
    code = (body.get("error") or {}).get("code")
    msg = (body.get("error") or {}).get("message")
    rid = body.get("id")
    raise RuntimeError(f"uAPI error {code}: {msg} (request {rid})")
  ```

  ```javascript Node.js (raw) theme={null}
  import 'dotenv/config'
  import fetch from 'node-fetch'

  const qs = new URLSearchParams({ url: 'https://invalid' }).toString()
  const res = await fetch(`https://api.uapi.nl/v1/extract?${qs}`, {
    headers: { 'X-API-Key': process.env.UAPI_API_KEY }
  })

  const body = await res.json().catch(() => ({}))

  if (!res.ok || body.success === false) {
    const code = body?.error?.code
    const msg = body?.error?.message
    const rid = body?.id
    throw new Error(`uAPI error ${code}: ${msg} (request ${rid})`)
  }
  ```
</CodeGroup>

## Practical guidance

Use the SDKs wherever possible so transport issues, retries, and status mapping are handled consistently. In all environments, rely on:
HTTP status for coarse-grained control.
`error.code` for branching logic.
`id` and `request` fields for logging, observability, and support.
