Learn Python Series (#34) - Working with APIs in 2026: What's Changed

Repository
What will I learn?
- You will learn what has changed in the Python API ecosystem since 2018;
- how
httpxcompares torequestsand when to use each; - modern authentication patterns (API keys, OAuth2, Bearer tokens);
- how to use environment variables for secure credential management;
- new error handling patterns and best practices for 2026.
Requirements
- A working modern computer running macOS, Windows or Ubuntu;
- An installed Python 3(.11+) distribution, such as (for example) the Anaconda Distribution;
- Familiarity with the basics from Learn Python Series #13-17 (recommended but not required);
- The ambition to learn Python programming.
Difficulty
- Intermediate
Curriculum (of the Learn Python Series):
- Learn Python Series - Intro
- Learn Python Series (#2) - Handling Strings Part 1
- Learn Python Series (#3) - Handling Strings Part 2
- Learn Python Series (#4) - Round-Up #1
- Learn Python Series (#5) - Handling Lists Part 1
- Learn Python Series (#6) - Handling Lists Part 2
- Learn Python Series (#7) - Handling Dictionaries
- Learn Python Series (#8) - Handling Tuples
- Learn Python Series (#9) - Using Import
- Learn Python Series (#10) - Matplotlib Part 1
- Learn Python Series (#11) - NumPy Part 1
- Learn Python Series (#12) - Handling Files
- Learn Python Series (#13) - Mini Project - Developing a Web Crawler Part 1
- Learn Python Series (#14) - Mini Project - Developing a Web Crawler Part 2
- Learn Python Series (#15) - Handling JSON
- Learn Python Series (#16) - Mini Project - Developing a Web Crawler Part 3
- Learn Python Series (#17) - Roundup #2 - Combining and analyzing any-to-any multi-currency historical data
- Learn Python Series (#18) - PyMongo Part 1
- Learn Python Series (#19) - PyMongo Part 2
- Learn Python Series (#20) - PyMongo Part 3
- Learn Python Series (#21) - Handling Dates and Time Part 1
- Learn Python Series (#22) - Handling Dates and Time Part 2
- Learn Python Series (#23) - Handling Regular Expressions Part 1
- Learn Python Series (#24) - Handling Regular Expressions Part 2
- Learn Python Series (#25) - Handling Regular Expressions Part 3
- Learn Python Series (#26) - pipenv & Visual Studio Code
- Learn Python Series (#27) - Handling Strings Part 3 (F-Strings)
- Learn Python Series (#28) - Using Pickle and Shelve
- Learn Python Series (#29) - Handling CSV
- Learn Python Series (#30) - Data Science Part 1 - Pandas
- Learn Python Series (#31) - Data Science Part 2 - Pandas
- Learn Python Series (#32) - Data Science Part 3 - Pandas
- Learn Python Series (#33) - Data Science Part 4 - Pandas
Learn Python Series (#34) - Working with APIs in 2026: What's Changed
Welcome back to the Learn Python Series! It's been a while since episodes #13-17, where we built a web crawler using the requests library and fetched data from the CoinMarketCap API. If you haven't read those episodes, I encourage you to check them out - the fundamentals we covered there still apply today.
But it's now 2026, and the Python ecosystem has evolved. In this episode, we'll focus on what's new and what's changed - not rehashing the basics, but building on them.
Quick refresher: What we covered in #13-17
Back in 2018, we learned:
- Installing and using the
requestslibrary (#13) - Making GET requests and parsing HTML with BeautifulSoup
- Working with JSON responses (#15)
- Fetching cryptocurrency data from APIs (#16, #17)
Those fundamentals haven't changed. What HAS changed is the tooling and best practices around them.
What's new: httpx - The modern alternative
While requests is still perfectly valid, httpx has emerged as a modern alternative that offers both sync AND async support:
pip install httpx
import httpx
# Synchronous (just like requests)
response = httpx.get("https://api.coingecko.com/api/v3/ping")
print(response.json())
# The API is nearly identical to requests
response = httpx.get(
"https://api.example.com/data",
params={"key": "value"},
headers={"Accept": "application/json"},
timeout=10.0
)
When to use which:
requests: Battle-tested, huge ecosystem, synchronous onlyhttpx: Modern, async support built-in, HTTP/2 support
Nota bene: If you're doing async programming (which we'll cover in episodes #39-40), httpx is the clear choice. For simple synchronous scripts, either works fine.
Modern authentication patterns
In 2018, we mostly dealt with simple API keys in URLs. Today's APIs often use more sophisticated auth:
Bearer tokens (JWT)
import httpx
token = "your_jwt_token_here"
response = httpx.get(
"https://api.example.com/protected",
headers={"Authorization": f"Bearer {token}"}
)
OAuth2 with httpx
import httpx
# OAuth2 client credentials flow
token_response = httpx.post(
"https://auth.example.com/oauth/token",
data={
"grant_type": "client_credentials",
"client_id": "your_client_id",
"client_secret": "your_client_secret"
}
)
access_token = token_response.json()["access_token"]
# Use the token
client = httpx.Client(
headers={"Authorization": f"Bearer {access_token}"}
)
response = client.get("https://api.example.com/data")
Secure credential management
Never hardcode credentials. This was true in 2018, but I didn't emphasize it enough. Here's the proper way:
import os
from dotenv import load_dotenv
# Load from .env file
load_dotenv()
API_KEY = os.getenv("API_KEY")
API_SECRET = os.getenv("API_SECRET")
if not API_KEY:
raise ValueError("API_KEY environment variable not set")
Your .env file (never commit this!):
API_KEY=your_actual_key_here
API_SECRET=your_actual_secret_here
Your .gitignore:
.env
Improved error handling patterns
In #13-17, our error handling was basic. Here's the modern approach:
import httpx
from httpx import HTTPStatusError, RequestError, TimeoutException
def fetch_with_resilience(url: str, max_retries: int = 3) -> dict:
"""Fetch data with proper error handling and retries."""
for attempt in range(max_retries):
try:
with httpx.Client(timeout=10.0) as client:
response = client.get(url)
response.raise_for_status()
return response.json()
except TimeoutException:
print(f"Timeout on attempt {attempt + 1}")
if attempt == max_retries - 1:
raise
except HTTPStatusError as e:
if e.response.status_code == 429: # Rate limited
retry_after = int(e.response.headers.get("Retry-After", 60))
print(f"Rate limited. Waiting {retry_after}s...")
import time
time.sleep(retry_after)
elif e.response.status_code >= 500:
print(f"Server error {e.response.status_code}, retrying...")
else:
raise # Client errors (4xx) shouldn't be retried
except RequestError as e:
print(f"Network error: {e}")
if attempt == max_retries - 1:
raise
raise Exception("Max retries exceeded")
Type hints for API responses
In episode #36, we'll dive deep into type hints. But here's a preview of how they improve API code:
from dataclasses import dataclass
from typing import Optional
import httpx
@dataclass
class CryptoPrice:
symbol: str
price_usd: float
change_24h: float
market_cap: Optional[float] = None
def get_crypto_price(coin_id: str) -> CryptoPrice:
"""Fetch and return typed cryptocurrency data."""
response = httpx.get(
"https://api.coingecko.com/api/v3/simple/price",
params={
"ids": coin_id,
"vs_currencies": "usd",
"include_24hr_change": "true",
"include_market_cap": "true"
}
)
response.raise_for_status()
data = response.json()[coin_id]
return CryptoPrice(
symbol=coin_id.upper(),
price_usd=data["usd"],
change_24h=data.get("usd_24h_change", 0),
market_cap=data.get("usd_market_cap")
)
# Usage - IDE now knows exactly what fields are available
price = get_crypto_price("bitcoin")
print(f"{price.symbol}: ${price.price_usd:,.2f} ({price.change_24h:+.2f}%)")
HTTP/2 support
One thing requests doesn't support but httpx does:
import httpx
# HTTP/2 is automatic when the server supports it
with httpx.Client(http2=True) as client:
response = client.get("https://www.google.com")
print(f"HTTP Version: {response.http_version}") # HTTP/2
Comparing 2018 vs 2026 approach
2018 (from episode #15):
import requests
import json
response = requests.get("https://api.coinmarketcap.com/v1/ticker/bitcoin/")
data = response.json()
print(data[0]["price_usd"])
2026 (modern approach):
import httpx
import os
from dataclasses import dataclass
@dataclass
class BitcoinPrice:
usd: float
change_24h: float
def get_bitcoin_price() -> BitcoinPrice:
with httpx.Client(timeout=10.0) as client:
response = client.get(
"https://api.coingecko.com/api/v3/simple/price",
params={"ids": "bitcoin", "vs_currencies": "usd", "include_24hr_change": "true"}
)
response.raise_for_status()
data = response.json()["bitcoin"]
return BitcoinPrice(usd=data["usd"], change_24h=data["usd_24h_change"])
price = get_bitcoin_price()
print(f"${price.usd:,.2f} ({price.change_24h:+.2f}%)")
The core concept is the same, but the 2026 version has:
- Type safety with dataclasses
- Proper timeout handling
- Context manager for resource cleanup
- Modern API endpoint (CoinMarketCap changed their API significantly)
What we've learned
In this refresher episode, we covered:
httpxas a modern alternative torequests- Bearer tokens and OAuth2 authentication
- Secure credential management with environment variables
- Improved error handling with retries
- Type hints for API responses
- HTTP/2 support
In the next episode, we'll explore advanced API patterns including POST/PUT/DELETE requests, handling pagination, and rate limiting strategies.
Thank you!
Thank you for your time! It's great to be back writing the Learn Python Series after all these years. If you have any questions, feel free to ask in the comment section below!
Thanks for your contribution to the STEMsocial community. Feel free to join us on discord to get to know the rest of us!
Please consider delegating to the @stemsocial account (85% of the curation rewards are returned).
Consider setting @stemsocial as a beneficiary of this post's rewards if you would like to support the community and contribute to its mission of promoting science and education on Hive.