Embeddings
Buy vectors for text. The endpoint has the shape of the OpenAI embeddings API, so an unchanged OpenAI client works. You pay for the input tokens only, because an embedding job has no output price.
curl https://router.inference.ai.exchange/v1/embeddings \
-H "Authorization: Bearer sk-liquid-..." \
-H "Content-Type: application/json" \
-d '{
"model": "text-embedding-3-small",
"input": ["a cat on a bicycle", "a dog in a car"]
}'
The response has the OpenAI shape. The vectors come back in the order of the texts, and each vector has an index. The exchange adds an exchange block that shows what the job bought and what it cost.
{
"object": "list",
"model": "text-embedding-3-small",
"data": [
{ "object": "embedding", "index": 0, "embedding": [0.0123, -0.0456] },
{ "object": "embedding", "index": 1, "embedding": [0.0789, 0.0012] }
],
"usage": { "prompt_tokens": 9, "total_tokens": 9 },
"exchange": { "...": "..." }
}
Use a key with the spend scope. See API keys.
Examples
Search your documents. Embed each document once and store the vectors. To search, embed the query with the same model, then return the documents whose vectors are nearest.
docs = ["Reset your password in Settings.", "Invoices are sent on the 1st."]
vectors = client.embeddings.create(model="text-embedding-3-small", input=docs)
query = client.embeddings.create(model="text-embedding-3-small", input=["how do I change my password"])
Find duplicates. Embed a batch of texts in one request. Two texts whose vectors are very close say nearly the same thing.
batch = client.embeddings.create(model="text-embedding-3-small", input=tickets)
vectors = [item.embedding for item in batch.data] # in the order of `tickets`
Use the OpenAI SDK
from openai import OpenAI
client = OpenAI(
base_url="https://router.inference.ai.exchange/v1",
api_key="sk-liquid-...",
)
result = client.embeddings.create(
model="text-embedding-3-small",
input=["a cat on a bicycle", "a dog in a car"],
)
print(len(result.data), len(result.data[0].embedding))
import OpenAI from 'openai'
const client = new OpenAI({
baseURL: 'https://router.inference.ai.exchange/v1',
apiKey: process.env.LIQUID_API_KEY,
})
const result = await client.embeddings.create({
model: 'text-embedding-3-small',
input: ['a cat on a bicycle', 'a dog in a car'],
})
console.log(result.data.length, result.data[0]?.embedding.length)
Find a model and its price
The model list and the prices are public. You need no key to read them.
curl 'https://router.inference.ai.exchange/v1/models?kind=embedding'
Each entry has the model id and min_input_usd_per_mtok, the lowest input price among the offers now on the book, in USD for each million tokens. To keep the models whose cheapest offer is at most USD 0.05, add max_input_price: ?kind=embedding&max_input_price=0.05. To see how many independent providers serve a model, read providers in GET /v1/exchange/models. See Market data.
You name the model to buy. The exchange does not choose an embedding model for you. The router models and Auto Router cannot choose one, because they rank by scores that were measured on text. A preset of the same kind also works.
What a job costs
The exchange auctions each request among the providers that serve the model that you named. The charge is the input rate of the offer that won, not min_input_usd_per_mtok. The receipt records the rate. A job runs in three steps:
- The exchange estimates the tokens of your texts and fixes that figure at the input rate of the offer as the price cap, the highest charge for the job.
- The exchange returns the cap in
x-liquid-cap-usdand the job id inx-liquid-job-id. - The charge is the tokens that the provider reports. It never goes above the cap.
The exchange fails the job, and you pay nothing, if the provider sends one of these:
- a different number of vectors than texts,
- vectors of different lengths, or
- output tokens.
exchange.limits.price.input_usd_per_mtok limits the input price. A rule that limits output price or speed in tokens returns a 400 error. See Purchasing rules.
Keep one model for one index
Vectors from different models cannot be compared. A search index must hold vectors from one model, at one vector length. If you change the model, embed the whole corpus again.
The provider can change from one request to the next. Offers of one model can differ in precision, so their vectors can differ by a small amount. To keep an index stable:
- Use
dimensionsonly for a model that supports a shorter vector. - Use the same
dimensionsvalue for every request that feeds the index.
Use a framework or a vector database
These guides show the exchange as the embedding source:
- LangChain:
OpenAIEmbeddings. Setcheck_embedding_ctx_length=False. - LlamaIndex:
OpenAILikeEmbedding. - pgvector: store and search vectors in Postgres.
- Qdrant: store and search vectors in a Qdrant collection.
- Pinecone: store and search vectors in a Pinecone index.
Limits
- There is no streaming form. The exchange sends the batch complete.
- Embeddings do not use response caching or prompt caching.
Reference
Request fields
| Field | Description |
|---|---|
model |
The deployment to buy from. Required. The exchange does not choose an embedding model for you. |
input |
One string, or an array of up to 2,048 strings. Required. |
dimensions |
The length of each vector, from 1 to 4,096. |
encoding_format |
float (the default) or base64. |
input_type |
What the vectors are for: search_query, search_document, classification or clustering. |
exchange |
Your purchasing rules. See Purchasing rules. |
Each string can have up to 32,768 characters. All strings together can have up to 1,200,000 characters. The exchange does not support token arrays or multimodal objects.
With base64, each vector is a string of little-endian 32-bit floats, as the OpenAI SDKs expect.
Input type
A model that embeds a query and a document in different ways reads input_type. Only an offer that declared embedding.input_type can win a request that has it. Without the field, any offer can win. The model card lists input_type in parameters when an offer declares it.
{ "model": "<model>", "input": "how do I cancel a job?", "input_type": "search_query" }
A value outside the four gets 400 invalid_request. If no offer declared embedding.input_type, the answer is 400 unsupported_parameter and param is embedding.input_type.
Multimodal input items are not supported yet.
Vector length
A deployment that shortens its vectors serves a request up to its own length. Any other deployment serves only its own length. A request for a length that the deployment cannot serve goes to a different offer. It does not fail after the choice.
Job kinds
A chat deployment never wins an embedding job. An embedding deployment never wins a chat job. The exclusion reason is job_kind_not_served.
Next steps
- Modalities: images, speech and video.
- Embeddings API reference: the full request and response schema.