Imported from abdullibrahim733-pixel/Eagle (
hermes-backup/skills/mlops/gemini-api-python/SKILL.md). Install upstream withnpx skills add abdullibrahim733-pixel/Eagle --skill gemini-api-python. Copyright stays with the author.
Gemini API Python Integration
Call Google's Gemini models from Python serverless functions — with system instructions, live context injection, and proper error handling.
Architecture (Frontend → Serverless → Gemini)
Browser UI → POST /api/ai/chat → Python serverless → Gemini API → response back
The serverless function acts as a proxy that:
- Receives the user's message + live app context (telemetry, state, etc.)
- Builds a structured prompt combining system instructions + context + user question
- Calls Gemini API with the API key (hidden from the client)
- Returns the response to the frontend
Setup
1. API Key
Get a key at https://aistudio.google.com/apikey (free tier: 60 requests/min with billing-enabled project).
Vercel env var:
echo "YOUR_API_KEY" | vercel env add GOOGLE_AI_API_KEY production --yes
# Use --yes to skip interactive confirmation
Important: Use --yes flag when piping the key via stdin. Without it, the piped content is consumed as interactive prompt answers instead of the env value.
# In your serverless handler:
GOOGLE_AI_API_KEY = os.environ.get("GOOGLE_AI_API_KEY", "")
2. API Endpoint
import os, json
import urllib.request, urllib.error
GEMINI_MODEL = "gemini-2.0-flash"
GEMINI_API_URL = f"https://generativelanguage.googleapis.com/v1/models/{GEMINI_MODEL}:generateContent"
V1 vs v1beta: Use
/v1/(not/v1beta/). Thev1betaendpoint may not support certain models (e.g.,gemini-1.5-flashreturns 404 on v1beta). Preferv1for stability.
3. System Instruction Pattern
SYSTEM_PROMPT = """You are an AI assistant inside [app name].
Your role:
- You receive live telemetry/context with each message.
- Answer questions concisely (1-3 sentences) based on that context.
- Be helpful, specific, and grounded in the data provided.
"""
payload = {
"contents": [
{
"role": "user",
"parts": [{"text": f"## Current State\n{context_text}\n\n## User Question\n{user_message}"}]
}
],
"systemInstruction": {
"parts": [{"text": SYSTEM_PROMPT}]
},
"generationConfig": {
"temperature": 0.7,
"maxOutputTokens": 250,
"topP": 0.9
}
}
Key design choices:
- Inject live context as structured markdown section before the user question — Gemini handles this well.
systemInstructionis passed as a separate top-level field (not insidecontents).- Keep
maxOutputTokenslow (150–300) for chat responses; higher (1024+) for analysis/creative tasks.
4. Calling the API
req = urllib.request.Request(
f"{GEMINI_API_URL}?key={GOOGLE_AI_API_KEY}",
data=json.dumps(payload).encode("utf-8"),
headers={"Content-Type": "application/json"},
method="POST"
)
with urllib.request.urlopen(req, timeout=10) as resp:
result = json.loads(resp.read().decode())
candidates = result.get("candidates", [])
if candidates:
reply = candidates[0].get("content", {}).get("parts", [{}])[0].get("text", "")
5. Frontend Chat UI Pattern (Vanilla JS)
async function sendMessage(text) {
const context = collectAppState(); // Grab live state from the app
const resp = await fetch("/api/ai/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: text, context: context }),
signal: AbortSignal.timeout(15000),
});
const data = await resp.json();
displayReply(data.reply);
}
Context shape (sent from browser):
{
"speed": 15,
"battery": 72,
"state": "Path Following",
"decision": "Following path",
"waypoint": 12,
"sensors": {"fm": {"distance": 8, "active": false}}
}
Error Handling
Common Gemini API errors
| HTTP | Error | Meaning | Fix |
|---|---|---|---|
| 429 | Quota exceeded for metric: generativelanguage.googleapis.com/generate_content_free_tier_requests, limit: 0 |
Free tier quota exhausted for this project/API key | Enable billing on the Google Cloud project (you won't be charged at free tier rates). Or create a new project + new key. |
| 404 | models/gemini-1.5-flash is not found for API version v1beta |
Model not available at the API version used | Use v1/ instead of v1beta/. Check model availability at the chosen endpoint. |
| 403 | API key not valid | Key was revoked or doesn't have Gemini API enabled | Generate a new key at aistudio.google.com. Verify the project has the Generative Language API enabled. |
| 400 | systemInstruction is not supported |
Model doesn't support system instructions | Use gemini-2.0-flash or newer. Older models (1.0, 1.5-pro) may not support systemInstruction as a top-level field — inline it into the user message instead. |
Python error handling pattern
try:
with urllib.request.urlopen(req, timeout=10) as resp:
result = json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
error_body = e.read().decode()
reply = f"Error ({e.code}): {parse_error_message(error_body)}"
except urllib.error.URLError as e:
reply = f"Network error: {e.reason}"
Model Selection
| Model | Strengths | Latency | System Prompt | Free Tier |
|---|---|---|---|---|
gemini-2.0-flash |
Fast, good context, supports systemInstruction |
Low | ✅ Yes | 60 req/min |
gemini-2.0-flash-lite |
Faster, cheaper, slightly lower quality | Very low | ✅ Yes | 60 req/min |
gemini-1.5-flash |
Previous gen, still widely available | Low | ✅ Yes | Varies |
gemini-1.5-pro |
Higher quality, slower, more expensive | High | ✅ Yes | Varies |
Default choice: gemini-2.0-flash — balances speed and quality, supports system instructions.
Pitfalls
- Piping API keys to
vercel env addwithout--yes— The key content gets consumed as interactive prompt answers. Always usevercel env add <name> <env> --yeswhen piping from stdin. - Environment variable not picked up — After adding an env var to Vercel, you need a new deployment. Use
git commit --allow-empty -m "redeploy" && git pushto trigger a fresh deploy. - CORS on POST requests — Python
BaseHTTPRequestHandlerneeds explicit CORS headers on bothdo_OPTIONS(preflight) anddo_POST(response). Missing preflight handler = browser blocks the request before it reaches your code. - Timeout for LLM calls — Gemini can take 5-15 seconds. Set frontend timeout to at least 15s (
AbortSignal.timeout(15000)). Set the serverless function timeout higher (Vercel Pro: 60s, Hobby: 10s). urllib.errornot imported — Useimport urllib.request, urllib.error(not justimport urllib.request). TheHTTPErrorclass lives inurllib.error.- 429 on brand-new key — If the key was created from a project that already exhausted its quota (same billing project), a new key won't help. Create a new project in Google Cloud Console, enable the Generative Language API, then generate a key.
maxOutputTokensignored on some models — Older models may have different token limits. Always check the model's docs for the correctmaxOutputTokensrange.- System instructions ignored on non-proxy setup — If calling Gemini directly from the browser (not recommended — exposes your API key),
systemInstructionmay be silently dropped. Always proxy through your backend.
See also
static-site-deploymentskill — Hybrid deployment (static + serverless) on Vercel- Gemini API docs: https://ai.google.dev/gemini-api/docs
- Google AI Studio: https://aistudio.google.com/