Embeddings
OpenAI-compatible embeddings under the same wallet — semantic search, RAG, and clustering with in-band cost.
POST /v1/embeddings turns text into vectors for semantic search, RAG,
recommendations, deduplication, and clustering. It is wire-identical to the
OpenAI Embeddings API, so the SDK works with just the base URL swap — and as
everywhere on Gopura, the exact billed cost rides in the response.
Already using OpenAI embeddings? Nothing needs re-embedding.
openai/text-embedding-3-small, openai/text-embedding-3-large and
openai/text-embedding-ada-002 are all served here, at their native
dimensions, so an existing index stays valid and the migration really is
just the base URL. Twenty-six embedding models are available in total —
see the table below.
from openai import OpenAI
client = OpenAI(base_url="https://api.gopura.net/v1", api_key=GOPURA_API_KEY)
result = client.embeddings.create(
model="alibaba/qwen3-embedding-0.6b",
input=["the gopuram rises over the temple", "an unrelated sentence"],
)
print(len(result.data), "vectors")
print(result.usage) # prompt_tokens + cost, in-band
curl https://api.gopura.net/v1/embeddings \
-H "Authorization: Bearer $GOPURA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "alibaba/qwen3-embedding-0.6b", "input": "the gopuram rises"}'
Request
| Field | Type | Notes |
|---|---|---|
model | string | any embedding model from the catalog |
input | string or string[] | up to 2,048 items / 1M characters total |
encoding_format | "float" | "base64" | optional, passed through |
dimensions | int | optional — only on models marked adjustable below; see the warning there |
Response
The OpenAI shape, plus usage.cost:
{
"object": "list",
"data": [{ "object": "embedding", "index": 0, "embedding": [0.0123, …] }],
"model": "alibaba/qwen3-embedding-0.6b",
"usage": { "prompt_tokens": 8, "total_tokens": 8, "cost": 0.0000002 }
}
Embeddings bill on input tokens only, at the rate shown per model in the
catalog (margin included; the figure matches usage.cost exactly).
Models and dimensions
Every model carries modality and its measured vector geometry in the live
catalog, so you can size a vector column before indexing anything:
curl -s https://api.gopura.net/v1/models \
| jq '.data[] | select(.modality == "embedding") | {id, embedding, pricing}'
{
"id": "openai/text-embedding-3-small",
"embedding": { "dimensions": 1536, "dimensions_adjustable": true },
"pricing": { "prompt": "0.000000025", "completion": "0" }
}
dimensions_adjustable: falsemeans the model returns its native width no matter what you ask for. Most such models do not error when you senddimensions— they accept it and return the full-width vector anyway, so a column sized from the value you requested will not match what arrives. Onlyopenai/text-embedding-ada-002fails loudly, with a400. Check this field rather than assuming;dimensionsappears in a model'ssupported_parametersonly where it is genuinely honored.
| Model | Dimensions | dimensions adjustable | Input /M |
|---|---|---|---|
alibaba/qwen3-embedding-0.6b | 1024 | yes | $0.0125 |
alibaba/qwen3-embedding-4b | 2560 | yes | $0.025 |
alibaba/qwen3-embedding-8b | 4096 | yes | $0.0625 |
amazon/titan-embed-text-v2 | 1024 | yes | $0.025 |
cohere/embed-v4.0 | 1536 | yes | $0.15 |
google/gemini-embedding-001 | 3072 | yes | $0.1875 |
google/gemini-embedding-2 | 3072 | yes | $0.25 |
google/text-embedding-005 | 768 | yes | $0.03125 |
google/text-multilingual-embedding-002 | 768 | yes | $0.03125 |
mistral/codestral-embed | 1536 | no | $0.1875 |
mistral/mistral-embed | 1024 | no | $0.125 |
openai/text-embedding-3-large | 3072 | yes | $0.1625 |
openai/text-embedding-3-small | 1536 | yes | $0.025 |
openai/text-embedding-ada-002 | 1536 | no | $0.125 |
perplexity/pplx-embed-v1-0.6b | 1024 | no | $0.005 |
perplexity/pplx-embed-v1-4b | 2560 | no | $0.0375 |
voyage/voyage-3-large | 1024 | no | $0.225 |
voyage/voyage-3.5 | 1024 | no | $0.075 |
voyage/voyage-3.5-lite | 1024 | no | $0.025 |
voyage/voyage-4 | 1024 | no | $0.075 |
voyage/voyage-4-large | 1024 | no | $0.15 |
voyage/voyage-4-lite | 1024 | no | $0.025 |
voyage/voyage-code-2 | 1536 | no | $0.15 |
voyage/voyage-code-3 | 1024 | no | $0.225 |
voyage/voyage-finance-2 | 1024 | no | $0.15 |
voyage/voyage-law-2 | 1024 | no | $0.15 |
Dimensions are measured, not taken from vendor documentation: each model
is embedded and the returned vector length recorded. Cheap bulk pick:
perplexity/pplx-embed-v1-0.6b ($0.005/M) or alibaba/qwen3-embedding-0.6b
($0.0125/M). Highest quality: google/gemini-embedding-001, cohere/embed-v4.0
or the voyage/* family. Sending a chat model here (or an embedding model to
/v1/chat/completions) returns a 400 naming the right endpoint.
Sizing a bulk indexing job
Three limits govern how fast a corpus can be indexed:
| Limit | Value | On exceeding |
|---|---|---|
| Items per request | 2,048 | 400 invalid_request |
| Characters per request | 1,000,000 total | 400 invalid_request |
| Requests per minute | your key's rpm_limit (default 600) | 429 rate_limited + Retry-After |
| Concurrent requests | your key's concurrency_limit (default 30) | 429 too_many_concurrent |
Read the two key limits rather than assuming them — they are per key, and yours may have been raised:
curl -s https://api.gopura.net/v1/key -H "Authorization: Bearer $GOPURA_API_KEY"
A practical shape for a large corpus: batch 100–200 chunks per request
(well inside both caps, and large enough that per-request overhead
disappears), run 8–16 requests concurrently, and honour Retry-After on
429 rather than retrying immediately. Embedding models whose upstream
publishes no context window are listed at a conservative 8,192-token
floor — that is a documented safe floor for batching, not a measured ceiling,
so a model may accept more.
Every response carries its exact cost in usage.cost, so a job's true spend
is the sum of what you already received — no reconciliation pass needed.