# Embeddings, rerank and redaction

> Vector embeddings with POST /v1/embeddings, reranking with POST /v1/rerank and personal-data redaction with POST /v1/redact: request and answer shapes, limits, billing and errors.

Source: https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/

Besides chat completions, Serenity Edge serves three building blocks for search, retrieval-augmented generation and privacy. Each has its own route and its own model, and each is billed on input tokens only.

|  | [Embeddings](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#embeddings) | [Rerank](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#rerank) | [Redaction](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#redaction) |
| --- | --- | --- | --- |
| Endpoint | `POST /v1/embeddings` | `POST /v1/rerank` | `POST /v1/redact` |
| Format | OpenAI's embeddings API | The rerank API of Cohere and Jina | The privacy filter's own format |
| Model | `qwen3-embedding-0.6b` | `qwen3-reranker-0.6b` | `privacy-filter` (optional) |
| Result | A vector of 1024 numbers per input | A relevance score per document | The text with its personal data masked, and where each item was found |
| Billed tokens | Reported in `usage.prompt_tokens` | Estimated, returned in `usage` | Estimated |
| Per request | Up to 512 inputs and about 300,000 tokens | Up to 1000 documents, a query of up to 4096 characters and about 300,000 tokens | Up to 256 inputs and 192 KiB |
| Access | Serenity Edge API keys | Serenity Edge API keys | Serenity Edge API keys |

See [Access](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#access) for the keys, [Errors](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#errors) for what can go wrong and [Pricing and limits](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#pricing-and-limits) for billing.

## Models

Each route serves one model. Each id also has an alias with its publisher's prefix (`qwen/qwen3-embedding-0.6b`, `qwen/qwen3-reranker-0.6b`, `openai/privacy-filter`) that you can send instead; the `model` of an embeddings or rerank answer is always the id without the prefix.

The table is read from `GET /v1/models?format=openai` when this page is published and again when you open it. Prices are in US dollars per million input tokens.

| Model id | Input | Endpoint |
| --- | --- | --- |
| `privacy-filter`, also `openai/privacy-filter` | $0.009 | /v1/redact |
| `qwen3-embedding-0.6b`, also `qwen/qwen3-embedding-0.6b` | $0.008 | /v1/embeddings |
| `qwen3-reranker-0.6b`, also `qwen/qwen3-reranker-0.6b` | $0.008 | /v1/rerank |

Read from the API (`GET /v1/models?format=openai`) when this page was published, on 6 November 2026, 19:49 UTC.

These models are not chat models: the default catalogue of `GET /v1/models` does not list them. In the OpenAI-style list (`?format=openai`) each one carries an `endpoints` array with its route, a `pricing.input` per million tokens and a `pricing.output` of 0:

```json
{
  "id": "qwen3-embedding-0.6b",
  "object": "model",
  "created": 1792000000,
  "owned_by": "serenity",
  "pricing": { "input": 0.008, "output": 0 },
  "endpoints": ["/v1/embeddings"]
}
```

A model is served only on its own route. Sending `qwen3-reranker-0.6b` to `/v1/embeddings` is refused with HTTP 400, and a chat or decision model id with HTTP 404 (see [Errors](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#errors)).

## Embeddings

`POST /v1/embeddings` turns text into vectors for semantic search, clustering, deduplication and retrieval-augmented generation. It takes OpenAI's embeddings request and returns OpenAI's answer, so the official OpenAI SDKs work as they are. `qwen3-embedding-0.6b` is multilingual and returns vectors of 1024 dimensions.

### Request

| Field | Required | Description |
| --- | --- | --- |
| `model` | Yes | `qwen3-embedding-0.6b`, or its alias `qwen/qwen3-embedding-0.6b`. |
| `input` | Yes | The text to embed: a non-empty string, or an array of up to 512 non-empty strings. Token ids are refused: send the text itself (see [Errors](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#embeddings-errors)). |
| `encoding_format` | No | `float` or `base64`, as in OpenAI's API. Vectors always come back as arrays of numbers, also when `base64` is asked for; the official OpenAI SDKs accept either form. `dimensions` is not supported: every vector has 1024 numbers. |

### curl

```bash
curl https://api.serenityedge.ai/v1/embeddings \
  -H "Authorization: Bearer $SERENITY_EDGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen3-embedding-0.6b",
    "input": [
      "Refunds are issued within 5 working days.",
      "Orders ship from Valencia within 48 hours."
    ],
    "encoding_format": "float"
  }'
```

### Python

The OpenAI Python library's `client.embeddings.create` works with the Serenity Edge base URL:

```python
from openai import OpenAI

client = OpenAI(
    base_url="https://api.serenityedge.ai/v1",
    api_key="YOUR_SERENITY_EDGE_API_KEY",
)

response = client.embeddings.create(
    model="qwen3-embedding-0.6b",
    input=[
        "Refunds are issued within 5 working days.",
        "Orders ship from Valencia within 48 hours.",
    ],
    encoding_format="float",
)

vectors = [item.embedding for item in response.data]
print(len(vectors), len(vectors[0]))  # 2 1024
print(response.usage.prompt_tokens)
```

### Node.js

```javascript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.serenityedge.ai/v1",
  apiKey: process.env.SERENITY_EDGE_API_KEY,
});

const response = await client.embeddings.create({
  model: "qwen3-embedding-0.6b",
  input: ["Refunds are issued within 5 working days.", "Orders ship from Valencia within 48 hours."],
  encoding_format: "float",
});

console.log(response.data.length, response.data[0].embedding.length); // 2 1024
```

### The reply

```json
{
  "object": "list",
  "data": [
    { "object": "embedding", "index": 0, "embedding": [0.0213, -0.0472, 0.0091, 0.0368] },
    { "object": "embedding", "index": 1, "embedding": [-0.0154, 0.0327, 0.0619, -0.0083] }
  ],
  "model": "qwen3-embedding-0.6b",
  "usage": { "prompt_tokens": 19, "total_tokens": 19 }
}
```

The vectors are shortened to four numbers here; each one has 1024. The token count is illustrative.

-   `data` has one entry per input; `index` is the position of the input in `input`.
-   `model` is `qwen3-embedding-0.6b`, also when the request used the alias.
-   `usage.prompt_tokens` is the number of tokens the model read, and what you are billed for. Each input counts its own tokens, so a batch costs the same as sending its inputs one by one.

Compare vectors with cosine similarity, and embed your queries and your documents with the same model. In our measurements of 13 October 2026, embedding 25 texts took about 71 ms of server time, without the network.

### Usage and billing

You are billed on `usage.prompt_tokens` at the model's input price, shown in the [table above](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#models); output is not billed. The cost of a request is `usage.prompt_tokens` × the price per million tokens ÷ 1,000,000.

If an answer ever comes without `usage`, the request is billed on an estimate instead: a quarter of the characters of each string, rounded up. For the two inputs above, 41 and 42 characters give `ceil(41 / 4) + ceil(42 / 4) = 11 + 11 = 22` tokens.

### Limits

-   At most 512 inputs per request.
-   At most 300,000 tokens per request, estimated as a quarter of the characters of all the inputs. A larger request is refused with HTTP 400 (see [Errors](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#embeddings-errors)): split it into several requests.
-   Each input is read up to its first 8,192 tokens. The rest is ignored, without an error, and is not billed: split longer texts into chunks before embedding them.
-   At most 4 MB per request body.

### Errors

Besides the [errors shared by the three routes](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#errors), the request is refused with HTTP 400 and `code: "invalid_request_error"` when a field is wrong. `param` names the field and `message` says what to fix:

| `param` | `message` |
| --- | --- |
| `input` | `'input' is required.` |
| `input` | `'input' must be a non-empty string, or an array of non-empty strings.` |
| `input` | `'input' must be text: this model does not take token ids. Send the strings themselves (in LangChain's OpenAIEmbeddings, set check_embedding_ctx_length=False).` |
| `input` | `'input' takes at most 512 inputs per request.` |
| `input` | `'input' holds about 300001 tokens (a quarter of the characters); this API takes at most 300000 per request. Split it into several requests.` (with the request's own estimate) |
| `encoding_format` | `'encoding_format' must be float or base64.` |

## Rerank

`POST /v1/rerank` scores how relevant each document in a list is to a query, in the format of the rerank APIs of Cohere and Jina. Use it after a first, fast retrieval step (vector or keyword search) to put the best passages first before you send them to a chat model. `qwen3-reranker-0.6b` is multilingual.

### Request

| Field | Required | Description |
| --- | --- | --- |
| `model` | Yes | `qwen3-reranker-0.6b`, or its alias `qwen/qwen3-reranker-0.6b`. |
| `query` | Yes | The query, a non-empty string of at most 4096 characters. |
| `documents` | Yes | 1 to 1000 documents: strings, or objects with a string `text` (other members of such an object are accepted). |
| `top_n` | No | Return only the `top_n` most relevant documents. An integer of at least 1. |
| `return_documents` | No | Accepted for compatibility with Cohere and Jina clients, but results never repeat the documents: use `index` to find each one in your list. |

### curl

```bash
curl https://api.serenityedge.ai/v1/rerank \
  -H "Authorization: Bearer $SERENITY_EDGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen3-reranker-0.6b",
    "query": "Which city is the capital of Spain?",
    "documents": [
      "Madrid is the capital and largest city of Spain.",
      "Valencia is known for paella and the City of Arts and Sciences.",
      "Málaga lies on the Costa del Sol, in Andalucía."
    ],
    "top_n": 2
  }'
```

### Python

The OpenAI SDKs have no rerank method, so send the request as plain JSON over HTTPS. With `httpx`, which the OpenAI Python library already installs:

```python
import os

import httpx

response = httpx.post(
    "https://api.serenityedge.ai/v1/rerank",
    headers={"Authorization": f"Bearer {os.environ['SERENITY_EDGE_API_KEY']}"},
    json={
        "model": "qwen3-reranker-0.6b",
        "query": "Which city is the capital of Spain?",
        "documents": [
            "Madrid is the capital and largest city of Spain.",
            "Valencia is known for paella and the City of Arts and Sciences.",
            "Málaga lies on the Costa del Sol, in Andalucía.",
        ],
        "top_n": 2,
    },
    timeout=60,
)
response.raise_for_status()

reranked = response.json()
for result in reranked["results"]:
    print(result["index"], result["relevance_score"])
print(reranked["usage"]["prompt_tokens"])  # 67
```

### The reply

```json
{
  "results": [
    {
      "index": 0,
      "relevance_score": 0.982
    },
    {
      "index": 2,
      "relevance_score": 0.031
    }
  ],
  "model": "qwen3-reranker-0.6b",
  "usage": { "prompt_tokens": 67, "total_tokens": 67 }
}
```

The scores in this example are illustrative.

-   `results` holds the `top_n` most relevant documents, or every document when `top_n` is not set, sorted from the most to the least relevant. Each result has the `index` of its document in `documents` and its `relevance_score`, between 0 and 1: the higher, the more relevant. Use `index` to find the document in your own list.
-   `model` is `qwen3-reranker-0.6b`, also when the request used the alias.
-   `usage` is the token estimate described below, which the API writes into every answer. It is what you are billed for.
-   The answer may carry other members of Cohere's format, such as `id` or `meta`. Ignore the ones you do not use.

In our measurements of 13 October 2026, reranking 25 documents took about 28 ms of server time, without the network.

### Usage and billing

The model reads the query together with each document, so the tokens are estimated per document and added up:

```text
tokens = sum over documents of ceil((characters(query) + characters(document)) / 4)
```

Characters are Unicode code points, not bytes. For the request above, the query has 35 characters and the documents 48, 63 and 47 ("Málaga lies on the Costa del Sol, in Andalucía." is 47 characters, 49 bytes in UTF-8):

| Document | Characters with the query | Tokens |
| --- | --- | --- |
| 0 | 35 + 48 = 83 | 21 |
| 1 | 35 + 63 = 98 | 25 |
| 2 | 35 + 47 = 82 | 21 |
| **Total** |  | **67** |

The answer reports `"usage": {"prompt_tokens": 67, "total_tokens": 67}` and you are billed 67 tokens at the model's input price, shown in the [table above](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#models). Every document is scored and billed, also when `top_n` returns fewer; and the query counts once per document, so keep queries short when you rerank many documents.

### Limits

-   At most 1000 documents per request, and a query of at most 4096 characters.
-   At most 300,000 tokens per request, estimated as in [Usage and billing](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#rerank-billing): a quarter of the characters of the query and each document, the query counted once per document. A larger request is refused with HTTP 400 (see [Errors](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#rerank-errors)).
-   The model reads each document together with the query up to 8,192 tokens, its prompt template included. The rest of a longer document is ignored, without an error.
-   At most 4 MB per request body.

### Errors

Besides the [errors shared by the three routes](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#errors), the request is refused with HTTP 400 and `code: "invalid_request_error"` when a field is wrong:

| `param` | `message` |
| --- | --- |
| `query` | `'query' is required.` |
| `query` | `'query' must be a non-empty string.` |
| `query` | `'query' takes at most 4096 characters.` |
| `documents` | `'documents' is required.` |
| `documents` | `'documents' must be a non-empty array of strings or of objects with a string 'text'.` |
| `documents` | `'documents' takes at most 1000 documents per request.` |
| `documents` | `'documents' with the query hold about 300001 tokens (a quarter of the characters); this API takes at most 300000 per request. Split it into several requests.` (with the request's own estimate) |
| `top_n` | `'top_n' must be an integer of at least 1.` |
| `return_documents` | `'return_documents' must be a boolean.` |

## Redaction

`POST /v1/redact` finds personal data in text (names, postal addresses, email addresses, phone numbers, dates, account numbers, URLs and secrets such as passwords) and masks it, so that you can remove it before the text reaches a model, a log or a third party. It serves `privacy-filter`, the open privacy filter model published by OpenAI, in the filter's own request and answer format.

> **Your text is processed in memory only**
> 
> The text you send to `/v1/redact`, and the masked text and spans the API returns, are processed in memory only. They are never written to disk or logs, are discarded once the answer is sent, and are never used for training. See [Data residency](https://docs.serenityedge.ai/docs/data-residency/).

### Request

| Field | Required | Description |
| --- | --- | --- |
| `inputs` | Yes | The text to redact: a string, or an array of 1 to 256 strings. |
| `placeholders` | No | `true` (the default) to replace each item found with a placeholder that names its category, such as `[PERSON]`, `[EMAIL]` or `[PHONE]`; `false` to remove the item from the text. |
| `categories` | No | Look only for these [categories](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#redaction-categories), as an array of strings such as `["private_email", "private_phone"]`. Omit it, or send an empty array, to look for every category. |
| `model` | No | `privacy-filter`, or its alias `openai/privacy-filter`. When it is left out, `privacy-filter` answers. |

### Categories

The filter knows eight categories. Their names are the values of `categories` in the request and of `entity_group` in the answer.

| Category | Finds | In `text` | `placeholder` |
| --- | --- | --- | --- |
| `private_person` | Names of people | `[PERSON]` | `<PRIVATE_PERSON>` |
| `private_address` | Postal addresses | `[ADDRESS]` | `<PRIVATE_ADDRESS>` |
| `private_email` | Email addresses | `[EMAIL]` | `<PRIVATE_EMAIL>` |
| `private_phone` | Phone numbers | `[PHONE]` | `<PRIVATE_PHONE>` |
| `private_date` | Dates, such as a date of birth | `[DATE]` | `<PRIVATE_DATE>` |
| `account_number` | Account numbers, such as an IBAN | `[ACCOUNT_NUMBER]` | `<ACCOUNT_NUMBER>` |
| `private_url` | URLs, such as a personal web page | `[URL]` | `<PRIVATE_URL>` |
| `secret` | Secrets, such as a password or an API key | `[SECRET]` | `<SECRET>` |

> **Unknown names are ignored**
> 
> Write the names exactly as above, in lower case. A name the filter does not know, `PRIVATE_PERSON` in capitals included, is ignored without an error: a request whose `categories` holds only unknown names answers HTTP 200 with the text unchanged and no spans.

### curl

```bash
curl https://api.serenityedge.ai/v1/redact \
  -H "Authorization: Bearer $SERENITY_EDGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": "Hi, I am Ana García. Write to ana.garcia@example.com or call +34 600 000 000.",
    "placeholders": true
  }'
```

### Python

As for rerank, send plain JSON over HTTPS:

```python
import os

import httpx

response = httpx.post(
    "https://api.serenityedge.ai/v1/redact",
    headers={"Authorization": f"Bearer {os.environ['SERENITY_EDGE_API_KEY']}"},
    json={
        "inputs": "Hi, I am Ana García. Write to ana.garcia@example.com or call +34 600 000 000.",
        "placeholders": True,
    },
    timeout=60,
)
response.raise_for_status()

redacted = response.json()
print(redacted["text"])  # Hi, I am [PERSON]. Write to [EMAIL] or call [PHONE].
for span in redacted["spans"]:
    print(span["entity_group"], span["start"], span["end"])
```

### The reply

For a string in `inputs`, the answer is one object:

```json
{
  "text": "Hi, I am [PERSON]. Write to [EMAIL] or call [PHONE].",
  "spans": [
    { "entity_group": "private_person", "score": 1.0, "word": "Ana García", "start": 9, "end": 19, "placeholder": "<PRIVATE_PERSON>" },
    { "entity_group": "private_email", "score": 1.0, "word": "ana.garcia@example.com", "start": 30, "end": 52, "placeholder": "<PRIVATE_EMAIL>" },
    { "entity_group": "private_phone", "score": 1.0, "word": "+34 600 000 000", "start": 61, "end": 76, "placeholder": "<PRIVATE_PHONE>" }
  ]
}
```

-   `text` is the input with each item found masked: replaced with its placeholder, or removed when `placeholders` is `false`.
    
-   `spans` has one entry per item found:
    
    -   its [category](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#redaction-categories) (`entity_group`, such as `private_person`, `private_email` or `private_phone`);
    -   the model's confidence (`score`, from 0 to 1);
    -   the text found (`word`);
    -   its position in the input you sent (`start` and `end`, in characters, `end` excluded);
    -   the category's tag (`placeholder`, such as `<PRIVATE_PERSON>`).
    
    Ignore the fields you do not use.
    

For an array in `inputs`, the answer is an array with one such object per input, in the same order:

```json
[
  {
    "text": "My name is [PERSON].",
    "spans": [{ "entity_group": "private_person", "score": 1.0, "word": "Ana García", "start": 11, "end": 21, "placeholder": "<PRIVATE_PERSON>" }]
  },
  {
    "text": "Call [PHONE]",
    "spans": [{ "entity_group": "private_phone", "score": 1.0, "word": "+34 600 000 000", "start": 5, "end": 20, "placeholder": "<PRIVATE_PHONE>" }]
  }
]
```

The answer has no `model` and no `usage`: it is returned exactly as the filter writes it.

> **warning**
> 
> A model finds the personal data, so it can miss an item or mask text that is not personal. Check the results on a sample of your own texts, and do not make redaction the only safeguard for data you are not allowed to disclose.

### Usage and billing

The tokens are estimated over the whole request: a quarter of the characters of all the inputs together, rounded up once.

```text
tokens = ceil(characters of all inputs / 4)
```

Characters are Unicode code points, not bytes. The string of the curl example has 77 characters (78 bytes in UTF-8), so it is billed `ceil(77 / 4) = 20` tokens. The two inputs of the array example, `"My name is Ana García."` and `"Call +34 600 000 000"`, have 22 and 20 characters: `ceil(42 / 4) = 11` tokens. You are billed these tokens at the model's input price, shown in the [table above](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#models). Since the answer carries no `usage`, compute the estimate on your side if you need it per request.

### Limits

-   At most 256 inputs per request.
-   At most 192 KiB (196,608 bytes) per request body, field names and JSON syntax included. A larger body is refused with HTTP 413; split long documents into several requests.

### Errors

Besides the [errors shared by the three routes](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#errors), the request is refused with HTTP 400 and `code: "invalid_request_error"` when a field is wrong:

| `param` | `message` |
| --- | --- |
| `inputs` | `'inputs' is required.` |
| `inputs` | `'inputs' must be a string or a non-empty array of strings.` |
| `inputs` | `'inputs' takes at most 256 inputs per request.` |
| `placeholders` | `'placeholders' must be a boolean.` |
| `categories` | `'categories' must be an array of strings.` |

## Access

The three routes can be used only with **Serenity Edge API keys**, the keys Substrate AI issues directly to customers and partner platforms. Today they are issued on request: see [Enterprise](https://docs.serenityedge.ai/docs/access/enterprise/) or write to [contact@substrate.ai](mailto:contact@substrate.ai).

-   Any other key, such as a key from a [Serenity Star account](https://docs.serenityedge.ai/docs/access/serenity-star-account/), gets HTTP 403 with `code: "permission_denied"` and a message that names the route's API: `This API key cannot use the embeddings API. Use a Serenity Edge API key.` (`rerank` or `redact` on the other two routes).
-   The three models are not part of the [OpenRouter onboarding](https://docs.serenityedge.ai/docs/access/openrouter/), which covers the chat models only.

## Errors

Errors use the same [error object](https://docs.serenityedge.ai/docs/errors-and-rate-limits/#error-object) as chat completions. These can happen on all three routes; the field errors of each route are listed in its own section ([embeddings](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#embeddings-errors), [rerank](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#rerank-errors), [redaction](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#redaction-errors)).

| Status | `code` | `message` and when | What to do |
| --- | --- | --- | --- |
| 400 | `invalid_request_error` | `The request body is not a JSON object.`; `'model' is required.` (not on `/v1/redact`); `'model' must be a string.` | Fix the request. Do not retry as is. |
| 400 | `invalid_request_error` | `The model 'qwen3-reranker-0.6b' is not served on /v1/embeddings.`: the model exists but is served on another of these routes (`param: "model"`). | Use the model of the route (see [Models](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#models)). |
| 400 | `invalid_request_error` | `The model server rejected the request. Check the input sizes and parameters.`: the model could not process the request as sent, for example an input too long for it or a parameter it does not accept (`param` is `null`). | Shorten the inputs or check the parameters. Do not retry as is. |
| 401 | `invalid_api_key` | `Missing or invalid API key. Send it as 'Authorization: Bearer <key>'.` | Check the `Authorization` header and the key. |
| 403 | `permission_denied` | `This API key cannot use the embeddings API. Use a Serenity Edge API key.` (`rerank`, `redact`): the key is not a Serenity Edge API key (see [Access](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#access)). | Use a Serenity Edge API key. |
| 404 | `model_not_found` | `The model 'orion-pro' does not exist or you do not have access to it.`: an unknown id, or a chat or decision model (`param: "model"`). | Check `GET /v1/models?format=openai`. |
| 413 | `request_too_large` | `The request body exceeds the maximum size of 4194304 bytes.` (`196608 bytes` on `/v1/redact`). | Send fewer or shorter inputs per request. |
| 429 | `rate_limit_exceeded` | `The service is at capacity for this model. Retry after the number of seconds in the Retry-After header.`: your rate limit is exceeded, or the model is temporarily at capacity. | Wait for `Retry-After`, then retry with backoff. |
| 500 | `upstream_error` | `The model service returned an unexpected response. Please retry.` | Retry with backoff. |
| 500 | `timeout` | `The request timed out before the model produced a response.` (after 600 seconds) | Retry with fewer inputs per request. |
| 503 | `upstream_unavailable` | `The model service is temporarily unavailable. Please retry later.` | Retry later; check the [status page](https://status.serenityedge.ai/) if it persists. |

The messages are written by Serenity Edge: the model's own error text never reaches you. The three routes are idempotent, so retrying a request that failed with 429 or a 5xx is safe.

## Pricing and limits

-   **Input tokens only.** Output is not billed and there is no cached input price. Embeddings are billed on the `usage.prompt_tokens` the model reports; rerank and redaction on the estimates described in their sections, with characters counted as Unicode code points. Prices are in US dollars per million tokens, shown live in the [table above](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#models) and on [Pricing and limits](https://docs.serenityedge.ai/docs/pricing-and-limits/#embeddings-rerank-and-redaction).
-   **Per request.** Embeddings: 512 inputs and about 300,000 tokens, each input read up to its first 8,192 tokens. Rerank: 1000 documents, a query of 4096 characters and about 300,000 tokens, each document read with the query up to 8,192 tokens. Redaction: 256 inputs and a body of 192 KiB. The other two routes take a body of up to 4 MB, as chat completions.
-   **Rate limits and capacity.** Requests are admitted per model as on chat completions. Over your limit or when the model is at capacity, the API answers HTTP 429 with `Retry-After`; handle it as described in [Errors and rate limits](https://docs.serenityedge.ai/docs/errors-and-rate-limits/#handling-429-correctly).
-   **Data.** The same data policy as chat completions applies (see [Data residency](https://docs.serenityedge.ai/docs/data-residency/)): request and response bodies are processed in memory, never written to disk or logs and never used for training.

## Next steps

-   [Create embeddings](https://docs.serenityedge.ai/docs/api/edge/create-embedding/), [Rerank documents](https://docs.serenityedge.ai/docs/api/edge/rerank-documents/) and [Redact personal data](https://docs.serenityedge.ai/docs/api/edge/redact-text/) in the API reference.
-   [Models](https://docs.serenityedge.ai/docs/models/) for the chat models, including `paddleocr-vl-1.6` for reading document pages.
-   [Errors and rate limits](https://docs.serenityedge.ai/docs/errors-and-rate-limits/) for retries and backoff.
