Google AI Studio hands out real, working API keys for free — no credit card, no trial countdown, no "contact sales." You can have one wired into your code in about five minutes. This guide is the honest version: how to get the key, how to prove it works, and the two limits that actually matter before you build anything on it.
Step 1 — Sign in to Google AI Studio
Go to aistudio.google.com and sign in with any Google account. A personal Gmail is fine — you do not need a Google Cloud project, a billing account, or Workspace. If you have used Google AI Studio before, you are already set up.
The first time in, it asks you to accept the terms. Read the data clause — we come back to it at the bottom, because on the free tier it matters.
Step 2 — Create the API key
Click Get API key in the left sidebar, then Create API key. Pick "Create API key in new project" if you have no project yet — it makes one for you. You get a string that starts with AIza…. Copy it now; treat it like a password.
- Never paste the key into client-side JavaScript or a public repo — anyone who sees it can spend your quota.
- Store it in an environment variable, not in your source.
GEMINI_API_KEYis the convention. - If you leak one, delete it from the same screen and make a new one. Rotating takes ten seconds.
Step 3 — Prove it works
Before you build anything, confirm the key is live with one request. Drop your key in and run this:
curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=$GEMINI_API_KEY" \
-H 'Content-Type: application/json' \
-X POST \
-d '{
"contents": [{ "parts": [{ "text": "Say hello in five words." }] }]
}'
You should get JSON back with a candidates array containing the reply. If you would rather use code, the Python SDK is two lines once the key is in your environment:
# pip install google-genai
from google import genai
client = genai.Client() # reads GEMINI_API_KEY from the environment
resp = client.models.generate_content(
model="gemini-2.0-flash",
contents="Say hello in five words.",
)
print(resp.text)
What the free tier actually gives you
This is the part the marketing pages skip. Free-tier limits are per-model and Google adjusts them, but here is the shape of it at the time we last tested. Treat these as "enough to build and demo," not "enough to run a product."
| Model | Requests/min | Requests/day | Good for |
|---|---|---|---|
| gemini-2.0-flash | 15 RPM | 1,500 RPD | Most apps, chat, fast responses |
| gemini-2.5-flash | 10 RPM | 250 RPD | Harder reasoning, tighter daily cap |
| gemini-2.5-pro | 5 RPM | 100 RPD | Best quality, spend it carefully |
The requests-per-day cap is the one that bites. A chatbot with a handful of users will blow through 100 Pro calls before lunch. Flash is the workhorse — build on it, and reserve Pro for the calls that genuinely need it.
The catch: your prompts train the model
The catch: on the free tier, Google uses your prompts and the model's responses to improve its products. Human reviewers may read them. Do not send anything you would not want a stranger to see — customer data, secrets, anything under NDA. The moment you enable billing (paid tier), that stops: paid API traffic is not used for training. If you are only ever going to test with throwaway prompts, free is fine. If real data is involved, switch to paid before you ship.
Troubleshooting
- 400 API_KEY_INVALID — the key was copied with a trailing space, or you are pointing at the wrong project. Regenerate and paste carefully.
- 429 RESOURCE_EXHAUSTED — you hit the per-minute or per-day cap. Back off and retry, or drop to a lighter model. This is not a billing error; it is the free limit doing its job.
- 403 with "billing" in the message — you called a model or feature that is paid-only. Check the model name; free keys cannot reach every model.
- Empty
candidates— usually a safety filter blocked the response. Rephrase, or inspect thepromptFeedbackfield to see which category tripped.
Where to go next
You now have a free key and a request that works. Two sensible next moves: wire the key into an environment variable so it never touches your source, and put a small retry-with-backoff around your calls so a stray 429 does not crash your app. Everything else — streaming, system prompts, function calling — builds on top of the exact request you just ran.