> This page is for Expression Measurement.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://dev.hume.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://dev.hume.ai/_mcp/server.

# Authentication

**Requests to the API carry a credential in an HTTP header.** The upload and run endpoints need it on every request. A realtime session sends it once, on the handshake that opens the WebSocket.

## Credentials

<table>
  <thead>
    <tr>
      <th width="15%">
        Credential
      </th>

      <th width="34%">
        Header
      </th>

      <th>
        Description
      </th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        API key
      </td>

      <td>
        `X-Hume-Api-Key: <key>`
      </td>

      <td>
        A key created for your organization, valid until expiry.
      </td>
    </tr>

    <tr>
      <td>
        Access token
      </td>

      <td>
        `Authorization: Bearer <token>`
      </td>

      <td>
        A short-lived token issued for your account.
      </td>
    </tr>
  </tbody>
</table>

Send exactly one credential per request.

## API keys

1. Sign in to the [Hume Platform](https://cloud.hume.ai/), or create an account.
2. On the [API keys page](https://cloud.hume.ai/settings/organization/api-keys), select **Create API key**.
3. Name the key for whatever will use it, and choose its permissions and when it expires.
4. Create the key and copy it. The key is shown only once.

> **Warning**
>
> A key's expiry cannot be extended. Before a key expires, create a new one and switch your code to it.

> **Info**
>
> Expression Measurement accepts only keys created in the [Hume Platform](https://cloud.hume.ai/). Keys from [app.hume.ai](https://app.hume.ai/), which serve Hume's other APIs, are rejected with `401 Unauthorized`.

## HTTP requests

Set the credential header on every request to an upload or run endpoint. The request below lists your organization's runs.

#### cURL

```bash
curl https://api.cloud.hume.ai/v1/expression/run \
  -H "X-Hume-Api-Key: $HUME_API_KEY"
```

#### Python

```python
import os

from hume_expression_measurement import ExpressionMeasurementClient

client = ExpressionMeasurementClient(api_key=os.environ["HUME_API_KEY"])

page = client.runs.list()
print(f"{len(page.runs)} runs")
```

#### Node.js

```javascript
import { ExpressionMeasurementClient } from "@humeai/expression-measurement-node";

const client = new ExpressionMeasurementClient({
  apiKey: process.env.HUME_API_KEY,
});

const page = await client.runs.list();
console.log(`${page.runs.length} runs`);
```

The SDKs send the key you pass to the client in this header on every request and connection. If you pass none, they read it from the `HUME_API_KEY` environment variable.

A missing, invalid, or expired credential is rejected with `401 Unauthorized` before the request reaches the service. The body has a `message`, plus a `request_id` when an API key was rejected, but no `type` or `code`. See [HTTP errors](/expression-measurement/docs/errors#http-errors).

#### 401 Unauthorized, rejected API key

```json
{
  "message": "Invalid or expired API key. Use a key created in the Hume Platform; keys from app.hume.ai do not work here. See https://dev.hume.ai/expression-measurement/docs/authentication#api-keys",
  "request_id": "5c0e2a7d9b4f41e3a8d6c1f07b2e9a44"
}
```

## WebSocket handshake

Set the credential header when opening the connection. The handshake request your client library sends looks like this:

#### Handshake request

```http
GET /v1/expression/audio/realtime HTTP/1.1
Host: api.cloud.hume.ai
Upgrade: websocket
Connection: Upgrade
X-Hume-Api-Key: <your API key>
```

A valid credential completes the upgrade, and the server immediately sends `session.created`. A missing, invalid, or expired credential is rejected with `401 Unauthorized`, and a handshake from an organization that is not permitted to use the endpoint is rejected with `403 Forbidden`. Both happen before any WebSocket exists, so there is no `error` message; read the status from your client library's handshake result.

## Browsers

An API key must never ship in client-side code, so call the API from a server you control. To use the API from a web page, relay through that server: the browser sends media to your server, which forwards it to Hume with the credential attached and returns the results. The realtime endpoints need this relay in any case, because browsers cannot set custom headers on a WebSocket handshake.

## Next steps

#### [Python quickstart](/expression-measurement/docs/quickstart/python)

Label datasets and evaluate audio from a script, notebook, or backend service.

#### [Node.js quickstart](/expression-measurement/docs/quickstart/nodejs)

Add expression measurement to a Node.js service or command-line tool.

#### [Limits](/expression-measurement/docs/limits)

The send rates and size limits of every endpoint.

#### [Errors](/expression-measurement/docs/errors)

The error format and the codes every endpoint returns.