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

# Authentication and rate limits

> How to pass a Modulate API key, which limits apply per model, and what each auth and limit error means.

Modulate exposes two API surfaces with different authentication schemes. The **Models API** covers every model endpoint documented on this site. The **Modulate Platform API** is a separate early-access orchestration surface, covered at the end of this page.

| Surface | Host | Auth |
| - | - | - |
| Models API | `platform.modulate.ai` | `X-API-Key` header (HTTP), `api_key` query parameter (WebSocket) |
| Modulate Platform API | `cloud-processing-api.modulate.ai` | `accountuuid` + `apikey` headers |

## API keys

Generate keys from the [API Keys page](https://platform.modulate.ai/dashboard/api-keys) in the dashboard.

A key belongs to an organization. Model access and usage limits are properties of the organization, not of the individual key, and they are fixed once the key is issued.

<Frame>
  <img src="https://mintcdn.com/modulateai/0NHKM9zXEnghoEfZ/images/api-key-screenshot.png?fit=max&auto=format&n=0NHKM9zXEnghoEfZ&q=85&s=f7f821fc70cb4803f70a1dcf60187ba5" alt="API key creation screen" width="307" height="624" data-path="images/api-key-screenshot.png" />
</Frame>

## Passing the key

HTTP endpoints take the key as a request header:

```http theme={null}
X-API-Key: YOUR_API_KEY
```

WebSocket endpoints take it as a query parameter on the connection URL. A header cannot be used, because the key must be present during the handshake:

```text theme={null}
wss://platform.modulate.ai/api/velma-2-stt-streaming?api_key=YOUR_API_KEY
```

This applies to every Models API endpoint. HTTP endpoints are the `/api/velma-2-*-batch` paths plus `/api/velma-2-batch`; WebSocket endpoints are the `/api/velma-2-*-streaming` paths plus `/api/velma-2-streaming`.

<Warning>
  API keys in WebSocket URLs can appear in server access logs and proxy logs. Avoid logging or persisting the full connection URL.
</Warning>

## Limits

Concurrency is capped **per model**: the number of requests or connections in flight at the same time against one endpoint. The default is 3. Reaching it on one model does not affect any other model.

Credits are separate. A request is rejected for insufficient credits whatever the concurrency situation.

Concurrency ceilings are set by Modulate per organization. To raise one, [contact us](/support#contact-us) with the model and the traffic you expect.

## Error responses

### Authentication

| Status | Meaning |
| - | - |
| `401` | The key is missing or invalid. |
| `403` | The key is valid but the request is not permitted, or the organization has no access to this model. |

A `403` has two causes. Read the `detail` field to tell them apart. The per-endpoint reference page lists the exact set each endpoint returns.

On WebSocket endpoints the handshake closes instead of returning a status:

| Close code | Meaning |
| - | - |
| `4001` | The `api_key` parameter is missing or invalid. |
| `4003` | The request is not permitted. |
| `4004` | The key has no access to this model. |

An `error` message carrying the reason is sent before each close.

### Limits

Every rejected-for-limits request returns `429`. The `detail` field names the cause:

| `detail` | Cause | Retry |
| - | - | - |
| `Concurrent request limit reached. Please retry after your in-flight requests complete.` | The model's concurrency cap is full. | Yes, after a short delay. |
| `Insufficient credits.` | The organization is out of credits. | Not until credits are added. |
| `Monthly usage limit reached.` | The organization's monthly usage limit has been reached. | Not until the next billing cycle, or the limit is raised. |

On WebSocket endpoints these arrive as close codes `4030`, `4029`, and `4031`.

Emotion Detection and Accent Detection (batch) declare `429` for insufficient credits only. Match on the `detail` string rather than on `429` alone.

For a backlog of files, bound the work with a semaphore rather than retrying into a full queue:

```python theme={null}
import asyncio

MAX_CONCURRENT = 3  # the model's concurrency cap
semaphore = asyncio.Semaphore(MAX_CONCURRENT)

async def transcribe_file(session, filepath):
    async with semaphore:
        ...
```

## Modulate Platform API

The Modulate Platform API (`cloud-processing-api.modulate.ai`) submits jobs that combine transcription with optional analysis features such as emotion, demographics, deepfake detection, and behavioral insights. It authenticates differently from the Models API.

<Warning>
  The Platform API is in **early access**. Any part of the contract can change before 1.0.0.
</Warning>

Every request carries two headers:

```http theme={null}
accountuuid: YOUR_ACCOUNT_UUID
apikey: YOUR_API_KEY
```

`accountuuid` comes from an account administrator or the Platform dashboard. `apikey` is a Platform key, issued separately from Models API keys.

A single `POST /api_service` endpoint accepts three submission patterns:

* **Real-time WebSocket**: `submission_type: "realtime_websocket"` with no files. The response carries a `realtime_url` for streaming through the [Pipecat Client SDK](https://docs.pipecat.ai/client/js/introduction).
* **Single-file batch**: one audio file of 5 MB or less in a single POST.
* **Multi-file batch**: one POST per file sharing a `job_id`, with `finalize_job: true` on the last.

All three then poll `GET /api_service/job_status/{job_id}` until `status='completed'`.

## Related

* [Troubleshooting](/guides/troubleshooting)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.