Embeddings
Vector embeddings via the OpenAI wire format. Float or base64. Variable dimensions.
POST /v1/embeddings turns text into a vector. The wire format is the
OpenAI embeddings spec, so any storage layer that already speaks
OpenAI (pgvector, Pinecone, Qdrant, LangChain stores) works
unchanged.
At a glance
| Endpoint | POST /v1/embeddings |
| Default model | cohere/embed-v4 |
| Encoding | float (default) or base64 |
| Dimensions | Fixed per model, or truncatable on cohere/embed-v4 |
Single string
import { createCleverRouter } from '@cleverrouter/sdk';
const cr = createCleverRouter({ apiKey: process.env.CLEVERROUTER_API_KEY! });
const res = await cr.embed('Hello from the EU.', {
model: 'cohere/embed-v4',
});
const vector = res.data[0]!.embedding as number[];
console.log(vector.length); // → e.g. 1024Batch
const res = await cr.embed(
['First document.', 'Second document.', 'Third document.'],
{ model: 'cohere/embed-v4' },
);
for (const entry of res.data) {
console.log(entry.index, (entry.embedding as number[]).length);
}Batching matters: one batched call is dramatically cheaper than N serial calls — both in HTTP overhead and in cents.
float vs base64
The default encoding_format: 'float' returns number[]. Switch to
'base64' for a compact string, handy for JSON-heavy pipes or direct
bytea storage:
const res = await cr.embed('Hello', { encoding_format: 'base64' });
const compact = res.data[0]!.embedding as string;Variable dimensions
Models with Matryoshka-style truncation (e.g. cohere/embed-v4)
accept dimensions to shrink the output:
await cr.embed('Hello', {
model: 'cohere/embed-v4',
dimensions: 512, // instead of the model default of 1024
});Smaller dimensions trade a little recall for cheaper storage and faster vector search.
Options
interface EmbeddingOpts {
model?: string; // default: 'cohere/embed-v4'
encoding_format?: 'float' | 'base64'; // default: 'float'
dimensions?: number;
user?: string; // forwarded for upstream audit logs
provider?: 'scaleway' | 'tensorix' | 'bedrock';
}Response shape
interface EmbeddingResponse {
object: 'list';
data: Array<{
index: number;
object: 'embedding';
embedding: number[] | string; // string when encoding_format='base64'
}>;
model: string;
usage?: { prompt_tokens?: number; total_tokens?: number };
}Provider options
| Model | Provider | Region |
|---|---|---|
cohere/embed-v4 | Bedrock | eu-central-1 |
baai/bge-multilingual-gemma2 | Scaleway | Paris |
qwen/qwen3-embedding | Scaleway, Tensorix | Paris, Frankfurt |
Pin a provider when you need it:
await cr.embed(['hello'], { provider: 'scaleway' });Multilingual?
cohere/embed-v4 and baai/bge-multilingual-gemma2 handle ~100
languages each with consistent vector geometry. For English-only
workloads, smaller English-only models are cheaper.
cURL
curl https://api.cleverouter.eu/v1/embeddings \
-H "Authorization: Bearer $CLEVERROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "cohere/embed-v4",
"input": ["Hello from the EU.", "Bonjour from Paris."]
}'Common gotchas
- Don't mix models. Vectors from
cohere/embed-v4andqwen/qwen3-embeddinglive in different spaces. Pick one per index and stick with it. - Re-embed on dimension change. Truncating from 1024 → 512 is a
property of the call, not the store. Re-embed everything if you
change
dimensions. - Batch size limits. ~96 strings per call is a safe upper bound. The gateway accepts more but upstream providers cap at provider- specific numbers; chunk yourself for portability.
Related
- Rerank — re-rank embeddings results.
- Cookbook: RAG pipeline — embed + rerank + chat.
- Catalog — embedding models.