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

# Embeddings

> Turn text into vectors with POST /v1/embeddings, then compare them to rank documents by meaning.

Send text to `POST /v1/embeddings` and NagaAI returns one vector per input. Texts with similar meaning get vectors that point the same way, so you can rank documents against a query by comparing vectors. The endpoint follows the OpenAI Embeddings format, and the OpenAI SDK works unchanged. Choose a model whose `operations` include `embeddings` in [`GET /v1/models`](https://docs.naga.ac/get-started/models).

## Rank documents against a query

The example embeds a search query together with three support articles, then sorts the articles by cosine similarity to the query.

```python Python theme={"theme":{"light":"github-light-default","dark":"github-dark-dimmed"}}
import math
import os

from openai import OpenAI

client = OpenAI(base_url="https://api.naga.ac/v1", api_key=os.environ["NAGA_API_KEY"])

query = "I forgot my login"
articles = [
    "How do I reset my password?",
    "Where can I download my invoice?",
    "How do I delete my account?",
]

result = client.embeddings.create(model="text-embedding-3-small", input=[query, *articles])
vectors = [item.embedding for item in sorted(result.data, key=lambda item: item.index)]


def cosine(a, b):
    dot = sum(x * y for x, y in zip(a, b))
    return dot / (math.sqrt(sum(x * x for x in a)) * math.sqrt(sum(y * y for y in b)))


ranked = sorted(zip(articles, vectors[1:]), key=lambda pair: cosine(vectors[0], pair[1]), reverse=True)
for article, vector in ranked:
    print(f"{cosine(vectors[0], vector):.3f}  {article}")
```

```javascript Node.js theme={"theme":{"light":"github-light-default","dark":"github-dark-dimmed"}}
import OpenAI from "openai";

const client = new OpenAI({ baseURL: "https://api.naga.ac/v1", apiKey: process.env.NAGA_API_KEY });

const query = "I forgot my login";
const articles = [
  "How do I reset my password?",
  "Where can I download my invoice?",
  "How do I delete my account?",
];

const result = await client.embeddings.create({
  model: "text-embedding-3-small",
  input: [query, ...articles],
});
const vectors = [...result.data].sort((a, b) => a.index - b.index).map((item) => item.embedding);

function cosine(a, b) {
  const dot = a.reduce((sum, x, i) => sum + x * b[i], 0);
  return dot / (Math.hypot(...a) * Math.hypot(...b));
}

const ranked = articles
  .map((article, i) => ({ article, score: cosine(vectors[0], vectors[i + 1]) }))
  .sort((a, b) => b.score - a.score);

for (const { article, score } of ranked) console.log(score.toFixed(3), article);
```

```bash cURL theme={"theme":{"light":"github-light-default","dark":"github-dark-dimmed"}}
curl https://api.naga.ac/v1/embeddings \
  -H "Authorization: Bearer $NAGA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "text-embedding-3-small", "input": ["I forgot my login", "How do I reset my password?", "Where can I download my invoice?", "How do I delete my account?"]}'
```

The password article should score highest, because its meaning is closest to the query even though it shares almost no words with it.

## Read the response

`data` holds one entry per input. Match each vector to its input by `index`, not by position. `usage.prompt_tokens` is what the request bills on. The `embedding` array below is shortened to three numbers for the page:

```json theme={"theme":{"light":"github-light-default","dark":"github-dark-dimmed"}}
{
  "object": "list",
  "model": "text-embedding-3-small",
  "data": [
    {"object": "embedding", "index": 0, "embedding": [0.0123, -0.0456, 0.0789]}
  ],
  "usage": {"prompt_tokens": 5, "total_tokens": 5}
}
```

## Choose the input and the vector format

`input` is a string, a list of strings, a list of token ids, or a list of such lists. Send many texts in one request instead of one request per text.

| Field | Notes |
| - | - |
| `encoding_format` | `float` (default) or `base64`. Base64 holds little-endian float32 values: smaller on the wire, and the same numbers as `float` rounded to float32 |
| `dimensions` | Shorter vectors, on models that support it. A provider that doesn't support it ignores the field and returns the full length |

Vector lengths differ by model: 1536 for `text-embedding-3-small`, 3072 for `gemini-embedding-001`, 4096 for `qwen3-embedding-8b`. Compare vectors only when they come from the same model and the same `dimensions`, because vectors from different models live in different spaces.

## Billing

NagaAI charges embeddings per input token, at the `token` row of `pricing` in `GET /v1/models`. Embeddings have no output tokens.

## Troubleshooting

### 400 `model_not_found`

The model does not exist or has no `embeddings` operation. A chat model fails here too. Check the model's `operations` in `GET /v1/models`.

### Vectors of different lengths

Two models, or one model with and without `dimensions`, produce vectors of different lengths. Embed your documents and your queries with the same model and the same `dimensions`.

### `dimensions` has no effect

The provider that served the request has no such parameter, so NagaAI dropped the field and the vector came back at full length. Pick a model that supports `dimensions`, or keep the full length.


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