Build a Chatbot with the Gemini API
Build a terminal chatbot with multi-turn memory using Google's Gemini API.
Introduction
30 Second Summary
Every time you chat with ChatGPT, a customer support bot, or Google's Gemini app, there's a simple pattern running under the hood. Your messages get sent to an AI model, the model remembers what you've said, and responses stream back word by word.
In this project, you will build your own terminal chatbot powered by Google's Gemini API. You'll send messages, receive streaming responses, and carry on multi-turn conversations with a custom AI personality.
What You'll Build
A Python script that connects to the Gemini API, maintains conversation history, and streams responses in real-time through your terminal.
By the end of this project, you'll have:
- A working Gemini API connection that generates AI responses from a simple Python script.
- A multi-turn chat session that remembers everything you've said in the conversation.
- Streaming responses that display text token-by-token in your terminal, just like ChatGPT.
- Secret Mission: Add /save and /load commands to export and restore your conversations as JSON files.
Are there any prerequisites?
Basic familiarity with Python (variables, functions, loops) is all you need. No prior AI or API experience required.
Set Up Your Gemini API Key and Install the SDK
To build a chatbot powered by Gemini, you need two things: an API key that authenticates your requests, and a Python SDK that handles communication with the API.
In this step, you'll grab a free API key from Google AI Studio, configure your terminal to use it automatically, and install the SDK so your Python code can talk to Gemini.
In this step, get ready to:
- Create a free Gemini API key from Google AI Studio.
- Set the API key as an environment variable so Python can find it.
- Install the Google GenAI SDK and verify your setup.
Get your Gemini API key from Google AI Studio
Google AI Studio is a free web tool where you can create API keys for the Gemini API. You only need a Google account to get started.
- Open your browser and go to aistudio.google.com/apikey.
- Sign in with your Google account if prompted.
- Click Create API key.
- If prompted to select a project, choose the default project that Google AI Studio created for you.
- Copy the API key that appears. Keep this tab open until you complete the next substep.
What is an API key?
An API key is a unique string that identifies your requests to a service. Think of it like a library card. The library (Gemini) uses it to know who is borrowing books (making requests) and to enforce limits.
Keep your API key secret
Treat your API key like a password. Never paste it directly into code you share publicly. In the next substep, you'll store it safely as an environment variable.
Set your API key as an environment variable
Storing your API key as an environment variable keeps it out of your code. The Google GenAI SDK automatically reads a variable called GEMINI_API_KEY when it connects to the API.
Windows
- Open PowerShell by pressing the Windows key, typing PowerShell, and pressing Enter.
- Save your API key permanently by running this command:
setx GEMINI_API_KEY "your-api-key-here"
- Replace your-api-key-here with the API key you copied from Google AI Studio. Keep the quotes around it.
- Close PowerShell completely and reopen it. The setx command requires a terminal restart to take effect.
Why do I need to restart my terminal?
The setx command writes the variable to the Windows registry, but your current terminal session still has the old environment loaded. Closing and reopening the terminal picks up the new variable.
macOS
- Open Terminal by pressing Cmd+Space, typing Terminal, and pressing Enter.
- Save your API key permanently by running this command:
echo 'export GEMINI_API_KEY="your-api-key-here"' >> ~/.zshrc
- Replace your-api-key-here with the API key you copied from Google AI Studio. Keep the quotes around it.
- Load the new variable into your current session by running:
source ~/.zshrc
Why two commands?
The first command appends your key to .zshrc, which is a file that runs every time you open a new terminal. The second command reloads that file so your current session picks up the change immediately.
Not sure if it worked?
After restarting (Windows) or sourcing (macOS), verify the variable exists by running the appropriate command for your platform.
On Windows PowerShell, run echo $env:GEMINI_API_KEY. On macOS Terminal, run echo $GEMINI_API_KEY. You should see your API key printed back. If it's blank, double-check you restarted your terminal (Windows) or ran source ~/.zshrc (macOS).
Help me troubleshoot my GEMINI_API_KEY environment variable
Install the SDK and create your project
The google-genai package is Google's official Python SDK for the Gemini API. It handles all the network requests and data formatting so you can focus on building your chatbot.
- Install the Google GenAI SDK by running this command in your terminal:
pip install google-genai
What does this install?
This installs the google-genai package, which gives you a Python client for calling the Gemini API. It includes helpers for chat sessions, streaming, and more.
Seeing a permissions error?
If you see a permissions error, try running pip install --user google-genai instead. On macOS, you may need to use pip3 install google-genai if your system has both Python 2 and Python 3.
Help me fix my pip install error
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir gemini-chatbot
cd gemini-chatbot
- Open VS Code by pressing Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type VS Code and press Enter to open it.
- In VS Code, click File in the top menu bar.
- Click Open Folder.
- Navigate to your Desktop, select the gemini-chatbot folder, and click Open.
- In the Explorer sidebar on the left, hover over the folder name and click the New File icon (a page with a plus sign).
- Type chatbot.py and press Enter to create the file.
- Paste the following code into chatbot.py:
from google import genai
print("Setup complete!")
What does this code do?
The from google import genai line imports the Gemini SDK. If this import succeeds without errors, it confirms the package installed correctly.
The print("Setup complete!") line gives you a visible confirmation that everything is wired up.
- Save chatbot.py by pressing Cmd+S (macOS) or Ctrl+S (Windows).
- Open the integrated terminal in VS Code by pressing Ctrl+` (backtick key, both platforms).
- Run the script by entering this command:
python chatbot.py
You should see Setup complete! printed in the terminal.
Seeing a ModuleNotFoundError?
If you see ModuleNotFoundError: No module named 'google', it means pip installed the package for a different Python version than the one you're running.
Try running python3 chatbot.py instead. If that works, use python3 for all commands in this project.
Help me fix my ModuleNotFoundError
✔️ Awesome, I've got everything!
Great. Double check you've saved chatbot.py and you can see Setup complete! in your terminal output.
ⓧ I'd like to double check the full code
Here is the full chatbot.py file at this point:
from google import genai
print("Setup complete!")
Your environment is ready. Next up, you'll send your first message to the Gemini API and see it respond.
Send Your First Message to the API
Your environment is set up and your API key is ready. Now it's time to talk to an AI model.
In this step, you'll send a prompt to Google's Gemini 3.5 Flash model and print its response. Then you'll add a system instruction that shapes how the model responds, giving your chatbot a custom personality.
In this step, get ready to:
- Send a message to the Gemini API and print the response.
- Add a system instruction to shape the model's personality.
Send a message to the Gemini API
Right now chatbot.py just prints a test message. Let's replace it with real code that sends a prompt to the Gemini model and prints what it says back.
- In VS Code, open chatbot.py and replace all the existing code with the following:
from google import genai
# Create a client that connects to the Gemini API
client = genai.Client()
# Send a message to the model
response = client.models.generate_content(
model="gemini-3.5-flash",
contents="What is a variable?"
)
# Print the model's response
print(response.text)
What does this code do?
This code connects to the Gemini API and asks it a question.
- genai.Client() creates a client that automatically reads your GEMINI_API_KEY environment variable to authenticate.
- client.models.generate_content() sends a prompt to the specified model and returns the AI's response.
- response.text extracts the plain text from the model's response object.
- Save chatbot.py.
- Run your script by entering this command in your terminal:
python chatbot.py
You should see a paragraph or two explaining what a variable is. The exact wording will differ each time you run it because the model generates fresh responses.
Seeing an error about a missing key?
If you see an error mentioning a missing API key, your GEMINI_API_KEY environment variable is not set in this terminal session.
- Make sure you restarted your terminal after running the setx command in the previous step.
- On macOS/Linux, confirm the variable is set by running echo $GEMINI_API_KEY in your terminal. You should see your key printed.
Help me fix an API key error when running my Gemini chatbot script.
Add a system instruction to shape the response
The model gave you a generic answer. A system instruction is a behind-the-scenes directive that tells the model how to behave. It's how you give a chatbot its personality without the user ever seeing the instruction.
- In chatbot.py, replace all the existing code with the following updated version:
from google import genai
from google.genai import types
# Create a client that connects to the Gemini API
client = genai.Client()
# Configure the model's personality with a system instruction
config = types.GenerateContentConfig(
system_instruction="You are a friendly coding tutor who explains concepts with real-world analogies. Keep responses concise but helpful."
)
# Send a message with the personality config applied
response = client.models.generate_content(
model="gemini-3.5-flash",
contents="What is a variable?",
config=config
)
# Print the model's response
print(response.text)
What changed here?
You added two things to control how the model responds.
- from google.genai import types imports the configuration classes from the SDK.
- types.GenerateContentConfig(system_instruction=...) creates a config object that carries your system instruction. This is the equivalent of OpenAI's "system" message.
- Passing config=config to generate_content() applies the personality to this request.
- Save chatbot.py and run it again:
python chatbot.py
This time, the response should use a real-world analogy to explain what a variable is. The system instruction steered the model toward a friendlier, more illustrative answer.
Response looks the same as before?
If the response doesn't seem to use analogies or feels unchanged, try these checks.
- Make sure you saved the file after editing. Unsaved changes won't affect the script that runs.
- Confirm you passed config=config as the third argument inside generate_content(). Without it, the system instruction is ignored.
Help me figure out why my system instruction isn't changing the model's response.
✔️ Awesome, I've got everything!
Great. Double check you've saved chatbot.py before moving on.
ⓧ I'd like to double check the full code
Here's the complete chatbot.py at this point:
from google import genai
from google.genai import types
client = genai.Client()
config = types.GenerateContentConfig(
system_instruction="You are a friendly coding tutor who explains concepts with real-world analogies. Keep responses concise but helpful."
)
response = client.models.generate_content(
model="gemini-3.5-flash",
contents="What is a variable?",
config=config
)
print(response.text)
You've made your first API call and seen how a system instruction can completely change the model's tone. Next up, you'll turn this into a real conversation that remembers what you've said.
Build the Multi-Turn Conversation
Right now your chatbot handles a single question and exits. That is like having a phone that hangs up after one sentence.
In this step, you will upgrade the script into a proper conversation loop. The chatbot will remember everything you have said and respond in context, just like ChatGPT or Google's Gemini app.
In this step, get ready to:
- Create a chat session that automatically manages conversation history.
- Build an interactive loop that sends messages and prints responses.
- Add exit commands and empty input handling.
Create a chat session
Instead of calling generate_content() for a one-off answer, the SDK provides a chat session object that tracks every message automatically. Each time you send a message, the session appends it to the history and sends the full context to the model.
- In chatbot.py, find these lines at the bottom of your file:
response = client.models.generate_content(
model="gemini-3.5-flash",
contents="What is a variable?",
config=config
)
print(response.text)
- Delete those lines and replace them with this:
chat = client.chats.create(model="gemini-3.5-flash", config=config)
What does this line do?
client.chats.create() returns a stateful chat object tied to the gemini-3.5-flash model. It passes your config (which includes your system instruction) so the personality carries into every response.
Behind the scenes, the SDK maintains a list of messages. Every time you call chat.send_message(), your new message is appended, the full history is sent to the API, and the model's reply is appended too.
Build the conversation loop
Now you need a loop that continuously accepts input, sends it to the chat session, and prints the model's reply. You will also add a way to quit and skip empty inputs so the loop behaves gracefully.
- Below the chat = client.chats.create(...) line you just added, paste the following code:
def main():
"""Run the chatbot loop."""
print("Chatbot ready! Type 'quit' to exit.")
print("-" * 40)
while True:
user_input = input("\nYou: ").strip()
if not user_input:
continue
if user_input.lower() in ("quit", "exit"):
print("Goodbye!")
break
response = chat.send_message(user_input)
print(f"\nAssistant: {response.text}")
if __name__ == "__main__":
main()
What does this code do?
- while True creates an infinite loop that keeps the conversation going until the user explicitly quits.
- input("\nYou: ").strip() prompts the user for text and removes leading/trailing whitespace.
- if not user_input: continue skips empty inputs so the model does not receive a blank message.
- if user_input.lower() in ("quit", "exit") checks whether the user wants to leave and breaks out of the loop.
- chat.send_message(user_input) sends the message through the chat session, which automatically appends it to the conversation history.
- if __name__ == "__main__": main() ensures the chatbot only runs when you execute the script directly.
- Save chatbot.py.
Seeing a syntax error?
Make sure you deleted the entire old response = client.models.generate_content(...) block and the print(response.text) line. If any leftover code sits between config and chat = client.chats.create(...), Python will throw an error.
help me fix a syntax error in my Gemini chatbot script
Test multi-turn conversation
Time to prove the chatbot actually remembers what you say.
- Run your chatbot by executing this command in your terminal:
python chatbot.py
- When you see Chatbot ready! Type 'quit' to exit., type a message like My name is Alex and press Enter.
- After the assistant replies, type What's my name? and press Enter.
- The assistant should recall your name from the earlier message.
- Press Enter on an empty line to confirm empty inputs are skipped.
- Type quit to exit the loop.
Why does the model remember my name?
The chat session automatically sends the full conversation history with every request. When you asked "What's my name?", the model received both your earlier message and its own reply as context. This is exactly how apps like ChatGPT maintain conversational continuity.
Model not remembering earlier messages?
Make sure you are using chat.send_message() and not client.models.generate_content(). Only the chat session tracks history across turns. If you accidentally left the old code, each message is treated independently.
help me debug why my Gemini chatbot does not remember previous messages
✔️ Awesome, I've got everything!
Great. Double check you have saved chatbot.py and that your chatbot exits cleanly when you type quit.
ⓧ I'd like to double check the full code
from google import genai
from google.genai import types
client = genai.Client()
config = types.GenerateContentConfig(
system_instruction="You are a friendly coding tutor who explains concepts with real-world analogies. Keep responses concise but helpful."
)
chat = client.chats.create(model="gemini-3.5-flash", config=config)
def main():
"""Run the chatbot loop."""
print("Chatbot ready! Type 'quit' to exit.")
print("-" * 40)
while True:
user_input = input("\nYou: ").strip()
if not user_input:
continue
if user_input.lower() in ("quit", "exit"):
print("Goodbye!")
break
response = chat.send_message(user_input)
print(f"\nAssistant: {response.text}")
if __name__ == "__main__":
main()
Your chatbot now holds a real conversation. Next up, you will make the responses stream in token-by-token so the chatbot feels snappy and responsive.
Add Streaming Responses
Your chatbot can hold a multi-turn conversation with Gemini. Right now, you send a message and wait in silence until the entire response appears at once.
Real chat apps like ChatGPT show text appearing word-by-word as it's generated. This is called streaming. In this step, you'll swap out the blocking call for a streaming one that prints each token the moment it arrives.
In this step, get ready to:
- Create a streaming response function that prints text token-by-token.
- Update the chat loop to use streaming instead of the blocking call.
- Test multi-turn conversation memory with streaming enabled.
Create the streaming response function
The Gemini SDK offers a streaming variant of send_message() called send_message_stream(). Instead of waiting for the full response, it returns chunks as they're generated.
- In chatbot.py, find the line that says chat = client.chats.create(model="gemini-3.5-flash", config=config).
- Add the following function below that line (leave a blank line between them) by copying this code:
def stream_response(user_message):
"""Send a message and stream the response."""
full_response = ""
for chunk in chat.send_message_stream(user_message):
if chunk.text:
print(chunk.text, end="", flush=True)
full_response += chunk.text
print()
return full_response
What does this code do?
chat.send_message_stream(user_message) sends your message to Gemini and returns an iterable of chunks instead of one complete response.
Each chunk has a .text property that may be None for non-text chunks, so the if chunk.text: check keeps things safe. The end="" prevents a newline after each chunk, and flush=True forces Python to display the text immediately rather than buffering it.
The final print() adds a newline after the full response finishes streaming.
Update the chat loop to use streaming
Now you need to replace the blocking send_message() call in your main loop with the new streaming function.
- Inside the main() function, scroll down to the bottom of the while True loop.
- Find these two lines:
response = chat.send_message(user_input)
print(f"\nAssistant: {response.text}")
- Replace both lines with these two lines:
print("\nAssistant: ", end="")
stream_response(user_input)
Why print the label separately?
The print("\nAssistant: ", end="") line prints the "Assistant: " label without a newline at the end. Then stream_response() streams the actual response text directly after it. The result looks seamless to the user.
- Save chatbot.py.
Seeing a NameError for stream_response?
Make sure the stream_response() function is defined above the main() function. Python reads top-to-bottom, so functions must be defined before they're called.
help me fix a NameError in my chatbot
Test your streaming chatbot
Time to see streaming in action and confirm that conversation memory still works across multiple turns.
- Run your chatbot by running this command:
python chatbot.py
- Type a message like My name is Alex and press Enter.
- Watch the response appear token-by-token instead of all at once.
- Now type What's my name? and press Enter.
The model should respond with "Alex" (or whatever name you gave). This proves the chat session remembers earlier messages across turns.
- Type quit to exit when you're done testing.
Why does memory work without extra code?
The chat session you created with client.chats.create() is stateful. Every time you call send_message_stream(), the SDK automatically appends both your message and the model's response to the conversation history. You don't need to manage a messages array yourself.
Not seeing token-by-token output?
Check that your print() call inside the loop includes both end="" and flush=True. Without flush=True, Python buffers the output and you'll see it all at once.
If the model responds with very short answers, streaming may be too fast to notice. Try asking a longer question like "Explain how the internet works."
help me debug streaming in my Gemini chatbot
✔️ Awesome, I've got everything!
Great. Double check you've saved chatbot.py and confirmed streaming works with multi-turn memory.
ⓧ I'd like to double check the full code
Here's the complete chatbot.py at this point:
from google import genai
from google.genai import types
client = genai.Client()
config = types.GenerateContentConfig(
system_instruction="You are a friendly coding tutor who explains concepts with real-world analogies. Keep responses concise but helpful."
)
chat = client.chats.create(model="gemini-3.5-flash", config=config)
def stream_response(user_message):
"""Send a message and stream the response."""
full_response = ""
for chunk in chat.send_message_stream(user_message):
if chunk.text:
print(chunk.text, end="", flush=True)
full_response += chunk.text
print()
return full_response
def main():
"""Run the chatbot loop."""
print("Chatbot ready! Type 'quit' to exit.")
print("-" * 40)
while True:
user_input = input("\nYou: ").strip()
if not user_input:
continue
if user_input.lower() in ("quit", "exit"):
print("Goodbye!")
break
print("\nAssistant: ", end="")
stream_response(user_input)
if __name__ == "__main__":
main()
Secret mission
Save and Load Conversations
Your chatbot works great in a single session, but what happens when you close the terminal? Everything disappears. In this secret mission, you'll add /save and /load commands that export your conversation to a JSON file and restore it later, so you can pick up right where you left off.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely locally and uses the free Gemini API tier, so there are no ongoing costs.
Resources you used:
- Your project folder containing chatbot.py and the conversations/ directory.
- The google-genai Python package.
- The GEMINI_API_KEY environment variable.
- Your Gemini API key in Google AI Studio.
Keep everything running
No action needed. Choose this if you're still actively building or experimenting with your chatbot.
- Your project files, API key, and environment variable all stay in place.
- You can run your chatbot again any time with python chatbot.py.
- The Gemini free tier has no ongoing charges, so leaving everything as-is costs nothing.
Pause - I'll come back to this later
Shut down any running processes to free up your terminal. Your files and configuration stay in place for when you return.
- If your chatbot is still running, press Ctrl+C to stop it.
- Your chatbot.py file, saved conversations, environment variable, and API key all remain ready for next time.
- No costs accrue while the chatbot is not running.
Delete - I don't want to use this again
Remove all project resources and start fresh. Follow each item below to fully clean up.
- Delete your project folder from the Desktop. Open your terminal and run:
macOS
cd ~/Desktop
rm chatbot.py
rm -rf conversations/
Windows
cd ~\Desktop
Remove-Item chatbot.py
Remove-Item -Recurse -Force conversations/
- Uninstall the google-genai package by running:
pip uninstall google-genai
- Enter y when prompted to confirm.
- Remove the GEMINI_API_KEY environment variable:
macOS
If you added the variable to your shell config file (~/.zshrc or ~/.bash_profile), open that file and delete the line containing GEMINI_API_KEY. Then run:
unset GEMINI_API_KEY
Windows
Open PowerShell and run this command to remove the persistent environment variable you set with setx:
reg delete "HKCU\Environment" /v GEMINI_API_KEY /f
Restart your terminal for the change to take effect.
- Delete your API key from Google AI Studio. Go to ai.google.dev, sign in, navigate to the API keys page, find the key you created for this project, and click Delete.
Nice Work!
Nice Work!
You just built a fully working AI chatbot powered by Google's Gemini 3.5 Flash model. It streams responses in real time, remembers your entire conversation, and has a custom personality you defined from scratch.
Here's what you learned:
- Connected to the Gemini API and made your first content generation request using client.models.generate_content().
- Created a custom chatbot personality using system instructions passed through GenerateContentConfig.
- Built a multi-turn conversation using Gemini's chat session with automatic history management.
- Added streaming responses for real-time token-by-token output using send_message_stream().
- Added /save and /load commands to persist and restore conversations as JSON files.
Ready to quiz yourself?