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

> ## Agent Instructions
> Unkey is two separate products. Compute builds, deploys, and runs apps behind a gateway. API Management issues API keys, enforces rate limits, manages identities and permissions, and reports usage. Say which product a page belongs to; a reader can use either without the other.
> Every Unkey API endpoint is an HTTP POST to https://api.unkey.com/v2/{service}.{procedure} with a root key in the Authorization: Bearer header. Root keys are workspace scoped.
> Error codes have the form err:{system}:{category}:{specific} and each has a page at /errors/{system}/{category}/{specific}.
> The word environment means production or preview in Compute. Rate limiting has four meanings on this site; the glossary lists them.

# Python SDK

> Manage keys and rate limits from Python with unkey.py.

export const versions = {
  cli: "2.0.150",
  tsApi: "2.5.1",
  tsRatelimit: "2.1.4",
  tsHono: "2.0.0",
  tsNextjs: "2.0.0",
  tsCache: "1.5.0",
  tsNuxt: "1.1.15",
  goSdk: "v3.0.1",
  pySdk: "3.0.3"
};

Use `unkey.py` to call the Unkey API from Python. The current release is {versions.pySdk}, and it needs Python 3.10 or newer. Every method has a regular and an `_async` version. Methods are grouped as `unkey.keys`, `unkey.apis`, `unkey.identities`, `unkey.permissions`, `unkey.ratelimit`, `unkey.analytics`, and `unkey.portal`.

<Note>
  You need a root key with the permissions listed on this page. Create one in the dashboard under **Settings > Root Keys**. See [Permission reference](/docs/platform/root-keys/permissions-legacy) for every permission.
</Note>

## Install

<CodeGroup>
  ```bash uv theme={"system"}
  uv add unkey.py
  ```

  ```bash pip theme={"system"}
  pip install unkey.py
  ```

  ```bash poetry theme={"system"}
  poetry add unkey.py
  ```
</CodeGroup>

## Construct the client

```python theme={"system"}
import os
from unkey.py import Unkey

with Unkey(root_key=os.environ["UNKEY_ROOT_KEY"]) as unkey:
    ...
```

`root_key` is your root key. The client is a context manager that closes its connections on exit. In a long-running service, create one client and reuse it. To change retries, pass `retry_config=RetryConfig(...)` from `unkey.py.utils` to the client or to one call.

## Verify a key

<CodeGroup>
  ```python Sync theme={"system"}
  res = unkey.keys.verify_key(key="prod_abc123", permissions="documents.read")

  if not res.data.valid:
      print(res.data.code)  # NOT_FOUND, FORBIDDEN, INSUFFICIENT_PERMISSIONS, USAGE_EXCEEDED, RATE_LIMITED, DISABLED, EXPIRED
  ```

  ```python Async theme={"system"}
  import asyncio
  import os
  from unkey.py import Unkey

  async def main():
      async with Unkey(root_key=os.environ["UNKEY_ROOT_KEY"]) as unkey:
          res = await unkey.keys.verify_key_async(key="prod_abc123")
          print(res.data.valid)

  asyncio.run(main())
  ```
</CodeGroup>

Request fields are `snake_case` keyword arguments (`tags`, `permissions`, `credits`, `ratelimits`). An invalid key isn't an exception: `data.valid` is false and `data.code` says why. See [Verifying keys](/docs/api-management/keys/verifying-keys) for what each check means and [keys.verifyKey](/docs/api-management/api-reference/keys/verify-api-key) for the schema.

## Create a key

```python theme={"system"}
res = unkey.keys.create_key(
    api_id="api_1234abcd",
    prefix="prod",
    name="Payment service",
    external_id="user_42",
    permissions=["documents.read"],
    enabled=True,
)

print(res.data.key_id)
print(res.data.key)  # returned once; store it or hand it to the user now
```

The remaining fields (`byte_length`, `meta`, `roles`, `expires`, `credits`, `ratelimits`, `recoverable`) are described in [Creating keys](/docs/api-management/keys/creating-keys), and the endpoint is [keys.createKey](/docs/api-management/api-reference/keys/create-api-key).

## Apply a rate limit

```python theme={"system"}
res = unkey.ratelimit.limit(
    namespace="email.send",
    identifier="user_42",
    limit=10,
    duration=60_000,  # milliseconds
)

if not res.data.success:
    ...  # res.data.remaining and res.data.reset (Unix ms) tell the caller when to retry
```

The endpoint is [ratelimit.limit](/docs/api-management/api-reference/ratelimit/apply-rate-limiting).

## Handle errors

HTTP errors raise an `errors.UnkeyError`, with `message`, `status_code`, `headers`, `body`, and `raw_response`. Each status has its own subclass with a typed `data` attribute holding the API's `meta` and `error` fields.

```python theme={"system"}
from unkey.py import Unkey, errors

try:
    res = unkey.keys.get_key(key_id="key_1234abcd")
except errors.NotFoundErrorResponse as e:
    print(e.data.meta.request_id, e.data.error.detail)
except errors.UnkeyError as e:
    print(e.status_code, e.message)
```

The subclasses are `BadRequestErrorResponse` (400), `UnauthorizedErrorResponse` (401), `ForbiddenErrorResponse` (403), `NotFoundErrorResponse` (404), `ConflictErrorResponse` (409), `GoneErrorResponse` (410), `PreconditionFailedErrorResponse` (412), `UnprocessableEntityErrorResponse` (422), `TooManyRequestsErrorResponse` (429), `InternalServerErrorResponse` (500), and `ServiceUnavailableErrorResponse` (503). Network failures raise `httpx.RequestError` subclasses such as `httpx.ConnectError` and `httpx.TimeoutException`. Each endpoint page in the API reference, for example [keys.getKey](/docs/api-management/api-reference/keys/get-api-key), lists which errors it can return.

## Next steps

<Columns cols={2}>
  <Card title="Creating keys" icon="key" href="/docs/api-management/keys/creating-keys">
    Every field a key can carry.
  </Card>

  <Card title="API reference" icon="code" href="/docs/api-management/api-reference/keys/verify-api-key">
    Request and response fields for every method.
  </Card>
</Columns>
