# Authentication (https://upapi.io/docs/authentication)



Every machine call carries an API key in the `x-api-key` header. There is no
OAuth flow, no bearer token and no signature to compute for the REST gateway.

<Tabs groupId="upapi-docs-lang" items="['cURL', 'Node', 'Python']">
  <Tab value="cURL">
    ```bash
    curl -X POST https://api.upapi.io/github-trending.get \
      -H 'x-api-key: upapi_YOUR_KEY' \
      -H 'content-type: application/json' \
      -d '{}'
    ```
  </Tab>

  <Tab value="Node">
    ```ts
    await fetch('https://api.upapi.io/github-trending.get', {
      method: 'POST',
      headers: {
        'x-api-key': process.env.UPAPI_API_KEY,
        'content-type': 'application/json',
      },
      body: '{}',
    });
    ```
  </Tab>

  <Tab value="Python">
    ```python
    import os, requests

    requests.post(
        'https://api.upapi.io/github-trending.get',
        headers={'x-api-key': os.environ['UPAPI_API_KEY']},
        json={},
    )
    ```
  </Tab>
</Tabs>

The same header authenticates the [usage endpoint](https://upapi.io/docs/usage-api) and the
[local MCP server](https://upapi.io/docs/mcp).

## Key format [#key-format]

A key is the prefix `upapi_` followed by 48 hexadecimal characters — 54
characters in total.

```
upapi_YOUR_KEY_HERE
```

A real key has 48 hex characters after the prefix; the placeholder above is not
one. The first 12 characters are kept in plain text so the dashboard can label a key
in a list. Everything else exists only as a SHA-256 hash. A presented key is
hashed and matched against that hash, which is why a lost key cannot be
recovered — only replaced.

## Keys belong to the organization [#keys-belong-to-the-organization]

A key is scoped to the organization that created it, not to the person. Two
consequences worth designing around:

* Every member with admin rights sees and manages the same set of keys, and a
  key keeps working when its creator leaves.
* Usage and quota are pooled across all of an organization's keys. Ten keys do
  not give you ten quotas — see [Rate limits](https://upapi.io/docs/rate-limits).

Keys carry no per-operation scoping today. A key can call every operation
published to your organization.

## Limits [#limits]

|                          |                |
| ------------------------ | -------------- |
| Keys per organization    | 20             |
| New keys per hour        | 10             |
| Times a key is displayed | 1, at creation |

## Managing keys [#managing-keys]

Keys are created and deleted from
[Dashboard → API keys](https://app.upapi.io/dashboard/api-keys), by an
organization admin.

<Callout title="There is no key-management REST API">
  Creating, listing and deleting keys happen through the dashboard only. If you are building
  provisioning automation, that is not available yet — do not plan around an endpoint that does not
  exist.
</Callout>

Deleting a key removes it outright rather than flagging it as revoked. Calls
presenting a deleted key fail immediately with `UNAUTHORIZED`.

## Failure modes [#failure-modes]

| Situation                                        | Status | Code                  |
| ------------------------------------------------ | ------ | --------------------- |
| No `x-api-key` header                            | 401    | `UNAUTHORIZED`        |
| Unknown or deleted key                           | 401    | `UNAUTHORIZED`        |
| Key is valid, action is not allowed on your plan | 403    | `FORBIDDEN`           |
| Our auth infrastructure is down                  | 503    | `SERVICE_UNAVAILABLE` |

<Callout type="warn" title="503 is not a credential problem">
  A 503 here means we could not check your key, not that your key is bad. Retry rather than rotating
  credentials — see [Errors](https://upapi.io/docs/errors). Key-authenticated callers get `SERVICE_UNAVAILABLE`;
  `AUTH_UNAVAILABLE` is the same outage seen from a session surface such as the dashboard or the
  hosted MCP endpoint.
</Callout>
