Skip to content
,

Gemini Embedding 2 Python Tutorial: Pricing, Dimensions and RAG

10–14 minutes
Sceptical woman in a violet blazer in a blue server room, Gemini logo, hook $0.20 per 1M Embedding 2

Google’s gemini-embedding-2 is now the embedding model to use in the Gemini API. The old preview ID has been retired and its requests are being sent to the stable model.

This guide shows you how to call it from Python with the google-genai SDK. You will learn the prices, the dimension options, the new task prefixes and a small working RAG example.

TLDR

  • Model ID: use gemini-embedding-2. It went GA on April 22, 2026.
  • Preview retired: from November 6, 2026, calls to gemini-embedding-2-preview are reported to route to gemini-embedding-2.
  • Price: $0.20 per 1M text tokens, or $0.10 with the Batch API. The free tier is free of charge.
  • Dimensions: 3072 by default, any size from 128 to 3072. Google recommends 768, 1536 or 3072.
  • Task types: there is no task_type setting anymore. You add a short text prefix instead.
  • Inputs: text, images, audio, video and PDF all map into one shared vector space.

What Changed with gemini-embedding-2-preview

Google released gemini-embedding-2-preview on March 10, 2026. It was Google’s first multimodal embedding model. The stable version, gemini-embedding-2, followed on April 22, 2026.

Release trackers quoting Google’s notice report that the preview is now deprecated. Effective November 6, 2026, API requests to the preview ID route to gemini-embedding-2.

The same notice says the stable model uses the same API schema, pricing and embedding space. So no other code changes or vector re-indexing should be needed. You should still change your model string, because aliases do not last forever.

Note: Google’s deprecations table still shows an earlier “earliest possible” shutdown date of August 10, 2026 for the preview. Those dates are the earliest a model might retire, not exact dates. Either way, the fix is the same: switch to gemini-embedding-2 now.

If you want to be extra safe before trusting the “no re-index” promise, run a quick check. Embed 20 to 50 of your stored documents again with the new ID. Then compare each new vector with the old one using cosine similarity. Scores very close to 1.0 mean your index is fine.

What gemini-embedding-2 Can Do

An embedding is a list of numbers that captures the meaning of an input. Inputs with similar meaning get vectors that sit close together. That is what powers semantic search, RAG, clustering and recommendations.

Google’s docs list these limits for gemini-embedding-2:

Input type Limit per request Formats
Text Up to 8,192 tokens Plain text
Images Up to 6 images PNG, JPEG
Audio Up to 180 seconds MP3, WAV
Video Up to 120 seconds (max 32 frames, audio track ignored) MP4, MOV
PDF 1 file, up to 6 pages PDF

Google recommends one page per PDF for the best quality. For long PDFs, split them into single pages first and embed each page.

The older gemini-embedding-001 is still available for text only. It has a smaller 2,048 token input limit. Its earliest shutdown date is listed as May 14, 2028.

Gemini Embedding 2 Pricing

These are the paid tier prices from Google’s pricing page. The free tier is listed as “free of charge” for all modalities, with rate limits. Batch prices are not available on the free tier.

Modality Standard (paid) Batch API (paid)
Text $0.20 per 1M tokens $0.10 per 1M tokens
Image $0.45 per 1M tokens (about $0.00012 per image) $0.225 per 1M tokens
Audio $6.50 per 1M tokens (about $0.00016 per second) $3.25 per 1M tokens
Video $12.00 per 1M tokens (about $0.00079 per frame) $6.00 per 1M tokens
Text on gemini-embedding-001 $0.15 per 1M tokens $0.075 per 1M tokens

Here is a simple example. Embedding a 10 million token document set costs about $2.00 at standard rates. With the Batch API it drops to about $1.00.

Tip: Embed your document set once with the Batch API, since indexing is rarely urgent. Use standard calls only for live user queries. For the full picture of Gemini costs, see my Gemini API pricing and free tier guide.

Set Up Python and Your API Key

You only need Python 3, the google-genai package and a free API key. NumPy helps with the similarity maths in the RAG example.

  1. Create an API key. Open Google AI Studio, go to the API keys page and click “Create API key”. New users get a project and key created for them.
  2. Install the SDK. Run pip install -U google-genai numpy in a virtual environment.
  3. Set the key as an environment variable. On macOS or Linux run export GEMINI_API_KEY="YOUR_API_KEY". On Windows PowerShell use $env:GEMINI_API_KEY="YOUR_API_KEY".
  4. Create the client. In Python, genai.Client() reads GEMINI_API_KEY for you, so you never paste the key into code.
  5. Run a test call. Use the first snippet below to confirm everything works.

New to the SDK? My Gemini API Python tutorial covers text generation, streaming and chat with the same client.

Your First Embedding in Python

This is the basic call from Google’s docs. It sends one string and prints the vector.

from google import genai

client = genai.Client()

result = client.models.embed_content(
    model="gemini-embedding-2",
    contents="What is the meaning of life?"
)

print(result.embeddings)

The result holds a list of embedding objects. Each one has a .values list of floats. With default settings, that list has 3072 numbers.

Embedding many texts in one call

There is one big change from the older model. If you pass several plain strings, gemini-embedding-2 returns one combined embedding for all of them. To get one vector per text, wrap each text in its own Content object.

from google import genai
from google.genai import types

client = genai.Client()

texts = ["First document", "Second document", "Third document"]

result = client.models.embed_content(
    model="gemini-embedding-2",
    contents=[
        types.Content(parts=[types.Part.from_text(text=t)])
        for t in texts
    ],
)

print(len(result.embeddings))  # 3 vectors, one per text

Warning: Passing a plain list of strings is the most common silent bug when moving from gemini-embedding-001. Your code runs, but you get one vector back instead of many, and search results become useless.

Choosing Output Dimensions

Gemini Embedding 2 is trained so you can cut vectors down without retraining. Set output_dimensionality to any value from 128 to 3072.

from google import genai
from google.genai import types

client = genai.Client()

result = client.models.embed_content(
    model="gemini-embedding-2",
    contents="What is the meaning of life?",
    config=types.EmbedContentConfig(output_dimensionality=768),
)

[embedding_obj] = result.embeddings
print(len(embedding_obj.values))  # 768

Smaller vectors use less storage and make search faster. Larger vectors keep more detail. Here is how to choose:

  • 768: a good default for most apps, chatbots and small RAG projects. It uses a quarter of the storage of 3072.
  • 1536: a middle ground when you need better recall on tricky or technical content.
  • 3072: the full size, for when search quality matters more than storage cost.
  • 128 to 512: only for very large indexes or quick first-pass filtering.

The good news: gemini-embedding-2 normalizes cut-down vectors for you. With gemini-embedding-001 you had to normalize anything below 3072 yourself.

Task Types Are Now Text Prefixes

The old model used a task_type setting such as RETRIEVAL_QUERY. gemini-embedding-2 does not accept it. Instead, you add a short prefix to the text itself.

Use case Prefix format
Search query task: search result | query: {content}
Question answering query task: question answering | query: {content}
Fact checking query task: fact checking | query: {content}
Code retrieval query task: code retrieval | query: {content}
Documents (for all retrieval tasks) title: {title} | text: {content}
Classification task: classification | query: {content}
Clustering task: clustering | query: {content}
Semantic similarity task: sentence similarity | query: {content}

Search is “asymmetric”. Queries get a task prefix, and documents get the title: ... | text: ... format. Classification, clustering and similarity are “symmetric”, so every input uses the same prefix.

Google warns not to use the sentence similarity prefix for search or retrieval. If you have no title for a document, a short label like the file name works fine.

Embedding Images and Other Media

All media types land in the same vector space as text. So a text query like “red running shoes” can find a matching product photo.

from google import genai
from google.genai import types

client = genai.Client()

with open("example.png", "rb") as f:
    image_bytes = f.read()

result = client.models.embed_content(
    model="gemini-embedding-2",
    contents=[
        types.Content(parts=[
            types.Part.from_bytes(data=image_bytes, mime_type="image/png")
        ])
    ],
    config=types.EmbedContentConfig(output_dimensionality=768),
)

print(len(result.embeddings[0].values))

Use mime_type="audio/mpeg", "video/mp4" or "application/pdf" for other files. Remember the limits in the table above, and note that media costs more per token than text.

A Small RAG Example with google-genai

RAG means “retrieval augmented generation”. You find the most relevant text chunks first, then give them to a chat model as context. This example keeps everything in memory with NumPy, so you can run it as one file.

import numpy as np
from google import genai
from google.genai import types

client = genai.Client()
EMBED_MODEL = "gemini-embedding-2"
CHAT_MODEL = "gemini-3.8-flash"
DIMS = 768

docs = [
    ("Refund policy", "Customers can request a full refund within 30 days of purchase."),
    ("Shipping", "Standard shipping takes 3 to 5 business days inside the US."),
    ("Support hours", "Live chat support is open Monday to Friday, 9am to 6pm EST."),
    ("Warranty", "All devices include a one year limited hardware warranty."),
]

def embed(texts):
    result = client.models.embed_content(
        model=EMBED_MODEL,
        contents=[types.Content(parts=[types.Part.from_text(text=t)]) for t in texts],
        config=types.EmbedContentConfig(output_dimensionality=DIMS),
    )
    return np.array([e.values for e in result.embeddings])

# 1. Index the documents once, using the document format
doc_texts = [f"title: {title} | text: {body}" for title, body in docs]
doc_vectors = embed(doc_texts)

def retrieve(question, k=2):
    # 2. Embed the query with the search prefix
    q_vec = embed([f"task: search result | query: {question}"])[0]
    # 3. Cosine similarity against every document
    scores = doc_vectors @ q_vec / (
        np.linalg.norm(doc_vectors, axis=1) * np.linalg.norm(q_vec)
    )
    top = np.argsort(scores)[::-1][:k]
    return [docs[i][1] for i in top]

def answer(question):
    context = "\n".join(retrieve(question))
    prompt = (
        "Answer using only this context. If the answer is not there, say so.\n\n"
        f"Context:\n{context}\n\nQuestion: {question}"
    )
    response = client.models.generate_content(model=CHAT_MODEL, contents=prompt)
    return response.text

print(answer("How long do I have to ask for my money back?"))

How the example works

  1. Index once. Each document gets the title: ... | text: ... prefix and is embedded at 768 dimensions.
  2. Embed the question. The user’s question gets the task: search result prefix.
  3. Score. Cosine similarity ranks every document against the question.
  4. Pick the top results. The two best chunks become the context.
  5. Generate. A Gemini chat model answers using only that context.

For a real project, store the vectors in a vector database instead of a NumPy array. Google’s docs point to tutorials for ChromaDB, Qdrant, Weaviate and Pinecone. On Google Cloud you can also use BigQuery, AlloyDB or Cloud SQL.

The chat model is the expensive part of RAG, not the embeddings. Swap CHAT_MODEL to a cheaper Flash-Lite model for simple Q&A, and read my prompt caching guide to cut repeat costs.

Migrating from gemini-embedding-001

Moving from the preview ID to gemini-embedding-2 is a one-line change. Moving from gemini-embedding-001 is a bigger job, because the two vector spaces are not compatible.

  1. Change the model string to gemini-embedding-2 everywhere, including background jobs.
  2. Remove task_type from your config and add the matching text prefix instead.
  3. Wrap each input in its own Content object if you embed lists of texts.
  4. Delete manual normalization code for cut-down vectors. It is no longer needed.
  5. Re-embed every stored document into a new index. Never mix old and new vectors in one index.
  6. Switch traffic to the new index once it is complete, then delete the old one.
Quick answer: Preview users just rename the model ID. Users of gemini-embedding-001 must re-embed everything, because its vectors cannot be compared with the new model’s vectors.

Troubleshooting: Common Problems and Fixes

  • “task_type” error or ignored setting: gemini-embedding-2 does not accept task_type. Remove it and use a prefix such as task: search result | query: ....
  • Only one vector comes back for many texts: you passed a plain list of strings. Wrap each text in types.Content(parts=[types.Part.from_text(text=t)]).
  • Search results look random after an upgrade: you probably mixed gemini-embedding-001 vectors with new ones. Re-embed the whole index with one model.
  • Input too long errors: text is capped at 8,192 tokens. Split long documents into chunks of a few hundred tokens each.
  • PDF quality is poor: a request takes one PDF of up to 6 pages. Split PDFs into single pages, as Google recommends.
  • 429 rate limit errors: the free tier has rate limits. Add retries with backoff, send smaller batches or move indexing to the Batch API.
  • Authentication errors: check that GEMINI_API_KEY is set in the same terminal or service that runs your code.

Frequently Asked Questions

Is gemini-embedding-2-preview still working?

Reports of Google’s November 6, 2026 notice say requests to the preview ID now route to gemini-embedding-2. Update your code to the stable ID so you do not depend on that alias.

Do I need to re-index after moving from the preview to gemini-embedding-2?

Google’s notice, as quoted by release trackers, says the embedding space is the same, so no re-indexing is needed. A quick cosine check on a few stored documents will confirm it for your data.

How much does Gemini Embedding 2 cost?

Text costs $0.20 per 1M tokens on the paid tier and $0.10 with the Batch API. Images, audio and video cost more, and the free tier is free of charge with rate limits.

What dimension size should I use?

Start with 768 for most apps, since it is cheap to store and Google recommends it. Move to 1536 or 3072 if your search quality needs a boost.

Can I still use task_type with gemini-embedding-2?

No. Add a text prefix instead, such as task: question answering | query: ... for questions and title: ... | text: ... for documents.

Can I search images with a text query?

Yes. Text, images, audio, video and PDFs all share one embedding space, so a text query can match an image embedding directly.

Is gemini-embedding-001 going away?

Not yet. It is still available for text-only work, and Google lists May 14, 2028 as its earliest shutdown date, with gemini-embedding-2 as the replacement.

Want to compare embedding and chat costs across providers? See my AI API pricing comparison.

Sources

Free weekly email

Get new AI guides every week

One short weekly email with our newest local AI, ComfyUI and AI tool guides. No spam, no pop-ups, just the good stuff.

Free. Unsubscribe anytime in one click. We only use your email to send this newsletter. See our privacy policy.

Sceptical woman in a violet blazer in a blue server room, Gemini logo, hook $0.20 per 1M Embedding 2