Tech With Galvan
Courses/Python Mastery/The Python Ecosystem

requests: Talking to the Internet

30 min • Beginner • Module 11

Call APIs, parse JSON responses, and handle network failures.

requests: Talking to the Internet

Every weather app, chatbot, and price tracker has the same heartbeat: ask a server, get data back. The requests library is Python’s cleanest way to do it — and this lesson builds your first real API integration.

The anatomy of an API call

import requests

response = requests.get("https://api.github.com/users/GalvanMoto", timeout=10)
print(response.status_code)     # 200 — means "success"

Three parts:

  • requests.get(url) — the request: “please give me this resource”
  • timeout=10 — never wait forever; fail politely after 10 seconds (always set this)
  • response.status_code — the server’s answer code: 200 = OK, 404 = not found, 401 = unauthorized, 5xx = server’s fault

JSON APIs — the standard response

Modern APIs return JSON — which, as you know from Module 8, maps perfectly to Python dicts. requests parses it for you:

import requests

response = requests.get("https://api.github.com/users/GalvanMoto", timeout=10)
data = response.json()          # JSON → Python dict, automatically

print(data["name"])             # the account's display name
print(data["public_repos"])     # repository count
print(list(data.keys())[:5])    # peek at what's available

response.json() is your bridge: internet → dict → your code. Everything you learned about dictionaries applies directly.

A real app: the GitHub lookup

import requests

username = input("GitHub username: ")
url = f"https://api.github.com/users/{username}"

response = requests.get(url, timeout=10)

if response.status_code == 200:
    data = response.json()
    print(f"Name:  {data.get('name', 'N/A')}")
    print(f"Repos: {data['public_repos']}")
    print(f"Followers: {data['followers']}")
elif response.status_code == 404:
    print("No such user")
else:
    print(f"GitHub problem: {status_code}")

Notice the defensive shape: status check first, then parse. Parsing before checking crashes on 404s (the “user” JSON won’t exist). This guard-first pattern is non-negotiable in real code.

Sending parameters — the params argument

Many APIs take query parameters (the ?key=value parts of URLs). Build them properly with params=:

import requests

# Instead of fragile string-building:
# requests.get("https://api.openweathermap.org/data/2.5/weather?q=Delhi&appid=KEY")

response = requests.get(
    "https://api.openweathermap.org/data/2.5/weather",
    params={"q": "Delhi", "appid": "YOUR_KEY", "units": "metric"},
    timeout=10,
)
data = response.json()
print(f"{data['name']}: {data['main']['temp']}°C")

params= URL-encodes everything correctly — spaces, symbols, unicode — which manual string-building gets wrong. This is exactly the pattern from the weather app tutorial.

Headers — identifying yourself and authenticating

Two header uses you’ll meet immediately:

# 1. A User-Agent (many APIs reject default python requests)
headers = {"User-Agent": "MyLearningApp/1.0"}

# 2. Authentication (token-based APIs — like Mistral AI)
headers = {"Authorization": f"Bearer {api_key}"}

response = requests.get(url, headers=headers, timeout=10)

The Bearer-token pattern is how every AI API in this site’s projects authenticates — the key travels in the header, never in the URL.

The error-handling shape (production pattern)

import requests

def fetch_json(url, params=None):
    try:
        response = requests.get(url, params=params, timeout=10)
        response.raise_for_status()        # raises for 4xx/5xx!
        return response.json()
    except requests.exceptions.Timeout:
        print("Server took too long")
    except requests.exceptions.HTTPError as err:
        print(f"HTTP error: {err}")
    except requests.exceptions.RequestException as err:
        print(f"Network problem: {err}")
    return None

data = fetch_json("https://api.github.com/users/GalvanMoto")
if data:
    print(data.get("name"))

Three new tools: raise_for_status() converts bad status codes into exceptions (one check covers all 4xx/5xx), RequestException is the parent of ALL requests errors (catch it last), and returning None on failure lets the caller decide. This shape — try, raise_for_status, specific excepts, return None — is production-grade from day one.

Common Errors & Fixes

  • ConnectionError / timeouts — no internet, wrong URL, or firewall. Check the URL in a browser first.
  • 401 Unauthorized — missing/wrong API key, or the header is malformed. Re-read the API’s auth docs.
  • 404 — wrong URL or wrong resource ID; print the final URL you actually requested.
  • JSONDecodeError on response.json() — the API returned non-JSON (often an HTML error page). Check status_code before parsing.

✅ Checkpoint

  1. What does status 200 mean? 404? (Success / not found)
  2. What parses the response body into a dict? (response.json())
  3. Why params= instead of building the URL string? (Correct encoding of spaces and symbols)
  4. What does raise_for_status() do? (Raises an exception for 4xx/5xx — one check covers all bad statuses)

Next: 🧪 Practice — a complete project setup from empty folder to working API call.