faucet
Request access
Guides

Embeddings

Turn text into vectors on /v1/embeddings — widths, retrieval quality, and what is billed.

POST /v1/embeddings works exactly like the chat endpoint — same key, same namespaced slugs, same limits and the same billing — and the AI SDK reaches it through embeddingModel:

import { embed, embedMany } from 'ai';

const { embedding } = await embed({
  model: faucet.embeddingModel('voyage/voyage-4'),
  value: 'sunny day at the beach',
});

const { embeddings, usage } = await embedMany({
  model: faucet.embeddingModel('fireworks/qwen3-embedding-8b'),
  values: ['chunk one', 'chunk two', 'chunk three'],
});

A model belongs to one endpoint

Sending an embedding model to /v1/chat/completions, or a chat model here, is a 400 that names the endpoint you actually wanted — not a 404, because the model does exist and the slug is not what needs fixing. See Choosing a model.

Shorter vectors, where the model offers them

dimensions picks a width, and the Models reference lists what each model will truncate to. Voyage's voyage-4 family and the Nomic models are trained so a shorter vector is still a good vector, which makes the width a storage decision rather than a quality one:

{ "model": "voyage/voyage-4", "input": ["a", "b"], "dimensions": 512 }

From the AI SDK, that width is a provider option rather than a top-level argument:

await embed({
  model: faucet.embeddingModel('voyage/voyage-4'),
  value: 'sunny day at the beach',
  providerOptions: { faucet: { dimensions: 512 } },
});

Rather than hard-coding the widths, ask: GET /v1/models/{id} publishes default_dimensions and, where Faucet enforces a set, supported_dimensions. The embeddings route runs the same derivation before it resolves a credential, so a width outside the published set is a local 400 rather than a provider-dependent surprise. See Model capabilities.

Retrieval quality: tell Voyage what the text is for

Voyage models embed a search query and a stored document differently, and saying which you have measurably improves retrieval. OpenAI's format has no field for it, so it rides on provider_options — embed your corpus as document and your queries as query:

{
  "model": "voyage/voyage-4",
  "input": ["chunk one", "chunk two"],
  "provider_options": { "voyage": { "input_type": "document" } }
}

Billing

Input tokens only — a vector is not tokens, and nothing charges for producing one. Token counts come back on every request, including on Voyage, which reports only a total of its own accord. See Streaming and usage.