Building a Python API chatbot is a practical way to learn how a local program can send a message to a language model and display the answer. In this tutorial, you will create a complete terminal chatbot with the official OpenAI Python library, secure environment variables, conversation memory, model configuration, and useful error handling.
This project is different from a rule-based bot. A simple Python chatbot normally compares the user’s message with predefined words and chooses a stored reply. The chatbot in this guide sends natural-language requests to an external API, so it can respond to a much wider range of questions.
You only need basic Python knowledge to follow the steps. Familiarity with Python lists, dictionaries, functions, and loops will make the complete code easier to understand.
How a Python API chatbot works
The program follows a simple cycle:
- Read a message typed by the user.
- Send the message to the OpenAI Responses API.
- Receive the generated text.
- Print the answer in the terminal.
- Keep a reference to the previous response so the next request has conversational context.
The API is a remote service. Your Python script is the client: it prepares the request, authenticates with an API key, waits for the result, and processes the returned object. The official OpenAI developer quickstart uses the same client pattern shown in this guide.
Requirements
Before starting, confirm that you have:
- Python installed on your computer.
- A code editor and terminal.
- An OpenAI API account with an API key.
- API billing or credits available for requests.
- Basic knowledge of running Python files.
For a clean project setup, create a dedicated Python virtual environment. It isolates the libraries used by this chatbot from packages installed in other projects.
Step 1: Create the project folder
Create a folder named python-chatbot, open a terminal inside it, and create a virtual environment:
python -m venv .venvActivate it on Windows PowerShell:
.venv\Scripts\Activate.ps1On macOS or Linux, use:
source .venv/bin/activateAfter activation, your terminal normally displays (.venv) before the command prompt.
Step 2: Install the libraries
Install the official OpenAI package and python-dotenv:
python -m pip install openai python-dotenvThe openai package provides the API client. The python-dotenv package loads variables from a local .env file during development. Using python -m pip also helps ensure that the package is installed in the Python environment currently running the project.
Step 3: Store the API key securely
Create a file named .env in the project folder:
OPENAI_API_KEY=replace_with_your_real_key
OPENAI_MODEL=gpt-5.6-lunaThe official SDK automatically reads OPENAI_API_KEY from the environment. The second variable keeps the model outside the source code, which makes future model changes easier. Check the current options in the official OpenAI model catalog, because availability can vary by account and model options change over time.
Never publish your API key. Do not paste it directly into a Python file, screenshot, tutorial, public repository, or support message. Revoke a key immediately if it becomes exposed.
Create a .gitignore file so Git does not track secrets or the virtual environment:
.env
.venv/
__pycache__/Step 4: Send the first request
Create a file named chatbot.py and start with a single request:
import os
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
model = os.getenv("OPENAI_MODEL", "gpt-5.6-luna")
client = OpenAI()
response = client.responses.create(
model=model,
instructions="You are a helpful Python tutor. Keep answers clear and practical.",
input="Explain what a Python function is in three sentences.",
)
print(response.output_text)The OpenAI() client reads the API key from the environment. The responses.create() method sends the request. The instructions parameter defines the bot’s behavior, while input contains the user message. The generated answer is available through response.output_text.
The official text generation guide documents the Responses API, instructions, message roles, and text output.
Step 5: Add a conversation loop
A chatbot needs to keep asking for messages until the user decides to stop. A Python while loop is a natural fit for this behavior.
The Responses API can continue a conversation with previous_response_id. Instead of manually resending every earlier message, the program stores the ID returned by the last request and includes it in the next one.
previous_response_id = None
while True:
user_message = input("You: ").strip()
if user_message.lower() in {"exit", "quit"}:
print("Chat ended.")
break
if not user_message:
continue
request = {
"model": model,
"instructions": "You are a helpful Python tutor. Keep answers clear and practical.",
"input": user_message,
}
if previous_response_id is not None:
request["previous_response_id"] = previous_response_id
response = client.responses.create(**request)
previous_response_id = response.id
print("Bot:", response.output_text)Notice that the instructions are included on every request. When conversation state is continued through previous_response_id, instructions from an earlier call are not automatically reused for the new response. Repeating them keeps the chatbot’s purpose consistent.
Step 6: Handle API errors
Network calls can fail. The key may be invalid, the connection may be unavailable, the selected model may not be accessible, or the account may reach a rate limit. A real project should not close with an unexplained traceback.
Python’s exception system lets you present useful messages. The technique is covered in more detail in the Python try and except guide.
import openai
try:
response = client.responses.create(**request)
except openai.AuthenticationError:
print("Authentication failed. Check your API key.")
except openai.RateLimitError:
print("The request reached a rate or usage limit. Try again later.")
except openai.APIConnectionError:
print("Could not connect to the API. Check your internet connection.")
except openai.APIError as error:
print(f"The API returned an error: {error}")The official API error guide lists the Python error classes and explains how API, connection, authentication, timeout, and rate-limit failures can be handled programmatically.
Complete Python API chatbot
The following version combines secure configuration, reusable functions, conversation state, input validation, and error handling:
import os
import openai
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
MODEL = os.getenv("OPENAI_MODEL", "gpt-5.6-luna")
INSTRUCTIONS = (
"You are a helpful Python tutor. "
"Explain concepts in simple English, use short examples, "
"and say when you are uncertain."
)
client = OpenAI()
def create_response(message, previous_response_id=None):
request = {
"model": MODEL,
"instructions": INSTRUCTIONS,
"input": message,
}
if previous_response_id is not None:
request["previous_response_id"] = previous_response_id
return client.responses.create(**request)
def main():
previous_response_id = None
print("Python API Chatbot")
print("Type 'exit' or 'quit' to stop.")
while True:
user_message = input("You: ").strip()
if user_message.lower() in {"exit", "quit"}:
print("Chat ended.")
break
if not user_message:
print("Please type a message.")
continue
try:
response = create_response(
message=user_message,
previous_response_id=previous_response_id,
)
except openai.AuthenticationError:
print("Authentication failed. Check OPENAI_API_KEY.")
break
except openai.RateLimitError:
print("A rate or usage limit was reached. Try again later.")
continue
except openai.APIConnectionError:
print("Connection failed. Check your network and try again.")
continue
except openai.APIError as error:
print(f"API error: {error}")
continue
previous_response_id = response.id
print("Bot:", response.output_text)
if __name__ == "__main__":
main()Run the project with:
python chatbot.pyUnderstanding the complete code
Configuration
load_dotenv() loads the local variables. MODEL reads the configured model and provides a fallback. Keeping configuration separate from the main logic makes the script easier to deploy in different environments.
Reusable API function
create_response() receives a message and an optional previous response ID. It builds a dictionary of arguments and expands that dictionary with **request. This approach keeps API-specific code out of the terminal loop.
Conversation state
After every successful call, the program stores response.id. The next call sends it as previous_response_id, allowing follow-up questions to use the prior conversation as context.
Input validation
strip() removes unnecessary spaces. Empty messages are rejected, and a set checks the exit commands efficiently. This is a small but useful example of defensive programming.
How to customize the chatbot
The easiest customization is changing INSTRUCTIONS. You can define a role, audience, response format, tone, and limitations. For example:
INSTRUCTIONS = (
"You are a travel planning assistant. "
"Ask for the destination, dates, budget, and interests. "
"Return practical suggestions in a numbered plan."
)Other extensions include saving conversations in SQLite, adding a graphical interface, connecting the chatbot to a website, or exposing it through a web API. Before building a larger application, organize the code with clear functions and understand Python variable scope so configuration and conversation state do not become difficult to manage.
You can also connect the same API logic to a messaging platform. The Telegram bot with Python guide explains how commands, incoming messages, and deployment work in that environment.
Security and cost best practices
- Keep the API key on the server, never in browser-side JavaScript.
- Add
.envto.gitignorebefore the first commit. - Use project-specific keys when possible and revoke unused keys.
- Validate and limit user input in public applications.
- Set account usage limits and monitor API activity.
- Choose a model appropriate for the task instead of automatically using the largest option.
- Log errors without recording secrets or sensitive user content.
A subscription to a consumer chat product and API usage are separate services. Review the API dashboard and current pricing before deploying an application that other people can use.
Common problems
The API key is missing
Confirm that the file is named exactly .env, that it is in the project folder, and that load_dotenv() runs before OpenAI() is created.
The model is unavailable
Open the model catalog, choose a model available to your project, and update OPENAI_MODEL. Keeping the model in the environment avoids editing the Python source.
The bot forgets its previous answer
Verify that you store response.id after a successful request and send it as previous_response_id on the next request. Resetting that variable starts a new conversation.
The program displays only a traceback
Place the API request inside try and handle the library’s error classes. During development, log enough information to diagnose the problem, but never log the API key.
Conclusion
You now have a complete Python API chatbot that reads terminal input, calls the Responses API, maintains conversational context, protects credentials, and handles common failures. The same foundation can support a tutoring assistant, customer-service prototype, internal documentation helper, or another project that works with natural language.
The most important production improvements are secure key storage, input limits, monitoring, clear error messages, and a model chosen for the actual workload. Keep the API-specific logic in a small function so the terminal interface can later be replaced by a web page, desktop window, or messaging integration.






