Many of the most useful things a program can do involve data from somewhere else: today's weather, exchange rates, the latest posts from a service, a list of books matching a search, or a payment confirmation. Services share this data through web APIs: addresses that a program, rather than a person, visits to ask for information or send it.
An API usually answers in JSON, a text format that looks almost exactly like Python dictionaries and lists. That makes Python a natural fit: one function call fetches the data, and another turns it into structures you already know how to work with.
In this lesson you'll use the popular requests library to call APIs, read JSON responses, pass search options, check whether a request worked, send data with POST, keep API keys secret and handle the network problems that real programs always meet.
Your first request
Install requests in your virtual environment with pip install requests, then:
import requests
response = requests.get("https://api.example.com/books/42", timeout=10)
print(response.status_code) # 200
book = response.json()
print(book["title"])requests.get(url)sends an HTTP GET request, which asks for data.response.status_codeis a number describing the result.response.json()converts the JSON body into Python dictionaries and lists.timeout=10stops waiting after 10 seconds instead of hanging for ever.
Status codes and errors
| Code | Meaning |
|---|---|
200 | OK |
201 | Created (after a POST) |
400 | Bad request: something in your request is wrong |
401 / 403 | Not signed in / not allowed |
404 | Not found |
429 | Too many requests: slow down |
500 | The server had an error |
Codes in the 200s mean success, 400s mean a problem with your request, and 500s a problem on the server. response.raise_for_status() raises an HTTPError for any 400 or 500 code, so you can handle failures with try and except instead of checking numbers by hand.
Query parameters and POST
Many APIs take options in the address, after a ?. Let requests build them safely with params:
r = requests.get("https://api.example.com/books",
params={"q": "python", "limit": 5}, timeout=10)
# requests visits .../books?q=python&limit=5To send data, use POST with json=, which converts a dictionary to JSON and sets the right header:
r = requests.post("https://api.example.com/orders",
json={"item": "mug", "qty": 2}, timeout=10)Other methods follow the same pattern: requests.put(), patch() and delete().
Headers and API keys
Most APIs need a key to identify you, usually sent in a header:
import os
key = os.environ["WEATHER_API_KEY"]
r = requests.get(url, headers={"Authorization": f"Bearer {key}"}, timeout=10)Never write keys directly in your code: if the file is shared or pushed to GitHub, anyone can use your account. Keep them in environment variables or a .env file that's listed in .gitignore. Read each API's documentation for its exact header name and rate limits.
Sessions, pagination and rate limits
When you make many requests to the same API, a requests.Session reuses the network connection and remembers headers, which is faster and keeps your code shorter:
import requests
session = requests.Session()
session.headers["Authorization"] = f"Bearer {key}"
page = 1
books = []
while True:
r = session.get("https://api.example.com/books",
params={"page": page}, timeout=10)
r.raise_for_status()
data = r.json()
books.extend(data["results"])
if not data["next"]:
break
page += 1Most APIs return results in pages, so a loop like this keeps fetching until the response says there is no next page. Field names such as results and next vary between APIs; check the documentation.
If you receive status 429, you are sending requests too quickly. Many APIs include a Retry-After header saying how many seconds to wait. Respect it with time.sleep() before trying again, and don't retry indefinitely.
Python Web APIs Syntax
requests essentials
import requests
r = requests.get(url, params={"key": "value"}, headers={...}, timeout=10)
r.status_code
r.raise_for_status()
data = r.json()
r = requests.post(url, json={"field": "value"}, timeout=10)
except requests.RequestException as error:Python Web APIs Summary
requests.get(url, params=..., timeout=...)fetches data;response.json()turns JSON into Python objects.- Status codes: 200s success, 400s your request, 500s the server;
raise_for_status()turns errors into exceptions. requests.post(url, json=data)sends data to an API.- Send API keys in headers and keep them in environment variables, never in code.
- Always set a timeout and handle network errors with try and except.
Python Web APIs Examples
Each example shows the code, the output it produces and what each part does.
Searching an API
import requests
r = requests.get(
"https://openlibrary.org/search.json",
params={"title": "the hobbit", "limit": 3},
timeout=10,
)
r.raise_for_status()
data = r.json()
print("Results found:", data["numFound"])
for book in data["docs"]:
year = book.get("first_publish_year", "unknown")
print(f"- {book['title']} ({year})")Results found: 418
- The Hobbit (1937)
- The Hobbit, or There and Back Again (1937)
- The Annotated Hobbit (1988)Line-by-line explanation
- Line 5requests adds ?title=the+hobbit&limit=3 to the address, encoding the space correctly.
- Line 8Stop with a clear error if the request failed.
- Lines 11 to 13The response is a dictionary with a "docs" list inside. .get() handles books without a year. Live data changes, so your numbers will differ.
Handling failures gracefully
import requests
def get_rate(currency):
url = f"https://api.example.com/rates/{currency}"
try:
r = requests.get(url, timeout=5)
r.raise_for_status()
return r.json()["rate"]
except requests.Timeout:
print("The service took too long. Try again later.")
except requests.HTTPError:
print(f"No rate available for {currency} ({r.status_code}).")
except requests.RequestException as error:
print("Network problem:", error)
return None
print(get_rate("XYZ"))No rate available for XYZ (404).
NoneLine-by-line explanation
- Line 6A short timeout keeps the program responsive.
- Lines 9 to 12Specific errors get specific, helpful messages.
- Line 13RequestException catches every other requests error, such as no internet connection.
- Line 15The caller gets None, which it can check for.
Sending data and saving the result
import json
import os
import requests
token = os.environ.get("TASKS_TOKEN")
new_task = {"title": "Write tests", "done": False}
r = requests.post(
"https://api.example.com/tasks",
json=new_task,
headers={"Authorization": f"Bearer {token}"},
timeout=10,
)
print(r.status_code)
created = r.json()
print(created)
with open("task.json", "w") as file:
json.dump(created, file, indent=2)201
{'id': 731, 'title': 'Write tests', 'done': False}Line-by-line explanation
- Line 5The token comes from an environment variable, not the code.
- Line 10json= converts the dictionary to JSON for you.
- Line 14201 means "created".
- Lines 18 to 19Save the response to a file for later.
Common Python Web APIs Mistakes
No timeout
r = requests.get(url) # can hang for everr = requests.get(url, timeout=10)Assuming the request worked
data = requests.get(url, timeout=10).json() # 404 page may not be JSONr = requests.get(url, timeout=10)
r.raise_for_status()
data = r.json()API key in the source code
API_KEY = "sk_9f8e7d6c5b4a" # pushed to GitHubimport os
API_KEY = os.environ["API_KEY"]Python Web APIs in Real Projects
APIs everywhere
Python scripts call APIs to post messages to chat tools, pull sales figures into reports, send text messages, check the weather for farm sensors and collect data for research. Data scientists fetch datasets from public APIs, and companies connect their own systems through internal APIs. The pattern is always the same: request, check the status, read the JSON.
Python Web APIs FAQ
What is an API in simple terms?
An API (application programming interface) is a defined way for one program to ask another for data or actions. A web API does this over HTTP: your code sends a request to a URL and receives a structured answer, usually JSON, instead of a web page meant for people.
Is requests part of the Python standard library?
No. requests is a third-party package installed with pip install requests. The standard library has urllib.request, which works without installing anything but needs more code for the same tasks. Most Python developers use requests, or the newer httpx library, which also supports async code.
What is the difference between GET and POST?
GET asks for data and sends any options in the URL's query string; it should not change anything on the server. POST sends data in the request body, typically to create something, such as a new order. The HTTP and REST lesson explains the other methods and when to use each.
Why does response.json() raise an error?
The response body isn't valid JSON, usually because the request failed and the server returned an HTML error page, or because the endpoint returns plain text. Call response.raise_for_status() first, and print response.text[:200] to see what actually came back.
How do I keep my API key out of my code?
Store it in an environment variable and read it with os.environ["NAME"], or keep it in a .env file loaded with a package such as python-dotenv. Add .env to .gitignore so it's never committed. The API authentication lesson covers tokens and OAuth in more depth.
Python Web APIs Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Complete the line so the JSON response is converted into Python objects.
data = response.()
Which status code means "not found"?
What does params={"q": "cats"} do in requests.get?
r.status_code is 500 and you call r.raise_for_status(). What happens?
Where should an API key live?
Python Web APIs Quiz
5 questions to check what you learned.
What format do most web APIs use for their responses?
- CSV
- HTML only
- JSON
Which HTTP method is normally used to create something new?
- POST
- GET
- HEAD
- OPTIONS
What does status code 429 tell you?
- Success
- The server crashed
- You're sending too many requests
- The page moved
Why always pass
timeout=?- It makes requests faster
- Without it, a request to a slow server can wait for ever
- Some APIs require it
- It retries failed requests
Which exception catches all errors raised by requests, including connection problems?
- ValueError
- requests.RequestException
- HTTPError only
- KeyError