Unit 13 · lesson

Calling an API With Python

You now have the protocol model. The next job is making Python perform the request without hiding the evidence you will need when it fails.

This course uses requests, a common third-party HTTP client library for Python. It is not part of Python's standard library, so the environment that runs your program must have the package installed.

First prove which Python environment you are modifying

Do not begin with a blind pip install.

From the project environment, inspect Python:

python --version
python -m pip --version

The second command is useful because python -m pip asks the selected Python interpreter to run its pip module. That makes the relationship between interpreter and installer more explicit.

If your approved workspace does not already provide Requests:

python -m pip install requests

If installation is blocked, use the supplied response/fixture path. The learning target is HTTP-client reasoning, not bypassing environment controls.

Make the smallest useful request

import requests

url = "https://jsonplaceholder.typicode.com/users/1"
response = requests.get(url, timeout=5)

print(response.status_code)

There are already several pieces of evidence here:

url           -> request target
GET           -> method chosen by requests.get(...)
timeout=5     -> waiting boundary
response      -> object representing the received HTTP response
status_code   -> protocol result

A timeout is a boundary, not a promise that every request will finish in five seconds. It prevents your application from waiting indefinitely for this operation.

Do not parse the body before checking the result

This code is tempting:

response = requests.get(url, timeout=5)
data = response.json()
print(data["name"])

But it silently assumes several things:

  1. a response arrived;
  2. the HTTP result is acceptable;
  3. the body contains JSON;
  4. the JSON has the shape you expect;
  5. name exists.

Make the protocol failure explicit first:

response = requests.get(url, timeout=5)
response.raise_for_status()

raise_for_status() raises an HTTP-related exception for unsuccessful status results handled by Requests.

Then parse:

data = response.json()
print(type(data))
print(data)

Inspect the shape before extracting fields.

Concept flow

JSON crosses the boundary; Python structures stay local

The HTTP body is text encoded as JSON. response.json() parses that representation into normal Python data structures.

  1. HTTP BODYJSON text
    parse
  2. response.json()decode the representation
    creates
  3. PYTHON DATAdict / list
    access
  4. FIELDdata["name"]

JSON text becomes Python objects

A response body may contain JSON text such as:

{
  "id": 1,
  "name": "Leanne Graham",
  "email": "Sincere@april.biz"
}

After successful JSON decoding, response.json() returns corresponding Python data structures. For this object-shaped JSON, you receive a dictionary-like Python object:

data = response.json()
print(type(data))
print(data["name"])

Do not say “the server sent a Python dictionary.” It did not. The server sent bytes/text in an HTTP response. Your client library decoded JSON into Python objects.

That distinction becomes important when other languages consume the same API.

Lists change the local shape

An endpoint that returns multiple records may produce JSON like:

[
  {"name": "Nova"},
  {"name": "Maya"}
]

Now the decoded Python structure is a list containing dictionaries:

users = response.json()

for user in users:
    print(user["name"])
Concept flow

Process an API collection one record at a time

A JSON array becomes a Python list. The loop receives one dictionary per iteration and extracts only the field the program needs.

  1. API RESPONSEJSON array
    response.json()
  2. PYTHON LISTdict, dict, dict
    iterate
  3. FOR LOOPone user dictionary
    inspect
  4. FIELDuser["name"]

Notice how earlier Python concepts reappear:

HTTP response
   -> JSON decoding
   -> list
   -> loop
   -> dictionary lookup
   -> output

APIs do not replace Python fundamentals. They feed external data into them.

Write one request function with a clear boundary

import requests


def fetch_user(user_id):
    url = f"https://jsonplaceholder.typicode.com/users/{user_id}"
    response = requests.get(url, timeout=5)
    response.raise_for_status()
    return response.json()

This function owns acquisition. It does not print a report, calculate statistics, or decide what the user interface should look like.

That separation makes the next step easier:

def summarize_user(data):
    return f"{data['name']} <{data['email']}>"

Now summarize_user() can be tested with a local dictionary even when the network is unavailable.

What if the response is not JSON?

response.json() is a decoding operation. It can fail if the body is not valid JSON.

A status code by itself does not guarantee body format.

During investigation, inspect useful evidence such as:

print(response.status_code)
print(response.headers.get("content-type"))
print(response.text[:200])

Do not dump sensitive headers, authentication tokens, or private response data into screenshots or public logs.

Controlled exercise: prediction before network

Before running a live request, read this supplied response model:

sample = {
    "id": 7,
    "name": "Rover Lab",
    "active": True,
}

Predict the output:

print(sample["name"])
print(sample.get("status", "UNKNOWN"))

Then answer:

Which part of that exercise proves your data-processing logic even though it proves nothing about network availability?

That question is the reason Unit 13 includes fixtures. A live service is one dependency, not the entire lesson.

Before moving on

You should now be able to distinguish:

  • installing a package from importing it;
  • sending a request from parsing a response body;
  • HTTP success from application/data correctness;
  • JSON text from Python objects; and
  • network acquisition from local analysis.

Lesson 3 adds failure handling without collapsing every problem into one giant try/except.

Vocabulary lab

Flip the idea, not just the card

Explain the term before you reveal the back. Then compare your explanation with the definition, example, and warning.

1 / 5
Read all terms without animation
Requests
A third-party Python HTTP client library used to send requests and inspect responses. Example: requests.get(url, timeout=5). Do not confuse it with: Python's standard library itself.
Timeout
A boundary limiting how long a network operation is allowed to wait before failing. Example: timeout=5 on an HTTP request. Do not confuse it with: A guarantee that the server responds within that duration.
raise_for_status
A Requests response method that raises an HTTP error for unsuccessful HTTP status results. Example: response.raise_for_status(). Do not confuse it with: Validation of the JSON fields inside a successful response.
JSON Decoding
Converting JSON response text into corresponding Python data structures. Example: response.json() producing a dict for a JSON object. Do not confuse it with: Receiving a Python dictionary directly over the network.
Fixture
Controlled local data used to test processing independently of a live dependency. Example: A saved JSON sample matching the API's expected record shape. Do not confuse it with: Evidence that a live request succeeded.