Embeddings
POST https://api.mafdet.ai/v1/embeddings
Turn text into vectors with gemini-embedding-2. The endpoint is
OpenAI-compatible, so any OpenAI SDK works by pointing base_url at
https://api.mafdet.ai/v1.
gemini-embedding-2 is the text embedding model. For image and video
embeddings, use multimodal-embedding-1 (see Image embeddings
and Video embeddings below). Audio and PDF embedding inputs
are not enabled yet.
Request fields
| Field | Type | Description |
|---|---|---|
model | string | gemini-embedding-2 |
input | string | string[] | A single text, or an array of texts (batch) |
dimensions | number | Optional output vector size: 768, 1536, or 3072 (default 3072) |
Limits (per request): up to 100 inputs, 100,000 characters per input,
2 MB total. Empty input, an unsupported dimensions, or any non-string
element is rejected before the request reaches the model.
Response fields
| Field | Description |
|---|---|
object | list |
data | Array of { object: "embedding", embedding: number[], index } |
usage | prompt_tokens / total_tokens (embeddings have no completion_tokens) |
Billing
Embeddings are billed on input tokens only — there is no generated output.
gemini-embedding-2 sells at $0.24 per 1M input tokens (see
Models overview for the live catalog price).
curl
curl https://api.mafdet.ai/v1/embeddings \
-H "Authorization: Bearer $MAFDET_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-embedding-2",
"input": "The quick brown fox."
}'
Python (OpenAI SDK)
from openai import OpenAI
client = OpenAI(
base_url="https://api.mafdet.ai/v1",
api_key="sk-mafdet-xxxxxxxxxxxxxxxx",
)
resp = client.embeddings.create(
model="gemini-embedding-2",
input=["first text", "second text"],
dimensions=768,
)
print(len(resp.data), "vectors,", len(resp.data[0].embedding), "dims")
Node.js
const res = await fetch("https://api.mafdet.ai/v1/embeddings", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.MAFDET_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "gemini-embedding-2",
input: "The quick brown fox.",
}),
});
const data = await res.json();
console.log(data.data[0].embedding.length, "dims");
Image embeddings
multimodal-embedding-1 turns an image into a 1408-dim vector in the same
space as its text embeddings, for image search and multimodal retrieval. It is a
separate model from gemini-embedding-2 and uses the same /v1/embeddings
endpoint.
Pass each image as a data:image/...;base64,... data URI (a plain data URI
string, or an array of them for up to 8 images). Text, bare base64, and
image_url objects are rejected — send images as data URIs only.
# IMAGE=$(base64 -w0 photo.png)
curl https://api.mafdet.ai/v1/embeddings \
-H "Authorization: Bearer $MAFDET_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"model\": \"multimodal-embedding-1\",
\"input\": \"data:image/png;base64,$IMAGE\"
}"
import base64
from openai import OpenAI
client = OpenAI(base_url="https://api.mafdet.ai/v1", api_key="sk-mafdet-...")
with open("photo.png", "rb") as f:
uri = "data:image/png;base64," + base64.b64encode(f.read()).decode()
resp = client.embeddings.create(model="multimodal-embedding-1", input=uri)
print(len(resp.data[0].embedding), "dims") # 1408
Billing is per image: multimodal-embedding-1 sells at $0.00012 per image
(1 image = 1 unit). Up to 8 images per request. Errors (400, never billed):
MM_EMBEDDING_NO_IMAGE (no image data URI), MM_EMBEDDING_MODALITY_NOT_ENABLED
(a non-image item was included), MM_EMBEDDING_TOO_MANY_IMAGES.
Video embeddings
multimodal-embedding-1 also embeds video into the same 1408-dim space. Unlike
images, a video is passed in the Vertex-native form — an array element
{"video": {"bytesBase64Encoded": "<raw base64>"}} (raw base64, not a
data: URI). The provider returns one vector per interval of the clip.
Limits (per request), a staged first release: 1 video, up to 6 seconds, and up to 300 KB. The size cap matters as much as the duration one — a clip's base64 is what counts against your key's per-minute token limit (roughly 929 tokens per KB), so a short but high-bitrate clip can still be too large. Both caps are checked before the model is called, so an oversized clip is never billed.
import base64
from openai import OpenAI
client = OpenAI(base_url="https://api.mafdet.ai/v1", api_key="sk-mafdet-...")
with open("clip.mp4", "rb") as f:
b64 = base64.b64encode(f.read()).decode()
resp = client.embeddings.create(
model="multimodal-embedding-1",
input=[{"video": {"bytesBase64Encoded": b64}}],
)
print(len(resp.data), "vectors,", len(resp.data[0].embedding), "dims")
Billing is per second of video: $0.0006 per second (the clip's own
duration, rounded up to the whole second). A 6-second clip costs
6 × $0.0006 = $0.0036. Errors (400, never billed): VIDEO_EMBEDDING_NOT_ENABLED
(video not enabled for this deployment), MM_EMBEDDING_VIDEO_TOO_LONG (over the
duration cap), MM_EMBEDDING_VIDEO_TOO_LARGE (over the size cap),
MM_EMBEDDING_VIDEO_UNREADABLE (not a readable mp4 — an unmeasurable clip is
refused, never billed at 0), MM_EMBEDDING_TOO_MANY_VIDEOS.
Note: even within these caps, a video's base64 counts against your key's per-minute token limit — a 300 KB clip is worth roughly 279,000 tokens. A key on a lower plan can therefore still hit
429; if you plan to embed video, use a key whosetpmlimit covers it.
Error codes
| Code | Meaning |
|---|---|
EMBEDDING_MODALITY_NOT_ENABLED | Text model got a non-text item (use multimodal-embedding-1 for images) |
EMBEDDING_INPUT_EMPTY | Empty string or empty array |
EMBEDDING_ITEM_TOO_LONG | An input exceeded the per-item character limit |
EMBEDDING_BATCH_TOO_LARGE | More than 100 inputs in one call |
EMBEDDING_INPUT_TOO_LARGE | Combined input exceeded the total byte limit |
EMBEDDING_DIMENSIONS_INVALID | dimensions not one of 768 / 1536 / 3072 |
All of the above return 400 before the provider is called, so a rejected
request is never billed.