Gemini API and SDKs

The Gemini API surface in 2026 is one REST shape with two front doors: the Gemini Developer API (host generativelanguage.googleapis.com, key auth, fast onboarding via AI Studio) and Vertex AI (host aiplatform.googleapis.com, IAM auth, enterprise controls). Both are spoken by the unified google-genai SDK (Python pip install google-genai, JS npm install @google/genai, plus Go / Java / C#) — the same SDK switches mode by constructor argument. The legacy google-generativeai Python package and the original Vertex AI SDK are both being phased out in favor of google-genai; new code should use the unified SDK.

See also

1. Client initialization

Developer API (AI Studio key)

from google import genai
client = genai.Client(api_key="GEMINI_API_KEY")  # or set env GEMINI_API_KEY / GOOGLE_API_KEY
import { GoogleGenAI } from "@google/genai";
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });

Vertex AI (GCP project)

client = genai.Client(
    vertexai=True,
    project="my-gcp-project",
    location="us-central1",
)
const ai = new GoogleGenAI({
  vertexai: true,
  project: "my-gcp-project",
  location: "us-central1",
});

In Vertex mode the SDK uses Application Default Credentials (ADC). Authenticate with gcloud auth application-default login locally or service-account JSON on servers.

Vertex AI Express Mode

client = genai.Client(vertexai=True, api_key="VERTEX_EXPRESS_KEY")

Express keys provide an API-key on-ramp to Vertex without full IAM. Subset of Vertex features.

2. Core method: client.models.generate_content

The canonical entry point for everything except streaming, files, caches, batches, and chats (which have their own resource paths).

response = client.models.generate_content(
    model="gemini-3.5-flash",                          # bare ID — SDK adds models/ prefix
    contents="Explain quantum entanglement.",          # string OR list of Parts
    config=genai.types.GenerateContentConfig(
        system_instruction="You are a physics tutor.",
        temperature=0.7,
        top_p=0.95,
        top_k=40,
        max_output_tokens=2048,
        candidate_count=1,                              # only 1 supported on most models
        stop_sequences=["END"],
        response_mime_type="text/plain",                # or "application/json"
        safety_settings=[...],                          # see safety section
        tools=[...],
        tool_config=...,
        cached_content="cachedContents/abc123",         # reference an explicit cache
        thinking_config=genai.types.ThinkingConfig(
            thinking_budget=1024,                       # 2.5 series
            # thinking_level="medium",                  # 3.x series (Gemini 3 only)
            include_thoughts=True,
        ),
    ),
)
print(response.text)
print(response.usage_metadata.prompt_token_count,
      response.usage_metadata.candidates_token_count,
      response.usage_metadata.thoughts_token_count,
      response.usage_metadata.cached_content_token_count)

contents accepts a string, a Content object, or a list mixing Content / Part / strings / PIL images / File references / bytes. The SDK type-routes automatically.

3. Streaming

for chunk in client.models.generate_content_stream(
    model="gemini-3.5-flash",
    contents="Write a haiku.",
):
    print(chunk.text, end="", flush=True)
const stream = await ai.models.generateContentStream({
  model: "gemini-3.5-flash",
  contents: "Write a haiku.",
});
for await (const chunk of stream) {
  process.stdout.write(chunk.text ?? "");
}

Streaming uses Server-Sent Events under the hood. Each chunk has a partial candidates[].content.parts[].text. Function call parts arrive whole, not partial.

4. Async

The Python SDK exposes async via client.aio:

async def main():
    response = await client.aio.models.generate_content(
        model="gemini-3.5-flash",
        contents="Hello",
    )

JS / Node SDK is async-native.

5. Multi-turn chat

chat = client.chats.create(model="gemini-3.5-flash")
print(chat.send_message("Hi, my name is Alex.").text)
print(chat.send_message("What's my name?").text)
# History accumulates in chat.history

The chats helper auto-manages history. Stateful only on the client — server is stateless; every send replays the conversation.

For stateful conversation on the server (Vertex), use the Live API or the new conversation endpoints (limited GA). For most use cases, client-side history replay is fine and supports prompt caching.

6. Multimodal input

from google.genai import types
import pathlib
 
response = client.models.generate_content(
    model="gemini-3.5-flash",
    contents=[
        "What is in this image?",
        types.Part.from_bytes(
            data=pathlib.Path("photo.jpg").read_bytes(),
            mime_type="image/jpeg",
        ),
    ],
)

For files larger than ~20 MB or repeated use, upload via Files API first:

myfile = client.files.upload(file="bigvideo.mp4")
response = client.models.generate_content(
    model="gemini-3.5-flash",
    contents=["Summarize this video.", myfile],
)

See vision-and-long-context-gemini for sizing details.

7. Files API

# Upload
f = client.files.upload(file="report.pdf",
                       config=types.UploadFileConfig(
                           display_name="Quarterly Report",
                           mime_type="application/pdf",
                       ))
 
# List
for f in client.files.list():
    print(f.name, f.display_name, f.state)
 
# Get / delete
f = client.files.get(name=f.name)
client.files.delete(name=f.name)

Per ai.google.dev/gemini-api/docs/vision and the file-API docs:

  • Files persist for 48 hours before automatic deletion.
  • Max file size: 2 GB per file; max 20 GB total per project.
  • Files are private to the GCP project / API-key tier and cannot be shared across projects.

8. Caches API

# Create
cache = client.caches.create(
    model="models/gemini-3.5-flash",
    config=types.CreateCachedContentConfig(
        system_instruction="You are a legal-document analyzer.",
        contents=[long_document],
        ttl="3600s",                  # 1 hour
        # OR expire_time="2026-05-26T00:00:00Z"
    ),
)
 
# Use
resp = client.models.generate_content(
    model="gemini-3.5-flash",
    contents="Summarize Section 3.",
    config=types.GenerateContentConfig(cached_content=cache.name),
)
 
# Manage
caches = list(client.caches.list())
client.caches.update(name=cache.name,
                    config=types.UpdateCachedContentConfig(ttl="7200s"))
client.caches.delete(name=cache.name)

See context-caching-gemini for the cost model.

9. Batch mode

50 % discount, 24-hour SLA (per ai.google.dev/gemini-api/docs/batch-mode).

# JSONL input
batch = client.batches.create(
    model="gemini-3.5-flash",
    src="files/jsonl-input-file-name",        # uploaded JSONL
    config=types.CreateBatchJobConfig(
        display_name="nightly-summarization",
    ),
)
# Poll
while batch.state in {"JOB_STATE_PENDING", "JOB_STATE_RUNNING"}:
    time.sleep(30)
    batch = client.batches.get(name=batch.name)
# Read results
result_file = client.files.download(name=batch.dest.file_name)

JSONL input format (one request per line):

{"key": "req-1", "request": {"contents": [{"parts":[{"text":"Hello"}]}]}}
{"key": "req-2", "request": {"contents": [{"parts":[{"text":"World"}]}]}}

States: JOB_STATE_PENDING, JOB_STATE_RUNNING, JOB_STATE_SUCCEEDED, JOB_STATE_FAILED, JOB_STATE_CANCELLED, JOB_STATE_EXPIRED (after 48h).

Tip: check batch.dest.failed_request_count — partial failures are common. The output JSONL includes one line per request with either response or error.

For embeddings batching, use client.batches.create_embeddings(...).

10. Embeddings

result = client.models.embed_content(
    model="gemini-embedding-2",
    contents=["chunk 1 text", "chunk 2 text"],
    config=types.EmbedContentConfig(
        output_dimensionality=768,             # Matryoshka: 128 / 256 / 768 / 1536 / 3072
        task_type="RETRIEVAL_DOCUMENT",        # only for legacy gemini-embedding-001
    ),
)
for emb in result.embeddings:
    print(len(emb.values))

See embeddings-and-vertex-search-gemini for the full model + task-type matrix.

11. Token counting

resp = client.models.count_tokens(
    model="gemini-3.5-flash",
    contents=long_text,
)
print(resp.total_tokens)

count_tokens is free and does not invoke the model — useful for pre-flight cost estimation, especially before caching large content.

12. Function / tool calling (surface)

def get_weather(location: str, unit: str = "celsius") -> dict:
    """Returns current weather for a city."""
    return {"temp": 22, "unit": unit, "condition": "sunny"}
 
resp = client.models.generate_content(
    model="gemini-3.5-flash",
    contents="What's the weather in Tokyo?",
    config=types.GenerateContentConfig(
        tools=[get_weather],                       # SDK auto-introspects signature + docstring
        tool_config=types.ToolConfig(
            function_calling_config=types.FunctionCallingConfig(mode="AUTO"),
        ),
    ),
)
print(resp.text)

The Python SDK’s “automatic function calling” feature: pass a Python callable and the SDK both declares it (from signature + type hints + docstring) and executes it on your behalf when the model calls it, then feeds the result back. To disable auto-execution and handle manually:

config=types.GenerateContentConfig(
    tools=[get_weather],
    automatic_function_calling=types.AutomaticFunctionCallingConfig(disable=True),
)

See tool-use-and-function-calling-gemini for the full mechanics.

13. Safety settings

Per ai.google.dev/gemini-api/docs/safety-settings:

from google.genai.types import HarmCategory, HarmBlockThreshold
 
safety_settings = [
    types.SafetySetting(
        category=HarmCategory.HARM_CATEGORY_HARASSMENT,
        threshold=HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
    ),
    types.SafetySetting(
        category=HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
        threshold=HarmBlockThreshold.BLOCK_ONLY_HIGH,
    ),
]

Categories: HARASSMENT, HATE_SPEECH, SEXUALLY_EXPLICIT, DANGEROUS_CONTENT, CIVIC_INTEGRITY. Thresholds: BLOCK_NONE, BLOCK_ONLY_HIGH, BLOCK_MEDIUM_AND_ABOVE, BLOCK_LOW_AND_ABOVE. BLOCK_NONE requires elevated tier on most categories.

When a response is blocked, response.candidates[0].finish_reason == "SAFETY" and response.candidates[0].safety_ratings lists the triggered categories.

14. REST API directly

For non-SDK environments. The base URL pattern:

POST https://generativelanguage.googleapis.com/v1beta/models/gemini-3.5-flash:generateContent
Header: x-goog-api-key: $GEMINI_API_KEY
Content-Type: application/json

Body shape:

{
  "contents": [
    {"role": "user", "parts": [{"text": "Hello"}]}
  ],
  "systemInstruction": {"parts": [{"text": "You are concise."}]},
  "generationConfig": {
    "temperature": 0.7,
    "maxOutputTokens": 1024,
    "responseMimeType": "application/json",
    "responseSchema": {...},
    "thinkingConfig": {"thinkingBudget": 1024}
  },
  "tools": [{"functionDeclarations": [...]}],
  "toolConfig": {"functionCallingConfig": {"mode": "AUTO"}},
  "safetySettings": [...]
}

Streaming variant: :streamGenerateContent (SSE chunks).

curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.5-flash:generateContent" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"contents":[{"role":"user","parts":[{"text":"Explain caching"}]}]}'

15. Response shape

{
  "candidates": [
    {
      "content": {
        "role": "model",
        "parts": [
          {"text": "..."},
          {"functionCall": {"name": "...", "args": {...}, "id": "fc_abc"}},
          {"executableCode": {"language": "PYTHON", "code": "..."}},
          {"codeExecutionResult": {"outcome": "OUTCOME_OK", "output": "..."}},
          {"thought": true, "text": "summary of reasoning"}
        ]
      },
      "finishReason": "STOP",
      "safetyRatings": [...],
      "groundingMetadata": {...},
      "citationMetadata": {...}
    }
  ],
  "usageMetadata": {
    "promptTokenCount": 50,
    "candidatesTokenCount": 200,
    "thoughtsTokenCount": 800,
    "cachedContentTokenCount": 5000,
    "totalTokenCount": 1050
  }
}

finishReason values: STOP, MAX_TOKENS, SAFETY, RECITATION (output matched training data too closely), OTHER, BLOCKLIST, PROHIBITED_CONTENT, SPII, MALFORMED_FUNCTION_CALL.

16. Error model

Common HTTP errors:

  • 400 INVALID_ARGUMENT — schema violation, bad model ID, exceeded context, malformed content
  • 401 / 403 PERMISSION_DENIED — API key missing / wrong / project missing API
  • 404 NOT_FOUND — model ID typo, file not found
  • 429 RESOURCE_EXHAUSTED — rate-limit or quota
  • 500 INTERNAL — backend; retry with exponential backoff
  • 503 UNAVAILABLE — backend overloaded; retry

The SDK retries 429 / 5xx with exponential backoff by default. Use http_options=types.HttpOptions(timeout=30.0) or client.options.retry to tune.

17. Rate limits

Per ai.google.dev/gemini-api/docs/rate-limits. Three free tier + four paid tiers. Limits are per-model and per-tier on:

  • RPM (requests per minute)
  • TPM (tokens per minute)
  • RPD (requests per day)

Paid Tier 1 typically gives 1,000 RPM / 1M TPM / 1.5M RPD on Gemini Flash models. Tier 2 / 3 / 4 unlock larger limits with spend thresholds. Vertex AI uses different per-region quotas; check the GCP console.

18. SDK versioning + API versions

The unified SDK defaults to v1beta API endpoints — covering all current features. Pin to stable v1:

client = genai.Client(
    api_key="...",
    http_options=types.HttpOptions(api_version="v1"),
)

v1 is missing some features (thinking, caching, code-execution tool, some batch modes). Use v1beta for production unless you have a strict stability requirement and don’t need those features.

19. Migrating from google-generativeai (legacy SDK)

The pre-2025 Python SDK was google-generativeai. Migration to google-genai:

LegacyNew
import google.generativeai as genaifrom google import genai
genai.configure(api_key=...)client = genai.Client(api_key=...)
model = genai.GenerativeModel("gemini-1.5-flash")(no model object — use client.models.generate_content(model="..."))
model.generate_content("...")client.models.generate_content(model="...", contents="...")
model.start_chat()client.chats.create(model="...")
genai.upload_file("...")client.files.upload(file="...")
model.count_tokens("...")client.models.count_tokens(model="...", contents="...")

The legacy SDK is in maintenance mode; new features (Files API improvements, structured caches, batch v2) ship only on google-genai.

20. Further reading