ch11s2_MakingAPIRequests_with_requests

APIs communicate using **HTTP requests** and **responses**. Python’s `requests` library is one of the simplest and most powerful tools for working with HTTP-based APIs. It abstracts complex networking operations into clean, human-readable code.

Chapter 11: Working with APIs — Making API Requests (using requests)

⚙️ Making API Requests in Python with the requests Library

APIs communicate using HTTP requests and responses. Python’s requests library is one of the simplest and most powerful tools for working with HTTP-based APIs. It abstracts complex networking operations into clean, human-readable code.


🌍 1. The Request–Response Cycle

Whenever you interact with an API:

  1. You send a request (with optional data, headers, etc.).
  2. The server processes it and sends a response.
  3. The response includes a status code, headers, and body (data).
MethodDescriptionExample
GETRetrieve dataFetch user info
POSTSend or create dataSubmit form or upload file
PUTUpdate dataEdit a record
PATCHPartially update dataUpdate one field
DELETERemove dataDelete a resource

🧱 2. Sending a Basic GET Request

import requests

url = "https://jsonplaceholder.typicode.com/posts/1"
response = requests.get(url)

print("Status Code:", response.status_code)
print("Response Body:", response.text)

Tip: Use .json() when you know the response is JSON:

data = response.json()
print("Title:", data["title"])

✉️ 3. Sending Query Parameters

You can pass parameters to refine your GET requests using the params argument.

url = "https://jsonplaceholder.typicode.com/comments"
params = {"postId": 1}

response = requests.get(url, params=params)
print("Request URL:", response.url)
print("Number of comments:", len(response.json()))

🧾 4. Sending Data with POST Requests

Sending Form Data

url = "https://httpbin.org/post"
data = {"name": "Rambod", "message": "Hello API!"}

response = requests.post(url, data=data)
print(response.json())

Sending JSON Payloads

url = "https://httpbin.org/post"
payload = {"title": "New Post", "body": "Learning APIs", "userId": 42}

response = requests.post(url, json=payload)
print(response.json())

✅ Always use json=payload to automatically encode the data and set the Content-Type header.


🧠 5. Custom Headers and Authentication

You can send headers or tokens to authenticate or customize your request.

url = "https://api.github.com/user"
headers = {"Authorization": "Bearer YOUR_GITHUB_TOKEN"}

response = requests.get(url, headers=headers)
print(response.status_code)
print(response.json())

🔒 6. Using Sessions for Persistent Connections

A Session object keeps cookies and headers across multiple requests — improving performance.

with requests.Session() as session:
    session.headers.update({"User-Agent": "RambodAPI/1.0"})
    r1 = session.get("https://httpbin.org/cookies/set/sessionid/123")
    r2 = session.get("https://httpbin.org/cookies")
    print(r2.json())

🕵️ 7. Handling Responses Properly

url = "https://jsonplaceholder.typicode.com/posts/1"
response = requests.get(url)

print("Status Code:", response.status_code)
print("Headers:", response.headers)
print("JSON Data:", response.json())

Common Response Attributes

AttributeDescription
.status_codeHTTP status (e.g., 200, 404)
.headersMetadata like content type, date
.textRaw string content
.json()Parse response as JSON

🚨 8. Error Handling and Exceptions

Always check for errors before using the response.

import requests

url = "https://jsonplaceholder.typicode.com/posts/99999"

try:
    response = requests.get(url, timeout=5)
    response.raise_for_status()
except requests.exceptions.HTTPError as err:
    print("HTTP error:", err)
except requests.exceptions.RequestException as e:
    print("Network error:", e)
else:
    print("Success:", response.json())

💡 Use timeout to prevent hanging requests in case of network issues.


🔁 9. Uploading Files

You can upload files to APIs that accept file inputs.

url = "https://httpbin.org/post"
files = {"file": open("data.txt", "rb")}
response = requests.post(url, files=files)

print(response.json())

🧱 10. Downloading and Streaming Data

For large files, stream the response instead of loading it all into memory.

url = "https://www.example.com/largefile.zip"
response = requests.get(url, stream=True)

with open("downloaded.zip", "wb") as f:
    for chunk in response.iter_content(chunk_size=8192):
        f.write(chunk)

⚙️ Always use stream=True for large files to avoid memory overload.


🧩 11. Handling Pagination

Many APIs return data in pages. Use query parameters to iterate through them.

for page in range(1, 4):
    response = requests.get("https://jsonplaceholder.typicode.com/posts", params={"_page": page})
    print(f"Page {page}:", len(response.json()), "records")

🧠 12. Retrying Failed Requests (Advanced)

Use HTTPAdapter for automatic retries.

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

session = requests.Session()
retry = Retry(connect=3, backoff_factor=0.5)
adapter = HTTPAdapter(max_retries=retry)
session.mount("http://", adapter)
session.mount("https://", adapter)

response = session.get("https://httpbin.org/status/500")
print("Status:", response.status_code)

🧰 13. Debugging and Inspection

Enable request inspection using response.request.

print("Request Method:", response.request.method)
print("Request Headers:", response.request.headers)
print("Request Body:", response.request.body)

🧭 14. Best Practices for requests

✅ Always use timeout to avoid infinite waiting.
✅ Handle exceptions with try/except.
✅ Use Session for multiple calls.
✅ Don’t log API keys.
✅ Use pagination instead of downloading all data at once.
✅ Prefer json= instead of data= for structured APIs.
✅ Check .status_code before parsing .json().


🧠 Summary

ConceptDescriptionExample
GETRetrieve datarequests.get(url)
POSTSend datarequests.post(url, json=payload)
HeadersMetadata for requestsheaders={'Auth': 'token'}
ResponseContains status, headers, and contentresponse.json()
Error HandlingDetect and recover from issuesraise_for_status()
SessionReuse settings across requestsrequests.Session()

Mastering the requests library gives you the power to interact with nearly any API — efficiently, securely, and professionally.