Embeddings
Turn text into vectors with POST /embeddings, the same request and answer as OpenRouter's.
POST /embeddings turns text into vectors: lists of numbers that sit close together when the texts mean similar things.
Use them for search, retrieval for a chat model, clustering and finding duplicates.
The request and the answer have the same shape as OpenRouter's and OpenAI's, so an OpenAI SDK works with only the base URL and the key changed.
You pay for input tokens only. The answer carries the charge in usage.cost.
Request
POST https://api.plaingateway.com/api/v1/embeddings Authorization: Bearer $PLAINGATEWAY_API_KEY Content-Type: application/json
| Field | Type | Required | What it does |
|---|---|---|---|
model |
string | yes | An embedding model id, such as openai/text-embedding-3-small. See Models. |
input |
string or array | yes | The text or tokens to embed, in one of the five forms below. |
dimensions |
integer | no | How many numbers each vector has. Only text-embedding-3-small and text-embedding-3-large take it. |
encoding_format |
string | no | float (the default) returns arrays of numbers. base64 returns each vector as a base64 string of 32-bit floats, which is smaller. |
input_type |
string | no | What the input is, such as search_query or search_document. Accepted. The OpenAI and Azure hosts do not take it, so it is not sent to them. |
user |
string | no | Your end user's id. It is stored as external_user on the generation. |
provider |
object | no | Which hosts may serve the request and in what order. See Provider routing. |
session_id |
string | no | Accepted and not stored. It never reaches the host. |
trace |
object | no | Accepted and not stored. It never reaches the host. |
stream is ignored: the answer is always one JSON body. models is ignored too, so model is required.
The five forms of input
| Form | Example |
|---|---|
| A string | "input": "The cat sat on the mat" |
| An array of strings | "input": ["The cat sat", "A dog ran"] |
| One array of token ids | "input": [791, 8415, 7731] |
| An array of token id arrays | "input": [[791, 8415], [32, 5679]] |
| An array of content objects | "input": [{"content": [{"type": "text", "text": "The cat sat"}]}] |
A content object may hold only text parts; its text is sent to the host as a string. An image or any other part returns 400, because no listed model takes images.
Strings must not be empty, and token arrays must hold at least one token.
Response
One data item for each input, in the order you sent them. index is the input's position.
model is the host's own model name, without the openai/ prefix, as on OpenRouter.
id has the form gen-emb-<unix seconds>-<20 letters and digits>; pass it to GET /generation to read the request back.
usage.cost is the charge in USD. prompt_tokens and total_tokens are the same number, since there are no output tokens.
The X-PG-Host and X-PG-Cost-USD headers carry the host and the charge, as on every non-streamed answer. See Headers.
HTTP/1.1 200
{
"object": "list",
"data": [
{
"object": "embedding",
"embedding": [0.016754150390625, -0.055755615234375, 0.005634307861328125],
"index": 0
}
],
"model": "text-embedding-3-small",
"usage": {
"prompt_tokens": 1,
"total_tokens": 1,
"cost": 2e-08,
"is_byok": false,
"cost_details": {
"upstream_inference_cost": 2e-08,
"upstream_inference_prompt_cost": 2e-08,
"upstream_inference_completions_cost": 0
}
},
"provider": "OpenAI",
"id": "gen-emb-1794367041-trccdd4mAq6ZFMp5X8f0"
}This is a recorded answer with the vector shortened; a real one has 1,536 numbers for text-embedding-3-small. Your usage.cost is the charge at our price.
Errors
Errors have the shape described in Errors. These are the ones particular to embeddings:
| Status | When | Message |
|---|---|---|
| 400 | The model is unknown, or is not an embedding model | Model <id> does not exist |
| 400 | input is an empty array |
Input is empty |
| 400 | input is missing, an empty string, or holds an empty string, an empty token array or a non-text part |
A message naming the problem |
| 400 | More than 2,048 inputs, refused before any host | HTTP 400: ... holding array length must be 2048 or less |
| 400 | One input is over 8,192 tokens | HTTP 400: ... holding the host's message, maximum context length is 8192 tokens |
| 401 | No key, or a key that is unknown, disabled, expired or revoked | |
| 402 | The balance cannot cover the request, or the key reached its spend limit | |
| 404 | The provider filters leave no host |
|
| 429 | The host rate limited the request | |
| 502 | The host failed, or its answer was over the request's size cap | |
| 503 | Another request with a large answer is being read; see Limits | the gateway is busy with other large answers; try again shortly |
A chat model sent here gets the first 400. An embedding model sent to /chat/completions or another chat endpoint gets that endpoint's unknown model 400.
An error before the host answered is not charged. Once the host has answered, the request is charged even if the answer cannot be delivered, because the host was paid; see Errors.
Limits
- At most 2,048 inputs in one request.
- At most 8,192 tokens in each input.
- No streaming.
- A large answer is served one at a time. The gateway sizes each answer from the number of inputs and the numbers in each vector:
dimensions, or 3,072 when you name none. When that size could pass 32 MB and another such answer is being read, the request gets 503 at once, before any charge. With float vectors that is more than 310 inputs, or more than 619 withdimensionsat 1,536; withbase64(the OpenAI SDK's default) it is more than 1,953 inputs. Split a larger batch, or retry the 503 after a short wait. - The same input gives the same vector each time, so store vectors instead of asking again.
Examples
curl
curl https://api.plaingateway.com/api/v1/embeddings \
-H "Authorization: Bearer $PLAINGATEWAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "openai/text-embedding-3-small", "input": ["The cat sat on the mat", "A dog ran in the park"]}'Python (OpenAI SDK)
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.plaingateway.com/api/v1",
api_key=os.environ["PLAINGATEWAY_API_KEY"],
)
r = client.embeddings.create(
model="openai/text-embedding-3-small",
input=["The cat sat on the mat", "A dog ran in the park"],
)
print(len(r.data), len(r.data[0].embedding)) # 2 1536TypeScript (OpenAI SDK)
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.plaingateway.com/api/v1",
apiKey: process.env.PLAINGATEWAY_API_KEY,
});
const r = await client.embeddings.create({
model: "openai/text-embedding-3-small",
input: ["The cat sat on the mat", "A dog ran in the park"],
});
console.log(r.data.length, r.data[0].embedding.length); // 2 1536The OpenAI SDK asks for base64 by default and decodes it, so you get arrays of numbers either way.
Models
| Model id | Numbers in a vector |
|---|---|
openai/text-embedding-3-small |
1,536, or fewer with dimensions |
openai/text-embedding-3-large |
3,072, or fewer with dimensions |
openai/text-embedding-ada-002 |
1,536 |
Their prices are on Models, per 1M input tokens.
To list them from code, call GET /embeddings/models or GET /models?output_modalities=embeddings. The plain GET /models leaves them out, as OpenRouter's does.
Differences from OpenRouter
- Only the OpenAI embedding models in the table above are served.
- No batch API and no
:batchmodels. - No image or other multimodal input.
- A request whose answer could pass 32 MB is served one at a time; another such request gets 503 until the first ends. OpenRouter states no such limit.
- In
provider,require_parameters,preferred_max_latency,preferred_min_throughput,enforce_distillable_textandoptionsare accepted and have no effect. session_idandtraceare accepted and not stored. GET /generation returnssession_idas null.