Skip to main content

Embeddings

Generate numeric vector representations of text using the server's configured embedding model.

Overview​

One call accepts one or more strings and returns floating-point vectors for similarity scoring, clustering, classification, or a custom search index.

The model is configured server-side (EMBEDDING_PROVIDER, EMBEDDING_MODEL; backends ollama, openai, bedrock). Callers do not choose it, so all vectors in a deployment share one space.

See the Permissions Reference for the IAM action strings for this module.

Configuration​

Environment VariableRequiredDescription
EMBEDDING_PROVIDERYesEmbedding backend: ollama, openai, or bedrock.
EMBEDDING_MODELYesModel identifier for the backend (e.g. qwen3-embedding:0.6b, text-embedding-3-small, amazon.titan-embed-text-v2:0).
EMBEDDING_DIMENSIONSYesVector dimensionality. Must match the model output, e.g. 1024 — the server fails at startup when unset, or above 2000, the widest vector pgvector can index.
OLLAMA_BASE_URLNoOllama server URL (ollama only). Defaults to http://localhost:11434.
EMBEDDING_API_KEYNoAPI key for the backend: the OpenAI key (openai), or a Bedrock ABSK… bearer token (bedrock). For openai, falls back to OPENAI_API_KEY.
EMBEDDING_BASE_URLNoOverride the base URL for any OpenAI-compatible endpoint (openai only).
EMBEDDING_REGIONNoAWS region for Bedrock (bedrock only). Falls back to AWS_REGION, then us-east-1.
EMBEDDING_INPUT_1M_TOKEN_PRICE_USDNoUSD per million input tokens, the unit vendors publish ($0.02 / 1M → 0.02). Unset meters embeddings unpriced (0 on ollama). See Pricing embeddings.

Provider selection​

  • ollama — local models via the Ollama client at OLLAMA_BASE_URL. No credentials required.
  • openai — the OpenAI embeddings API (or any OpenAI-compatible endpoint via EMBEDDING_BASE_URL), authenticated with EMBEDDING_API_KEY.
  • bedrock — Amazon Bedrock embedding models. Authenticate with an ABSK… bearer token in EMBEDDING_API_KEY, or leave it unset to use the standard AWS credential chain (AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY).

Batch size​

inputs accepts at most 256 values per request (each a model call); larger batches are refused with VALIDATION_FAILED (400).

Data Model​

Stateless; nothing is stored. The response shape depends on the inputs:

FieldTypeDescription
embeddingnumber[]Returned when input (single string) is provided.
embeddingsnumber[][]Returned when inputs (array of strings) is provided.

Both fields can be present if the request includes both input and inputs.

project_id names the project billed, required unless the credential is bound to a project; see Metering.

Key Concepts​

Single vs batch​

input (string) for one vector, inputs (array) for many; both may be combined.

Shared vector space​

All embeddings from one deployment share a vector space, so cosine similarity between them is meaningful; vectors from other deployments or models are not comparable.

Metering​

Every embedding call is metered as an llm_tokens usage event with source embedding, whatever the origin: this endpoint, ingestion, a memory write, an embedding_similarity scorer, or a knowledge-search query. Spend appears in GET /api/v1/usage/events and counts towards cost_usd and tokens quotas.

A batch the provider serves in several calls meters every call that completed, even when a later one fails the batch.

A usage event belongs to a project, so an embedding call needs one:

CallBilled to
POST /api/v1/embeddings with project_idthat project — the caller must be able to write to it
The same call from a project-scoped credentialthe credential's project
The same call with neitherrefused, 400 VALIDATION_FAILED
Ingestion, memory, evaluationthe document's, memory's or run's project
A knowledge search with a querythe first project in scope, by public id, the caller holds embeddings:CreateEmbeddings on (a project-scoped credential's own project); 403 FORBIDDEN when there is none; an admin's search naming no project is not metered. A search with no query embeds nothing

The query an agent's knowledge_config embeds before its turn is also attributed to that generation: the event carries the generation's generation_id, agent_id, trace_id, session_id and actor_id, so the spend reads per generation and counts towards agent and actor quotas. If the generation's record is never written, the event is billed to the project only.

An embedding of stored text also names what it embedded: document_id on a document chunk (ingestion, a re-chunk, a conversation's turns), memory_store_id on written memory content. The store, not the memory: a store keeps one copy of each text, shared by every memory that states it. A memory an agent or a rule writes during a turn carries that generation's attribution too. A query embedding names neither.

Pricing embeddings​

The rate is deployment configuration, not a price book row. EMBEDDING_INPUT_1M_TOKEN_PRICE_USD prices every embedding; cost_usd = tokens × rate / 1,000,000 at write time. input_tokens is the only dimension; there is no output rate.

Per million tokens, as vendors publish, so the figure is copied as written:

EMBEDDING_PROVIDER=openai
EMBEDDING_MODEL=text-embedding-3-small
EMBEDDING_INPUT_1M_TOKEN_PRICE_USD=0.02

Unset means unpriced. With no rate, every embedding is metered with its tokens and cost_usd null, as an unpriced model is; the server warns at startup naming the variable. On ollama, a local model billed nothing per token, unset means 0. Cost is frozen per event; a rate set later prices the next embedding.

The price book does not reach embeddings. A row naming the embedding model is ignored:

TierScopeApplies to embeddings?
Provider instanceOne ai_provider_idNo — an embedding event carries no provider record
Project + slugOne project's rate for a provider slugNo
Global defaultEvery projectNo

The embedding stack is per deployment, not an AI provider record, so there is no instance or per-project rate to price; the operator who picks EMBEDDING_MODEL sets its price beside it.

An embedding is never named in a QUOTA_UNENFORCEABLE refusal's unpriced_rows and never counts towards one.

503 when unconfigured​

Without EMBEDDING_PROVIDER or EMBEDDING_MODEL, the server returns 503 EMBEDDING_NOT_CONFIGURED (a configuration error).

Examples​

Single text​

soat create-embeddings --project-id "proj_V1StGXR8Z5jdHi6B" \
--input "The quick brown fox jumps over the lazy dog."

Batch of texts​

soat create-embeddings --project-id "proj_V1StGXR8Z5jdHi6B" \
--inputs '["First sentence.", "Second sentence.", "Third sentence."]'