Python Requests: Call REST APIs and Handle JSON Responses - Python with AI tutorial chapter 35
Python

Python Requests: Call REST APIs and Handle JSON Responses

Python with AI Tutorial · Chapter 35 of 48

Python requests is the most popular library for talking to web services. With one line you can call a REST API, and with .json() you turn the response into Python dictionaries and lists. This chapter covers GET and POST calls, query parameters, headers, timeouts, error handling and how to pull live exchange rates into a sales report.

Installing requests

requests is not part of the standard library, so install it once with pip. The examples below use two free public APIs: GitHub and open.er-api.com for exchange rates.

pip install requests
Output:
Successfully installed requests-2.32.3 ...

Your first GET request

requests.get() sends the request and returns a Response object. Always pass a timeout so a slow server cannot hang your script for ever.

import requests

r = requests.get("https://api.github.com/repos/pandas-dev/pandas", timeout=10)
print(r.status_code)
print(r.headers["Content-Type"])
data = r.json()
print(data["full_name"])
print(data["language"], data["stargazers_count"] > 10000)
Output:
200
application/json; charset=utf-8
pandas-dev/pandas
Python True

r.json() parses the JSON body into a normal dictionary, so data["full_name"] works exactly like any other dictionary lookup. GitHub allows 60 unauthenticated calls per hour, which is plenty for learning.

Reading JSON from an exchange-rate API

Most APIs return nested JSON. Walk through it with keys just as you would with a dictionary loaded from a file.

import requests

r = requests.get("https://open.er-api.com/v6/latest/USD", timeout=10)
data = r.json()
print(data["result"], data["base_code"])
print("INR:", data["rates"]["INR"])
print("EUR:", data["rates"]["EUR"])
print("Currencies returned:", len(data["rates"]))
Output:
success USD
INR: 83.51
EUR: 0.92
Currencies returned: 162

The exact rates change every day, so your numbers will differ. The structure of the JSON stays the same, which is what your code depends on.

Query parameters with params

Never build query strings by hand. Pass a dictionary to params= and requests encodes spaces, colons and other characters correctly.

import requests

params = {"q": "excel dashboard language:python", "sort": "stars", "per_page": 3}
r = requests.get("https://api.github.com/search/repositories", params=params, timeout=10)
print(r.url)
items = r.json()["items"]
print(len(items), "repositories returned")
print(items[0]["full_name"])
Output:
https://api.github.com/search/repositories?q=excel+dashboard+language%3Apython&sort=stars&per_page=3
3 repositories returned
some-user/excel-dashboard

The last line shows whichever repository currently has the most stars, so it will vary. Notice how r.url reveals the final encoded address, which is handy when debugging.

HTTP status codes you will meet

r.status_code tells you whether the call worked. r.ok is True for any code below 400.

Code Meaning What to do
200 OK Parse the body
201 Created A POST succeeded; the new record is usually returned
204 No Content Success with an empty body; do not call .json()
301 / 302 Redirect requests follows these automatically
400 Bad Request Check your parameters or JSON payload
401 Unauthorized Missing or invalid API key or token
403 Forbidden Valid login but no permission, or rate limit on GitHub
404 Not Found Wrong URL or the record does not exist
429 Too Many Requests Slow down; read the Retry-After header
500 / 503 Server error Retry later with a delay

Handling errors properly

r.raise_for_status() converts a 4xx or 5xx response into an exception. Combine it with the requests exception classes so network problems produce a helpful message instead of a crash.

import requests

def get_json(url, **params):
    try:
        r = requests.get(url, params=params, timeout=10)
        r.raise_for_status()
        return r.json()
    except requests.exceptions.HTTPError as e:
        print("HTTP error:", e.response.status_code)
    except requests.exceptions.ConnectionError:
        print("Network problem - check your connection")
    except requests.exceptions.Timeout:
        print("The server took too long to respond")
    return None

print(get_json("https://api.github.com/repos/pandas-dev/does-not-exist"))
Output:
HTTP error: 404
None
Try it with AI

Paste the JSON you got back and ask for code that extracts exactly the fields you need.

I called https://open.er-api.com/v6/latest/USD with Python requests and r.json() returned a dictionary with keys result, base_code, time_last_update_utc and rates (a dict of currency code to float). Write a function usd_to(codes) that takes a list like ["INR", "EUR", "GBP"] and returns a dictionary of code to rate rounded to 4 decimals, uses a 10 second timeout, calls raise_for_status(), and returns an empty dict with a printed warning if the request fails.

Using API data in a sales report

Here is the pattern you will use most: fetch a reference value once, then apply it to your own business data.

import requests

sales = [("North", "Dashboard", 3996), ("South", "Tracker", 2990), ("East", "Dashboard", 1998)]
rates = requests.get("https://open.er-api.com/v6/latest/USD", timeout=10).json()["rates"]
inr = rates["INR"]

print(f"Rate used: 1 USD = {inr} INR")
for region, product, usd in sales:
    print(f"{region:<6} {product:<10} USD {usd:>5}   INR {usd * inr:>10,.0f}")
Output:
Rate used: 1 USD = 83.51 INR
North  Dashboard  USD  3996   INR    333,706
South  Tracker    USD  2990   INR    249,695
East   Dashboard  USD  1998   INR    166,853

Sending data with POST

To create or submit records, use requests.post() with json=. requests serialises the dictionary and sets the Content-Type header for you. httpbin.org echoes back whatever you send, which makes it a safe practice target.

import requests

payload = {"Region": "West", "Product": "Tracker", "Units": 5, "Revenue": 1495, "Date": "2026-01-09"}
r = requests.post("https://httpbin.org/post", json=payload, timeout=10)
print(r.status_code)
print(r.json()["json"])
Output:
200
{'Region': 'West', 'Product': 'Tracker', 'Units': 5, 'Revenue': 1495, 'Date': '2026-01-09'}

Headers, tokens and sessions

Authenticated APIs expect a token in the Authorization header. Read it from an environment variable, and when you make several calls to the same host use a Session, which reuses the connection and applies shared headers.

import os
import requests

headers = {"Accept": "application/vnd.github+json"}
token = os.environ.get("GITHUB_TOKEN")          # optional: raises the limit to 5000/hour
if token:
    headers["Authorization"] = f"Bearer {token}"

with requests.Session() as s:
    s.headers.update(headers)
    for repo in ["pandas-dev/pandas", "numpy/numpy", "matplotlib/matplotlib"]:
        r = s.get(f"https://api.github.com/repos/{repo}", timeout=10)
        print(f"{repo:<24} {r.json()['stargazers_count']:>7,} stars")
    print("Calls left this hour:", r.headers["X-RateLimit-Remaining"])
Output:
pandas-dev/pandas         45,812 stars
numpy/numpy               29,640 stars
matplotlib/matplotlib     21,375 stars
Calls left this hour: 56
Never hard-code API keys. Put them in an environment variable or a .env file that is excluded from Git. A key pasted into a script ends up in screenshots, ChatGPT prompts and public repositories faster than you expect.
Try it with AI

Ask for a robust wrapper with retries and rate-limit handling around an API you use at work.

Write a Python function fetch_all_pages(url, params, token) using requests that: sends a Bearer token header, follows pagination by reading the Link header (GitHub style, rel="next"), waits and retries up to 3 times with exponential backoff on status 429 or 5xx, uses a Session and a 15 second timeout, and returns all items as one list. Add short comments and a usage example for https://api.github.com/orgs/pandas-dev/repos.

Common mistakes

  • Leaving out timeout=. requests waits for ever by default, so one dead server can freeze a scheduled job.
  • Calling .json() on an HTML error page or an empty 204 body, which raises JSONDecodeError. Check r.ok first.
  • Building URLs with string concatenation instead of params=, which breaks on spaces and special characters.
  • Ignoring rate limits and getting a 403 or 429. Cache results and add time.sleep() between calls.
  • Passing a dictionary to data= when the API wants JSON. data= sends form fields; json= sends JSON.

Exercise

Call https://open.er-api.com/v6/latest/USD and print how much 1,000 USD is worth in EUR, GBP and INR, each rounded to two decimals and formatted like EUR: 920.00. Use a timeout and raise_for_status().

Show answer
import requests

r = requests.get("https://open.er-api.com/v6/latest/USD", timeout=10)
r.raise_for_status()
rates = r.json()["rates"]

for code in ["EUR", "GBP", "INR"]:
    print(f"{code}: {1000 * rates[code]:.2f}")
Output:
EUR: 920.00
GBP: 790.00
INR: 83510.00

Your figures will reflect the current rates.

Related chapters

FAQ

What is the Python requests library used for?

requests sends HTTP requests such as GET and POST to websites and REST APIs and gives you the response as an object with the status code, headers and body. It is the standard way to fetch JSON data from web services in Python.

How do I read JSON from an API response in Python?

Call r.json() on the Response object. It parses the JSON body and returns Python dictionaries and lists, so you can access values with normal keys such as data[“rates”][“INR”].

Is requests part of the Python standard library?

No. Install it with pip install requests. The standard library has urllib, but requests is simpler to use and is what most tutorials and libraries rely on.

Working with spreadsheets too? Ready-made Excel, Google Sheets and Power BI templates are at NextGenTemplates.com.

Chapter 35 of 48 · Python with AI: all 48 chapters

PK
Meet PK, the founder of NeotechNavigators.com! With over 15 years of experience in Data Visualization, Excel Automation, and dashboard creation. PK is a Microsoft Certified Professional who has a passion for all things in Excel. PK loves to explore new and innovative ways to use Excel and is always eager to share his knowledge with others. With an eye for detail and a commitment to excellence, PK has become a go-to expert in the world of Excel. Whether you're looking to create stunning visualizations or streamline your workflow with automation, PK has the skills and expertise to help you succeed. Join the many satisfied clients who have benefited from PK's services and see how he can take your data analysis skills to the next level!
https://neotechnavigators.com