# POST /v1/embeddings

Turn text into vectors. The route follows the OpenAI Embeddings API, so
`client.embeddings.create` works unchanged with the base URL `https://demo-api.routerplus.com/v1`.
`https://demo-api.routerplus.com/embeddings` is an alias. The route returns one JSON response and does not
stream.

## Examples

```bash
curl https://demo-api.routerplus.com/v1/embeddings \
  -H "Authorization: Bearer $ROUTERPLUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "text-embedding-3-small", "input": ["first text", "second text"]}'
```

```python
from openai import OpenAI

client = OpenAI(base_url="https://demo-api.routerplus.com/v1", api_key="tm_vk_...")
result = client.embeddings.create(model="text-embedding-3-small", input="hello world")
print(len(result.data[0].embedding), result.usage.prompt_tokens)
```

Send your key as `Authorization: Bearer tm_vk_...` or as `x-api-key: tm_vk_...`. A missing or
invalid key returns 401.

## Models

| Model | Provider | Price per 1M input tokens | Max tokens per input |
|---|---|---|---|
| `text-embedding-3-small` | OpenAI | $0.02 | 8,191 |
| `text-embedding-3-large` | OpenAI | $0.13 | 8,191 |
| `baai/bge-m3` | DeepInfra | $0.01 | 8,192 |
| `qwen/qwen3-embedding-8b` | DeepInfra | $0.01 | 32,768 |
| `intfloat/multilingual-e5-large` | DeepInfra | $0.01 | 512 |

`GET /v1/models?output_modalities=embeddings` lists the embeddings models your key can use.
You pay for input tokens only. Output tokens are free.

## Request

The body is JSON, at most 10 MB. RouterPlus checks every rule below before it reserves money
or calls a provider. A refused request has no ledger row and no charge.

| Parameter | Type | Behavior |
|---|---|---|
| `model` | string, required | An embeddings model id. A chat, image, video or decision model is a 400 that names its own route. An id that is not in the catalog is a 404. |
| `input` | required | A string, an array of strings, a token array (integers), or an array of token arrays. At most 2,048 inputs. An empty string, an empty array or an empty token array is a 400. |
| `encoding_format` | string | `float` (the default) or `base64`. |
| `dimensions` | integer | A positive integer. Only models that support it shorten their vectors (`text-embedding-3-*` and some open models). |
| `user` | string | Sent to the provider as is. |
| `provider` | object | Routing controls. RouterPlus reads them and does not send them to the provider. See [Provider routing](/docs/provider-routing). |

RouterPlus drops any other field and names it in the `x-tm-dropped-params` header. With
`provider.require_parameters: true`, a dropped field is a 400.

A 400 message starts with the field name, for example `input[1]: must not be an empty string`.

## Response

```json
{
  "object": "list",
  "data": [
    { "object": "embedding", "index": 0, "embedding": [0.0123, -0.0456] }
  ],
  "model": "text-embedding-3-small",
  "usage": { "prompt_tokens": 5, "total_tokens": 5, "cost": 0.0000001 }
}
```

| Field | Meaning |
|---|---|
| `data` | One item per input, in the order you sent them. |
| `embedding` | An array of floats. With `encoding_format: "base64"`, a base64 string of little-endian float32 values. |
| `model` | The id you sent. |
| `usage.prompt_tokens` | The count the provider reports. You are billed on it. |
| `usage.cost` | The charge in USD for this request. |

## Billing

1. Before the call, RouterPlus holds the estimated cost of your input on your balance.
2. After the call, it charges `usage.prompt_tokens` at the model's input price and releases the rest of the hold.
3. If the provider reports no usage, RouterPlus charges the estimate it held.

A failed attempt costs nothing. `GET /v1/generation` shows each attempt.

## Routing and failover

RouterPlus picks the provider with the same rules as chat. [Guardrails](/docs/guardrails)
apply: a model your guardrail does not allow is a 403 with `error_type: "guardrail_blocked"`.

If a provider fails before it answers (a 5xx, a 429 or a timeout), RouterPlus tries the next
provider of the model. `x-tm-attempts` gives the number of attempts. `x-tm-provider` names the
provider that answered.

A key with an OpenAI connection sends OpenAI embeddings requests on your own OpenAI key. See
[Bring your own key](/docs/byok).
