10 Practical Python API Tips to Turn Scripts from Working to Robust
This article presents ten concrete Python techniques for handling APIs—automatic retries, response caching, environment‑based secrets, Pydantic schema validation, clean pagination, WebSocket streaming, rate‑limiting, concurrent requests, mock responses, and GraphQL—each illustrated with runnable code to make scripts more reliable and efficient.
As large language models increasingly expose API services, Python scripts that call these endpoints become commonplace. The article outlines ten practical techniques to make such scripts more robust and maintainable.
1. Automatic retry of failed requests
APIs may intermittently return timeouts, rate‑limit (429), or 5xx errors. Using requests with urllib3.util.retry.Retry and mounting an HTTPAdapter enables automatic retries with exponential backoff (e.g., backoff_factor=1 yields 1 s, 2 s, 4 s delays).
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
session = requests.Session()
retry = Retry(total=5, backoff_factor=1, status_forcelist=[429, 500, 502, 503, 504])
session.mount('http://', HTTPAdapter(max_retries=retry))
session.mount('https://', HTTPAdapter(max_retries=retry))
response = session.get("https://api.example.com/data")
print(response.json())2. Local caching of API responses
When an API enforces strict rate limits, caching responses avoids unnecessary calls. The requests_cache library can install a persistent cache that expires after a configurable period (e.g., one hour).
import requests
import requests_cache
requests_cache.install_cache('api_cache', expire_after=3600)
r = requests.get('https://api.github.com/users/octocat')
print(r.json())3. Use environment variables for secrets
Storing API keys in a .env file and loading them with python-dotenv prevents hard‑coding credentials in source code.
import os
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.getenv("API_KEY")4. Schema validation with Pydantic
APIs can return unexpected types. Defining a Pydantic BaseModel enforces correct field types and automatically converts values (e.g., turning a string "12.5" into a float).
from pydantic import BaseModel
class Item(BaseModel):
name: str
price: float
data = {"name": "Apple", "price": "12.5"}
item = Item(**data)
print(item.price) # 12.5 as float5. Clean pagination handling
Many APIs paginate results. A generator that repeatedly fetches the next URL yields items without manual URL concatenation.
import requests
def fetch_all(url):
while url:
r = requests.get(url).json()
yield from r['results']
url = r.get('next')
for item in fetch_all("https://swapi.dev/api/people/"):
print(item['name'])6. WebSocket streaming
For real‑time data (e.g., market feeds), using the websocket library avoids polling. The example connects to Coinbase's feed and prints incoming messages.
import websocket, json
def on_message(ws, message):
data = json.loads(message)
print("Live:", data)
ws = websocket.WebSocketApp("wss://ws-feed.pro.coinbase.com", on_message=on_message)
ws.run_forever()7. Rate‑limit your own calls
To avoid being blocked by an API, the ratelimit decorator enforces a maximum number of calls per time window (e.g., 5 requests per minute).
from ratelimit import limits, sleep_and_retry
@sleep_and_retry
@limits(calls=5, period=60) # 5 requests per minute
def call_api():
print("API call")8. Parallel API calls
Using concurrent.futures.ThreadPoolExecutor to fire multiple requests concurrently can reduce total runtime dramatically (e.g., a script that previously took 2 minutes finishes in ~10 seconds).
import requests
from concurrent.futures import ThreadPoolExecutor
urls = [f"https://api.github.com/users/{u}" for u in ["octocat", "torvalds", "pjhyett"]]
def fetch(url):
return requests.get(url).json()
with ThreadPoolExecutor() as executor:
results = list(executor.map(fetch, urls))
print([r['login'] for r in results])9. Mock APIs for development
When the real API is unavailable, the responses library can mock HTTP calls, allowing tests to run without external dependencies.
import requests, responses
# (setup mock responses here)
# then reuse the same concurrent fetching logic as in tip 810. Easy GraphQL queries
Python's gql library simplifies GraphQL requests. The example queries a country’s name and capital, demonstrating that GraphQL can be treated like a regular Python dictionary.
from gql import gql, Client
from gql.transport.requests import RequestsHTTPTransport
transport = RequestsHTTPTransport(url="https://countries.trevorblades.com/")
client = Client(transport=transport, fetch_schema_from_transport=True)
query = gql("""{ country(code: "US") { name capital } }""")
result = client.execute(query)
print(result)By applying these ten tips, developers can write Python API scripts that are faster, safer, and easier to maintain.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
DeepHub IMBA
A must‑follow public account sharing practical AI insights. Follow now. internet + machine learning + big data + architecture = IMBA
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
