With the release of Gemini 3.1 Pro, the generativelanguage.googleapis.com endpoint has received significant updates. Calling the REST API directly — without the SDK — is useful in many real-world situations: serverless environments, lightweight scripts, or any context where you need language-agnostic integration.
1. Model Name and Endpoint
When making REST API calls to Gemini 3.1 Pro, use one of these model identifiers:
- Stable:
gemini-3.1-pro - Latest alias:
gemini-3.1-pro-latest(always points to the most recent minor version) - Preview:
gemini-3.1-pro-exp
The base URL remains the same:
POST https://generativelanguage.googleapis.com/v1beta/models/{model-name}:generateContent
As of April 2026, v1beta remains the de-facto main API version, giving access to Gemini's latest features including thinking, Function Calling, and multimodal input. While v1 is more stable, it may not yet support the newest model capabilities, so v1beta is recommended during development.
2. Getting Your API Key and Authenticating
API Key (for personal projects and prototypes)
Visit Google AI Studio and click Create API key. Store your key as an environment variable — never hard-code it in source files.
export GEMINI_API_KEY="YOUR_GEMINI_API_KEY"Pass the key via query parameter or the x-goog-api-key header:
# Query parameter
POST .../generateContent?key=${GEMINI_API_KEY}
# Header (recommended)
-H "x-goog-api-key: ${GEMINI_API_KEY}"OAuth / Service Account (for production)
In production, use Google service account credentials instead of an API key. An OAuth 2.0 access token works with the generativelanguage.googleapis.com endpoint as a Bearer token:
ACCESS_TOKEN=$(gcloud auth print-access-token)
# or
ACCESS_TOKEN=$(gcloud auth application-default print-access-token)3. Your First Request with curl
Here is the simplest possible text generation request:
curl -s -X POST \
"https://generativelanguage.googleapis.com/v1beta/models/gemini-3.1-pro:generateContent" \
-H "x-goog-api-key: ${GEMINI_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"contents": [
{
"parts": [{"text": "Explain Python list comprehensions in 3 sentences."}]
}
]
}'The response is JSON:
{
"candidates": [
{
"content": {
"parts": [{"text": "List comprehensions provide a concise way..."}],
"role": "model"
},
"finishReason": "STOP",
"index": 0
}
],
"usageMetadata": {
"promptTokenCount": 14,
"candidatesTokenCount": 138,
"totalTokenCount": 152
}
}The generated text lives at candidates[0].content.parts[0].text. Use usageMetadata to monitor token consumption for cost management.
4. Text Generation with Python requests
import os
import requests
API_KEY = os.environ.get("GEMINI_API_KEY")
BASE_URL = "https://generativelanguage.googleapis.com/v1beta"
MODEL = "gemini-3.1-pro"
def generate_text(prompt: str, system_instruction: str = None) -> str:
"""Generate text with Gemini 3.1 Pro via REST API."""
url = f"{BASE_URL}/models/{MODEL}:generateContent"
headers = {
"x-goog-api-key": API_KEY,
"Content-Type": "application/json",
}
payload = {
"contents": [
{"role": "user", "parts": [{"text": prompt}]}
],
"generationConfig": {
"temperature": 0.7,
"topP": 0.95,
"maxOutputTokens": 2048,
},
}
# Optional system instruction
if system_instruction:
payload["systemInstruction"] = {
"parts": [{"text": system_instruction}]
}
response = requests.post(url, headers=headers, json=payload, timeout=60)
response.raise_for_status()
data = response.json()
return data["candidates"][0]["content"]["parts"][0]["text"]
if __name__ == "__main__":
result = generate_text(
prompt="What are the main differences between FastAPI and Django?",
system_instruction=(
"You are a senior backend engineer. "
"Explain concepts accurately but in a way beginners can follow."
)
)
print(result)
# Expected output:
# FastAPI is an ASGI-based micro-framework that leverages Python type hints
# for automatic API documentation, while Django is a full-featured MVC framework...5. Multi-Turn Chat (Managing Conversation History)
With the REST API, you maintain conversation history on the client side and send it with each request:
import os
import requests
API_KEY = os.environ.get("GEMINI_API_KEY")
BASE_URL = "https://generativelanguage.googleapis.com/v1beta"
MODEL = "gemini-3.1-pro"
class GeminiChat:
"""Multi-turn chat with automatic history management."""
def __init__(self, system_instruction: str = None):
self.history = []
self.system_instruction = system_instruction
self.url = f"{BASE_URL}/models/{MODEL}:generateContent"
self.headers = {
"x-goog-api-key": API_KEY,
"Content-Type": "application/json",
}
def send_message(self, user_message: str) -> str:
# Append user message to history
self.history.append({
"role": "user",
"parts": [{"text": user_message}]
})
payload = {"contents": self.history}
if self.system_instruction:
payload["systemInstruction"] = {
"parts": [{"text": self.system_instruction}]
}
response = requests.post(
self.url, headers=self.headers, json=payload, timeout=60
)
response.raise_for_status()
data = response.json()
model_reply = data["candidates"][0]["content"]["parts"][0]["text"]
# Append model reply to history for next turn
self.history.append({
"role": "model",
"parts": [{"text": model_reply}]
})
return model_reply
# Example usage
chat = GeminiChat(system_instruction="You are a Python tutor.")
print(chat.send_message("Explain type casting in Python"))
print(chat.send_message("What is the difference between int and float?"))
# Expected output:
# Type casting in Python uses built-in functions like int(), float(), str()...
# The int type stores whole numbers without a decimal point, while float...6. Streaming Responses
Improve perceived performance on long answers with streaming mode:
import os
import requests
import json
API_KEY = os.environ.get("GEMINI_API_KEY")
BASE_URL = "https://generativelanguage.googleapis.com/v1beta"
MODEL = "gemini-3.1-pro"
def stream_generate(prompt: str):
"""Stream text generation, printing chunks as they arrive."""
url = f"{BASE_URL}/models/{MODEL}:streamGenerateContent?alt=sse"
headers = {
"x-goog-api-key": API_KEY,
"Content-Type": "application/json",
}
payload = {
"contents": [{"role": "user", "parts": [{"text": prompt}]}],
"generationConfig": {"maxOutputTokens": 4096},
}
with requests.post(url, headers=headers, json=payload,
stream=True, timeout=120) as response:
response.raise_for_status()
for line in response.iter_lines():
if line and line.startswith(b"data: "):
data_str = line[6:].decode("utf-8")
if data_str == "[DONE]":
break
try:
chunk = json.loads(data_str)
parts = chunk.get("candidates", [{}])[0].get(
"content", {}).get("parts", [])
for part in parts:
if "text" in part:
print(part["text"], end="", flush=True)
except json.JSONDecodeError:
pass
print() # Final newline
if __name__ == "__main__":
stream_generate("Walk me through building a simple web scraper in Python.")
# Expected behavior:
# Text appears in real time as SSE chunks stream inUse streamGenerateContent with ?alt=sse to receive Server-Sent Events.
7. JavaScript / fetch
Access the API from Node.js or Deno with the native fetch API:
const API_KEY = process.env.GEMINI_API_KEY;
const MODEL = "gemini-3.1-pro";
const BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
async function generateContent(prompt) {
const url = `${BASE_URL}/models/${MODEL}:generateContent`;
const response = await fetch(url, {
method: "POST",
headers: {
"x-goog-api-key": API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
contents: [{ role: "user", parts: [{ text: prompt }] }],
generationConfig: {
temperature: 0.8,
maxOutputTokens: 1024,
},
}),
});
if (\!response.ok) {
const error = await response.json();
throw new Error(`API Error ${response.status}: ${JSON.stringify(error)}`);
}
const data = await response.json();
return data.candidates[0].content.parts[0].text;
}
// Example usage
generateContent("What are TypeScript type guards?")
.then(console.log)
.catch(console.error);
// Expected output:
// Type guards in TypeScript are expressions that narrow the type of a variable...8. Common Errors and Fixes
400 Bad Request — INVALID_ARGUMENT
Usually caused by an incorrect contents array structure. The role field must be "user" or "model" only — "system" is not a valid role. Use the systemInstruction top-level field for system prompts.
403 Forbidden — API key not valid
Either the key is wrong, or the Generative Language API has not been enabled for your Google Cloud project. Enable it in Google Cloud Console.
429 Resource Exhausted — RATE_LIMIT_EXCEEDED
The free tier has per-minute request limits. Implement exponential backoff for retries, or upgrade to a paid plan:
import time
def generate_with_retry(prompt, max_retries=3):
for attempt in range(max_retries):
try:
return generate_text(prompt)
except requests.HTTPError as e:
if e.response.status_code == 429:
wait_time = 2 ** attempt # 1, 2, 4 seconds
print(f"Rate limit hit. Retrying in {wait_time}s...")
time.sleep(wait_time)
else:
raise
raise Exception("Max retries exceeded")500 Internal Server Error
A temporary server-side issue. Wait a few seconds and retry.
9. Controlling Output with generationConfig
"generationConfig": {
"temperature": 0.7, // 0.0-2.0 (lower = more deterministic)
"topP": 0.95, // nucleus sampling
"topK": 40, // top-k sampling
"maxOutputTokens": 8192, // max tokens in the response
"stopSequences": ["---"], // stop generation at this string
"candidateCount": 1, // number of candidates (default 1)
"responseMimeType": "application/json" // force JSON output
}Setting responseMimeType: "application/json" instructs Gemini to return a properly formatted JSON response. Combine it with response_schema for strict schema enforcement.
Looking back
The Gemini 3.1 Pro REST API lets you integrate powerful AI capabilities into virtually any application — no SDK required. Here is a quick recap:
- Endpoint:
https://generativelanguage.googleapis.com/v1beta/models/gemini-3.1-pro:generateContent - Authentication: the
x-goog-api-keyheader is the simplest approach - Conversation history: managed on the client side as the
contentsarray - Streaming: use
streamGenerateContent?alt=sse - Rate limits: handle 429 errors with exponential backoff
Ready to go further? The Gemini 3.1 Pro Custom Tools Agent Guide covers advanced use cases including Function Calling and agent workflows with full code examples.