Calling APIs with Python

The requests library is the standard way to call an HTTP API from Python. Install it with pip install requests, then a whole integration is usually fewer than twenty lines.

A first call

import requests

r = requests.get("https://api.github.com/repos/django/django", timeout=10)
r.raise_for_status()          # turns 4xx/5xx into an exception
data = r.json()               # parse the JSON body into a dict
print(data["stargazers_count"])

resp = requests.post("https://httpbin.org/post", json={"name": "BeeProton"}, timeout=10)
print(resp.status_code, resp.json()["json"])

r.status_code, r.headers, r.text and r.json() cover almost everything you need from a response.

Query parameters and JSON bodies

Never build a URL by pasting strings together; pass a dict and let requests encode it correctly (spaces, &, Devanagari text and all).

params = {"city": "Pune", "from": "2026-04-01", "page": 2}
r = requests.get("https://api.example.in/v1/orders", params=params, timeout=10)

payload = {"customer": "Sharma Sweets", "gstin": "08ABCDE1234F1Z5", "amount": 11760}
r = requests.post("https://api.example.in/v1/invoices", json=payload, timeout=10)

json= sets Content-Type: application/json and serialises the dict for you. Use data= only when the API expects an HTML-style form.

Headers and secrets

Most APIs authenticate with a header. Keep the key in an environment variable, never in the code you commit.

import os

headers = {"Authorization": f"Bearer {os.environ['ORDERS_API_TOKEN']}"}
r = requests.get("https://api.example.in/v1/orders", headers=headers, timeout=10)

Handling failure properly

Networks fail, tokens expire and servers throttle. Wrap calls so one bad response does not crash a nightly job.

try:
    r = requests.get(url, headers=headers, timeout=(5, 30))
    r.raise_for_status()
    orders = r.json()
except requests.Timeout:
    print("API did not respond in time")
except requests.HTTPError as exc:
    print("API returned", exc.response.status_code, exc.response.text[:200])
except requests.RequestException as exc:
    print("Network problem:", exc)

The tuple timeout=(5, 30) means five seconds to connect and thirty to read.

Sessions and retries

For many calls to the same host, a Session reuses the TCP connection and keeps the headers in one place. Attaching a retry policy handles temporary 429 and 5xx errors with exponential backoff.

from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

session = requests.Session()
session.headers.update(headers)
retry = Retry(total=3, backoff_factor=1, status_forcelist=[429, 500, 502, 503, 504])
session.mount("https://", HTTPAdapter(max_retries=retry))

r = session.get("https://api.example.in/v1/orders", params={"page": 1}, timeout=20)

Paging through results

Most list endpoints return a page at a time. Loop until the API stops giving you a next link.

url, all_rows = "https://api.example.in/v1/orders", []
while url:
    r = session.get(url, timeout=20)
    r.raise_for_status()
    body = r.json()
    all_rows.extend(body["results"])
    url = body.get("next")
print(len(all_rows), "orders fetched")
Need Use
Send JSON json=payload
Send a form data=payload
Upload a file files={"report": open("sales.csv", "rb")}
Follow the response type r.headers["Content-Type"]

Common mistakes

  • Omitting timeout, so a hung server freezes your script forever.
  • Calling r.json() without checking the status code, then crashing on an HTML error page.
  • Hard-coding API keys and pushing them to GitHub.
  • Hammering an endpoint in a tight loop instead of respecting the documented rate limit.

Practice

Call https://api.github.com/search/repositories with params={"q": "django", "per_page": 5}, print each repository's name and star count, and add error handling plus a Session with retries. Then move the token for any authenticated API you use into an environment variable.

B

Written by Bablu Kumar Jha

Founder, BeeProton AI · Mumbai, Maharashtra, India