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:

  1. 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.
  2. The exchange returns the cap in x-liquid-cap-usd and the job id in x-liquid-job-id.
  3. 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:

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 a framework or a vector database

These guides show the exchange as the embedding source:

Limits

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