Skip to Content
Embeddings

Embeddings

Generate Embeddings

POST /v1/embeddings

Generate vector embeddings for text inputs. Compatible with OpenAI’s Embeddings API.

Request

{ "model": "openai/text-embedding-3-large", "input": ["hello world", "hola mundo"] }

Response

Usage is tracked internally and not returned:

{ "object": "list", "model": "openai/text-embedding-3-large", "data": [ { "object": "embedding", "index": 0, "embedding": [0.1, 0.2, ...] } ] }

Supported Models

  • openai/text-embedding-3-large - 3072 dimensions
  • openai/text-embedding-3-small - 1536 dimensions
  • openai/text-embedding-ada-002 - 1536 dimensions (legacy)

Shared Embedding Resources

Manage vector embedding resources for semantic search. Resources store items with their embeddings and support similarity queries.

Access Modes

Each resource has an access mode set at creation time (immutable):

  • application_shared (default) — any user in the application can read and query the resource. Only the creator can mutate (upsert/delete items).
  • user_private — only the creator can read, query, or mutate the resource. Other users receive a 404 (existence is not leaked). Use this for privacy-sensitive use cases like encrypted personal search.

Direct Vector Support

By default, the server generates embeddings from text using the resource’s model. You can also provide pre-computed vectors directly, bypassing server-side embedding generation. This enables client-side embedding workflows (e.g., local models, encrypted/rotated vectors for privacy-preserving search).

  • Upsert: each item can have text (server embeds), vector (pre-computed), or both (vector takes precedence).
  • Query: provide query (text, server embeds) or query_vector (pre-computed). At least one is required.

Create Resource

POST /v1/embeddings/resources

With a model (server-side embeddings):

{ "key": "my-faq", "name": "FAQ Knowledge Base", "model": "openai/text-embedding-3-small", "description": "Company FAQ entries", "access_mode": "application_shared", "items": [ { "item_id": "faq-1", "text": "How do I reset my password?", "metadata": { "category": "account" } } ] }

Without a model (direct vectors only):

{ "key": "encrypted-search", "name": "Encrypted Chat Search", "dimensions": 384, "access_mode": "user_private" }

When model is omitted, dimensions is required. The resource only accepts pre-computed vectors — text items and text queries will be rejected with a 400 error. This is designed for client-side embedding workflows (e.g., local models, encrypted search with e5-small).

FieldTypeRequiredDescription
keystringyesUnique resource identifier
namestringyesDisplay name
modelstringnoProvider-prefixed embedding model. Required for server-side embedding; omit for direct-vector-only resources.
dimensionsnumberconditionalVector dimensions. Required when model is omitted. When model is provided, dimensions are auto-resolved (or can be overridden).
descriptionstringnoOptional description
access_modestringnoapplication_shared (default) or user_private
itemsarraynoInitial items to populate the resource. When model is omitted, all items must include a vector field.

Each item in items:

FieldTypeRequiredDescription
item_idstringyesUnique item identifier
textstringnoText content (server generates embedding). Not allowed on model-free resources.
vectornumber[]noPre-computed embedding vector
metadataobjectnoArbitrary metadata

At least one of text or vector must be provided per item. On model-free resources, only vector is accepted.


List Resources

GET /v1/embeddings/resources

Returns all resources visible to the authenticated user. user_private resources are only visible to their creator.


Get Resource

GET /v1/embeddings/resources/:key

Returns a single resource by key. Returns 404 for user_private resources owned by another user.


Update Resource

PATCH /v1/embeddings/resources/:key

{ "name": "Updated Name", "description": "Updated description" }

Only name and description can be updated. access_mode is immutable.


Delete Resource

DELETE /v1/embeddings/resources/:key

Deletes the resource and all its items and vectors.


Upsert Items

POST /v1/embeddings/resources/:key/items

{ "items": [ { "item_id": "doc-1", "text": "Refunds are processed within 30 days.", "metadata": { "category": "policy" } }, { "item_id": "doc-2", "vector": [0.1, 0.2, 0.3, ...], "metadata": { "source": "local-model" } } ] }

Each item must have at least one of text or vector. When vector is provided, the server skips embedding generation for that item. Vector dimensions must match the resource’s embedding_dimensions. On model-free resources, all items must use vector — text items are rejected with a 400 error.


Bulk Upsert Items

POST /v1/embeddings/resources/:key/items/bulk-upsert

High-throughput variant of POST /v1/embeddings/resources/:key/items for large client-side imports (e.g. first-time indexing of multi-million-vector datasets). Accepts up to 10,000 items and a 50 MB request body on this route only (the single-batch endpoint stays at the default 25 MB limit).

Pre-computed vectors only. Items containing a text field are rejected batch-wide with HTTP 400 and code: "bulk_text_not_supported". Use the single-batch endpoint if you need server-side embedding generation.

{ "items": [ { "item_id": "frag-0001", "vector": [0.1, 0.2, 0.3, ...], "metadata": { "source": "chatgpt-export" } }, { "item_id": "frag-0002", "vector": [0.4, 0.5, 0.6, ...] } ] }

Response (full success — HTTP 200):

{ "object": "embedding_resource_items_bulk", "resource_key": "chat-search-v4-...", "upserted": 9999, "failed": [] }

Partial success (HTTP 207 Multi-Status): per-item validation failures are quarantined into failed[] while valid items are still persisted. Inspect failed[] regardless of status to discover per-item errors.

{ "object": "embedding_resource_items_bulk", "resource_key": "chat-search-v4-...", "upserted": 9950, "failed": [ { "item_id": "frag-bad-1", "error": "vector_dimension_mismatch" }, { "error": "missing_item_id" } ] }

Error codes in failed[].error:

CodeMeaning
missing_item_iditem_id missing or not a non-empty string
vector_missingItem has no vector field (and no text, which is disallowed here)
vector_dimension_mismatchvector length does not match the resource’s embedding_dimensions
vector_non_numericvector contains non-finite values (NaN, Infinity, or non-numbers)

Idempotency. Upserts are idempotent by item_id, so retrying on network errors is safe — previously stored items will simply be overwritten.


Async Ingestion (multi-million items)

For imports large enough that the sync bulk-upsert endpoint hits HTTP gateway timeouts (typically >2 M items), use the async ingestion flow. Clients upload an NDJSON file of pre-computed vectors directly to S3 via a presigned URL, then enqueue an ingestion job that processes the file in the background. Poll the job for progress.

Four-step flow.

  1. Request a presigned upload URL.

    POST /v1/files/presigned-upload

    { "purpose": "embedding_ingestion", "content_type": "application/x-ndjson", "content_encoding": "gzip", "content_length_hint": 524288000, "filename_hint": "chat-export-2026-04-17.ndjson.gz" }

    Response:

    { "object": "file_presigned_upload", "file_id": "01J7XY…", "url": "https://s3.eu-central-1.amazonaws.com/…?X-Amz-Signature=…", "headers": { "Content-Type": "application/x-ndjson" }, "expires_at": "2026-04-17T12:15:00Z", "storage_key": "org/…/app/…/purpose/embeddings/01J7XY…" }

    Clients must complete the S3 PUT within the 15-minute expires_at window.

  2. PUT the gzipped NDJSON directly to S3. One line per item: {"item_id": "...", "vector": [...], "metadata": {...}}. Alternatively use "vector_b64" with a base64-packed Float32Array to halve the payload size.

  3. Confirm the upload.

    POST /v1/files/:file_id/confirm

    Response {object: "file_confirm_upload", status: "ready", size_bytes, confirmed_at}. Idempotent — re-calling a confirmed file returns the same response. Returns 409 if the S3 object is missing or empty.

  4. Enqueue the ingestion job.

    POST /v1/embeddings/resources/:key/ingestion-jobs

    { "file_id": "01J7XY…", "job_id": "client-uuid-for-idempotency", "total_items_hint": 2000000 }

    Response (HTTP 202 on fresh enqueue, HTTP 200 on idempotent duplicate):

    { "object": "embedding_ingestion_job", "job_id": "client-uuid-for-idempotency", "resource_key": "chat-search-v4-…", "status": "queued", "created_at": "2026-04-17T12:00:05Z" }
  5. Poll for completion.

    GET /v1/embeddings/resources/:key/ingestion-jobs/:job_id

    { "object": "embedding_ingestion_job", "job_id": "…", "resource_key": "chat-search-v4-…", "status": "processing", "total_items": 2000000, "processed_items": 823421, "upserted_items": 823408, "failed_items": [ { "item_id": "chat123_42_3", "error": "vector_dimension_mismatch" } ], "fatal_error": null, "created_at": "…", "started_at": "…", "finished_at": null }

    Recommended polling interval: 5 seconds. failed_items[] is capped at the first 1,000 entries. Terminal states: done, failed, cancelled.

  6. (Optional) Cancel an in-flight job.

    DELETE /v1/embeddings/resources/:key/ingestion-jobs/:job_id

    Marks the job cancelled; the worker bails out between batches. Returns 409 if the job already reached a terminal state.

Billing. One meter fires per successful job, with endpointPath: /v1/embeddings/resources/:resourceKey/ingestion-jobs and bytes = dimensions × upserted_items × 4. Failed jobs do not meter beyond whatever was successfully persisted at the time of failure.

Retention. Job rows + uploaded S3 files are deleted 30 days after completion.


List Items

GET /v1/embeddings/resources/:key/items?limit=50

ParameterTypeRequiredDescription
limitnumbernoMaximum items to return

Delete Item

DELETE /v1/embeddings/resources/:key/items/:itemId


Query Resource

POST /v1/embeddings/resources/:key/query

Semantic similarity search. At least one of query or query_vector is required.

{ "query": "How long do refunds take?", "top_k": 5, "min_score": 0.7 }

Or with a pre-computed query vector:

{ "query_vector": [0.1, 0.2, 0.3, ...], "top_k": 5 }
FieldTypeRequiredDescription
querystringnoText query (server generates embedding)
query_vectornumber[]noPre-computed query vector
top_knumbernoMaximum results to return
min_scorenumbernoMinimum similarity score threshold
filterobjectnoMetadata filter

Response:

{ "data": [ { "item_id": "doc-1", "score": 0.92, "excerpt": "Refunds are processed within 30 days.", "metadata": { "category": "policy" } } ] }

Error Responses

ScenarioStatusMessage
No model and no dimensions400”dimensions is required when model is not provided”
Invalid access_mode400”access_mode must be ‘application_shared’ or ‘user_private‘“
Item with neither text nor vector400”Each item must have either ‘text’ or ‘vector‘“
Text item on model-free resource400”Cannot embed text items — resource has no model. Use the ‘vector’ field for pre-computed embeddings.”
Text query on model-free resource400”Cannot generate query embedding — resource has no model. Use ‘query_vector’ for pre-computed query vectors.”
Neither query nor query_vector400”Either ‘query’ or ‘query_vector’ is required”
Vector dimension mismatch (upsert)400”Vector dimensions (N) do not match resource embedding_dimensions (M)“
Vector dimension mismatch (query)400”Query vector dimensions (N) do not match resource embedding_dimensions (M)“
Non-owner access to user_private resource404”Embedding resource not found”
Non-owner mutation of application_shared resource403”Only the resource creator can mutate this resource”
Bulk upsert contains any text item400”Bulk endpoint does not support text items; use the single-batch endpoint for server-side embedding.” (code: "bulk_text_not_supported")
Bulk upsert: per-item validation failure207Valid items persisted; invalid items returned in failed[] with machine-readable error codes
Last updated on