Environment Variables & Secrets: Never Hardcode API Keys
After this lesson, you will be able to:
- Understand what environment variables are and why they exist
- Read environment variables in Python using os.environ
- Use .env files with python-dotenv for local development
- Structure a project so secrets are never in your code
Before You Start
#What Are Environment Variables?
#Reading Environment Variables in Python
import os
# Method 1: os.environ["KEY"] — raises KeyError if missing
api_key = os.environ["ANTHROPIC_API_KEY"]
# Method 2: os.environ.get("KEY") — returns None if missing (safer)
api_key = os.environ.get("ANTHROPIC_API_KEY")
if api_key is None:
raise ValueError("ANTHROPIC_API_KEY not set. Check your .env file.")
# Method 3: os.environ.get("KEY", "default") — returns default if missing
debug = os.environ.get("DEBUG", "false")
port = int(os.environ.get("PORT", "8000")).get() with a clear error message. Crashing with KeyError: 'ANTHROPIC_API_KEY' tells the user nothing. Crashing with ValueError("ANTHROPIC_API_KEY not set. Check your .env file.") tells them exactly how to fix it.#Setting Environment Variables
#On macOS/Linux (terminal):
# Temporary — only for this terminal session
export ANTHROPIC_API_KEY=sk-your-key-here
# Verify it's set
echo $ANTHROPIC_API_KEY
# Now run your Python script — it will see the variable
python my_script.py
#On Windows (Command Prompt):
set ANTHROPIC_API_KEY=sk-your-key-here
python my_script.py
#On Windows (PowerShell):
$env:ANTHROPIC_API_KEY = "sk-your-key-here"
python my_script.py
#The .env File: Easier for Development
.env file lets you put all secrets in one place for your project.pip install python-dotenv
.env file in your project root# .env — NEVER commit this to Git
WEATHER_API_KEY=your-key-here
EMAIL_PASSWORD=your-password-here
DATABASE_URL=postgresql://user:password@localhost/mydb
DEBUG=true
from dotenv import load_dotenv
import os
# Load variables from .env into os.environ
load_dotenv()
# Now read them normally
api_key = os.environ.get("ANTHROPIC_API_KEY")
db_url = os.environ.get("DATABASE_URL")load_dotenv() reads the .env file and loads every variable into os.environ. Your code works the same way whether variables come from a .env file or the system environment.#The .gitignore File: Keep Secrets Out of Git
.env to your .gitignore so you never accidentally commit it:# .gitignore
.env
.env.local
.env.production
__pycache__/
*.pyc
.venv/
Provide a .env.example instead
# .env.example — commit this, it shows what variables are needed
ANTHROPIC_API_KEY=your-key-here
DATABASE_URL=postgresql://user:password@host/dbname
DEBUG=false
.env.example to .env, fill in their own keys, and everything works.#A Complete Project Structure
my-project/
├── .env ← secrets (never commit)
├── .env.example ← template (commit this)
├── .gitignore ← includes .env
├── main.py
├── requirements.txt
└── README.md
# main.py
from dotenv import load_dotenv
import os
import requests
def main():
load_dotenv() # Load .env if it exists, silently skip if not
api_key = os.environ.get("WEATHER_API_KEY")
if not api_key:
raise ValueError(
"WEATHER_API_KEY not set!\n"
"1. Copy .env.example to .env\n"
"2. Add your API key\n"
"3. Run again"
)
response = requests.get(
"https://api.example.com/forecast",
params={"key": api_key, "city": "Hyderabad"},
)
# ...
if __name__ == "__main__":
main()#Type-Safe Config with Pydantic (Advanced)
For larger projects, use Pydantic to validate all config at startup:
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
anthropic_api_key: str
database_url: str
debug: bool = False
port: int = 8000
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")
# Raises a clear error if any required variable is missing
settings = Settings()
print(settings.port) # 8000port becomes an int, debug becomes a bool, missing required fields raise a clean error with the variable name.Why use os.environ.get('KEY') instead of os.environ['KEY']?
Key Takeaways
- Environment variables keep secrets out of your code — never hardcode API keys
- os.environ.get('KEY') reads a variable, returning None if missing
- python-dotenv + .env file is the standard for local development
- Always add .env to .gitignore before your first commit — add .env.example instead