What vector databases actually solve
Machine learning models are fundamentally limited: they only know what they were trained on. A vector database bridges that gap by storing the model's representation of your own data—embeddings that capture semantic meaning—so you can compare new inputs against your dataset without retraining or passing massive context windows to an LLM.
Without a vector database, giving a model context about your data is impractical. To answer a user query against a documentation corpus of 65,000 sentences (2.1 GB), you'd have to pass the entire dataset with every request, wait for processing, and repeat the whole process for each new query. Most models can't accept that much context in a single call, and even if they could, the memory and latency costs would be prohibitive.
With a vector database, the workflow becomes:
- Run your dataset through the model once, store the resulting embeddings.
- For each query, embed only the query itself.
- Retrieve the nearest vectors from the database.
Vectors capture how a model maps an input to its internal features. Similar vectors mean the model considers the inputs similar. At production scale—10,000 to 250,000 vectors, each potentially 1,536 dimensions—brute-force comparison isn't feasible. Vector databases use algorithms like k-nearest neighbors (kNN) or approximate nearest neighbor (ANN) to make similarity search tractable.
These techniques aren't limited to AI applications. Any task involving classification or anomaly detection—content moderation, security alerting—can leverage the same machinery to answer "is this similar to something I've seen before?"
A concrete example: product recommendations
Vector search is a natural fit for recommendation engines. To show how it works, take an e-commerce catalog. Each product gets a vector embedding generated from its description via a sentence similarity model. Associate each vector with a product URL or ID, then query with the embedding of the product the user is currently viewing.
The core pattern is simple:
export interface Env {
// This makes our vector index methods available on env.MY_VECTOR_INDEX.*
// e.g. env.MY_VECTOR_INDEX.insert() or .query()
TUTORIAL_INDEX: VectorizeIndex;
}
// Sample vectors: 3 dimensions wide.
//
// Vectors from a machine-learning model are typically ~100 to 1536 dimensions
// wide (or wider still).
const sampleVectors: Array<VectorizeVector> = [
{ id: '1', values: [32.4, 74.1, 3.2], metadata: { url: '/products/sku/13913913' } },
{ id: '2', values: [15.1, 19.2, 15.8], metadata: { url: '/products/sku/10148191' } },
{ id: '3', values: [0.16, 1.2, 3.8], metadata: { url: '/products/sku/97913813' } },
{ id: '4', values: [75.1, 67.1, 29.9], metadata: { url: '/products/sku/418313' } },
{ id: '5', values: [58.8, 6.7, 3.4], metadata: { url: '/products/sku/55519183' } },
];
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
if (new URL(request.url).pathname !== '/') {
return new Response('', { status: 404 });
}
// Insert some sample vectors into our index
// In a real application, these vectors would be the output of a machine learning (ML) model,
// such as Workers AI, OpenAI, or Cohere.
let inserted = await env.TUTORIAL_INDEX.insert(sampleVectors);
// Log the number of IDs we successfully inserted
console.info(`inserted ${inserted.count} vectors into the index`);
// In a real application, we would take a user query - e.g. "durable
// objects" - and transform it into a vector emebedding first.
//
// In our example, we're going to construct a simple vector that should
// match vector id #5
let queryVector: Array<number> = [54.8, 5.5, 3.1];
// Query our index and return the three (topK = 3) most similar vector
// IDs with their similarity score.
//
// By default, vector values are not returned, as in many cases the
// vectorId and scores are sufficient to map the vector back to the
// original content it represents.
let matches = await env.TUTORIAL_INDEX.query(queryVector, { topK: 3, returnVectors: true });
// We map over our results to find the most similar vector result.
//
// Since our index uses the 'cosine' distance metric, scores will range
// from 1 to -1. A value of '1' means the vector is the same; the
// closer to 1, the more similar. Values of -1 (least similar) and 0 (no
// match).
// let closestScore = 0;
// let mostSimilarId = '';
// matches.matches.map((match) => {
// if (match.score > closestScore) {
// closestScore = match.score;
// mostSimilarId = match.vectorId;
// }
// });
return Response.json({
// This will return the closest vectors: we'll see that the vector
// with id = 5 has the highest score (closest to 1.0) as the
// distance between it and our query vector is the smallest.
// Return the full set of matches so we can see the possible scores.
matches: matches,
});
},
};
The query vector [54.8, 5.5, 3.1] returns its closest match, [58.799, 6.699, 3.400], with a cosine similarity score near 1 indicating a strong match. In production, you'd sort results by score and return the top recommendations. Stored metadata could point to an R2 object, a D1 database row, or a Workers KV key.
End-to-end on Workers
A real implementation needs the same model for both indexing and querying, since different models represent features differently. Workers AI provides an embedding model that plugs directly into a Vectorize index:
import { Ai } from '@cloudflare/ai';
export interface Env {
TEXT_EMBEDDINGS: VectorizeIndex;
AI: any;
}
interface EmbeddingResponse {
shape: number[];
data: number[][];
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const ai = new Ai(env.AI);
let path = new URL(request.url).pathname;
if (path.startsWith('/favicon')) {
return new Response('', { status: 404 });
}
// We only need to generate vector embeddings just the once (or as our
// data changes), not on every request
if (path === '/insert') {
// In a real-world application, we could read in content from R2 or
// a SQL database (like D1) and pass it to Workers AI
const stories = ['This is a story about an orange cloud', 'This is a story about a llama', 'This is a story about a hugging emoji'];
const modelResp: EmbeddingResponse = await ai.run('@cf/baai/bge-base-en-v1.5', {
text: stories,
});
// We need to convert the vector embeddings into a format Vectorize can accept.
// Each vector needs an id, a value (the vector) and optional metadata.
// In a real app, our ID would typicaly be bound to the ID of the source
// document.
let vectors: VectorizeVector[] = [];
let id = 1;
modelResp.data.forEach((vector) => {
vectors.push({ id: `${id}`, values: vector });
id++;
});
await env.TEXT_EMBEDDINGS.upsert(vectors);
}
// Our query: we expect this to match vector id: 1 in this simple example
let userQuery = 'orange cloud';
const queryVector: EmbeddingResponse = await ai.run('@cf/baai/bge-base-en-v1.5', {
text: [userQuery],
});
let matches = await env.TEXT_EMBEDDINGS.query(queryVector.data[0], { topK: 1 });
return Response.json({
// We expect vector id: 1 to be our top match with a score of
// ~0.896888444
// We are using a cosine distance metric, where the closer to one,
// the more similar.
matches: matches,
});
},
};
This pipeline does four things: embeds three sentences with Workers AI's @cf/baai/bge-base-en-v1.5 text embedding model, inserts those vectors into the index, embeds a user query with the same model, and queries the index for matches.
Moving to production only requires changing where the initial vectors come from—loading real data from R2, D1, or another store—and scheduling periodic re-indexing via Cron Triggers. This is how Cloudflare's own Cursor AI assistant operates: documentation embeddings are generated and indexed ahead of time, then user queries are embedded and searched on the fly.
Bring your own embeddings
Vectorize is a standalone vector database, not a Workers AI add-on. If you're already using an embedding API such as OpenAI's Embedding API or Cohere's multilingual model, you can generate vectors externally and insert them directly. The query flow is identical: embed your input with your chosen model, then search the index.
# Vectorize has ready-to-go presets that set the dimensions and distance metric for popular embeddings models
$ wrangler vectorize create openai-index-example --preset=openai-text-embedding-ada-002
This is useful when you've already standardized on an embedding provider or validated a specific multimodal or multilingual model for your data.
Predictable pricing
Vector database pricing often hinges on infrastructure estimates: CPU, memory, peak vs. off-peak usage. Vectorize instead charges on two metrics: total vector dimensions stored and total vector dimensions queried per month. There are no per-index fees, so you can create separate indexes for dev, staging, and production experimentation.
| Workers Free (coming soon) | Workers Paid ($5/month) | |
|---|---|---|
| Queried vector dimensions included | 30M total queried dimensions / month | 50M total queried dimensions / month |
| Stored vector dimensions included | 5M stored dimensions / month | 10M stored dimensions / month |
| Additional cost | $0.04 / 1M vector dimensions queried or stored | $0.04 / 1M vector dimensions queried or stored |
The pricing formula is straightforward: (total vector dimensions queried + stored) * dimensions_per_vector * price. Query volume scales linearly; reducing dimensions per vector lowers cost directly.

The Workers paid plan includes a usage allowance: 10,000 vectors at 384 dimensions each with 5,000 daily queries totals 49 million vector dimensions per month and fits within the plan's $5/month fee. Indexes are not deleted for inactivity.
Pricing is not fully finalized, but is expected to stay close to these numbers. The goal is to avoid the common failure mode of building on a platform and later discovering the cost model doesn't work at scale.
Vectorize is now in open beta
Vectorize, Cloudflare’s vector database, is now available in open beta for all developers on a paid Workers plan. You can start using it immediately via the developer documentation.
Cloudflare has a clear roadmap for Vectorize in the coming months. Planned work includes a new query engine for better query performance and support for larger indexes, along with sub-index filtering, increased metadata limits, and per-index analytics.
For developers looking to get hands-on, two tutorials stand out. The semantic search tutorial shows how to combine Workers AI and Vectorize for document search, running entirely on Cloudflare. Alternatively, there’s an example of using OpenAI with Vectorize for retrieval-augmented generation to give an LLM more context and improve answer accuracy.
Questions about using Vectorize, or want to share what you’re building on Workers AI? Join the product and engineering teams in the #vectorize and #workers-ai channels on the Developer Discord.



