Skip to content

HTTP fundamentals

  • Lesson 2
  • 35 min
  • Module 1: Understand the web request

What you will be able to do

Read any HTTP request and response, part by part, and choose the right method and status code for your own API. Every endpoint that you build in this course is a choice of these parts.

Before you startWhat to know and to have readyAbout 35 minutes

What you need to know

What you need on your computer

  • A terminal with a Python virtual environment that has httpx 0.28.1. For example, your ticket-cleaner project, which you set up in Isolate dependencies: Guided practice: set up ticket-cleaner. If you do not have one, make one in a new folder: python3 -m venv .venv, activate it, and run python -m pip install httpx==0.28.1.
  • An internet connection. The examples use Postman Echo, a free public service from Postman that sends your request back to you. It needs no account.

Tested with Python 3.14.6 and httpx 0.28.1 on macOS.

Here is the request that you will send to your own API later in the course, as text, exactly as HTTP sends it:

An HTTP request
POST /v1/classify?lang=en HTTP/1.1
Host: api.example.com
Content-Type: application/json
Accept: application/json
X-API-Key: k-7f3a9c
{"subject": "Charged twice for March", "body": "Same invoice two times."}
Part In the example Meaning
Method POST What the client wants to do.
Path and query /v1/classify?lang=en Which resource, with extra values.
Headers Content-Type: application/json and the next lines Facts about the request, as Name: value lines.
Empty line Ends the headers.
Body The JSON text The data that the client sends. Optional.

The method says what the client wants to do with the resource. APIs mainly use five:

Method Meaning Body? Example
GET Read a resource. Change nothing. No GET /v1/categories
POST Send data to be processed, or create a resource. Yes POST /v1/classify
PUT Replace a resource completely. Yes PUT /v1/tickets/T-1001
PATCH Change part of a resource. Yes PATCH /v1/tickets/T-1001
DELETE Delete a resource. No DELETE /v1/tickets/T-1001

GET is safe: it never changes anything on the server, so a client, a browser or a cache can repeat it without risk. That is why a request that changes data must never be a GET. A browser can load a GET link before you click it, and that link must not delete anything.

An API built around resources (things with addresses, such as /v1/tickets/T-1001) and these methods is called a REST API. Most web APIs follow this style, and so does the API in this course.

A value in the URL can be in the path or in the query. They have different jobs:

Kind Example Use it for
Path parameter /v1/tickets/T-1001 Which resource. Without it, the URL means something else.
Query parameter /v1/tickets?status=open&limit=20 Options: filters, sort order, page size. They can usually be left out.

All URL values are text. The server converts limit=20 to the number 20. It must reject limit=twenty with a clear error, as you did for file data in the Python course.

Headers carry facts about the request or the response. The name is not case-sensitive: content-type and Content-Type are the same header. The common ones in this course:

Header Sent by Meaning
Content-Type Both The format of the body, for example application/json.
Accept Client The formats that the client can read.
X-API-Key or Authorization Client Proves who is calling. Module 6 adds an API key.
X-Request-ID Server An ID for this request, to find it in the logs. Module 6 adds it.
Content-Length Both The size of the body in bytes.

Headers that start with X- are not in the HTTP standard. Applications define them for their own use.

A body can be any data, but APIs almost always use JSON (JavaScript Object Notation). JSON is text that holds objects, lists, strings, numbers, true, false and null. You used it in the Python course. The Content-Type: application/json header tells the other side how to read the body. Without it, many servers reject the body or read it as plain text.

Worked example: see what the server receives

Section titled “Worked example: see what the server receives”

Amira uses Postman Echo, which answers with a copy of what it received. She starts interactive Python in her virtual environment (python), and sends a GET with two query parameters:

Interactive Python
>>> import httpx
>>> r = httpx.get("https://postman-echo.com/get", params={"status": "open", "limit": 2}, timeout=10)
>>> r.request.url
URL('https://postman-echo.com/get?status=open&limit=2')
>>> r.status_code, r.headers["content-type"]
(200, 'application/json; charset=utf-8')
>>> r.json()["args"]
{'status': 'open', 'limit': '2'}

httpx built the query from the dictionary: ?status=open&limit=2. The server received limit as the text '2', not the number 2. All values in a URL are text.

Then she sends a POST with a JSON body:

Interactive Python
>>> r = httpx.post("https://postman-echo.com/post", json={"subject": "Charged twice", "priority": 1}, timeout=10)
>>> r.request.headers["content-type"]
'application/json'
>>> r.request.content
b'{"subject":"Charged twice","priority":1}'
>>> r.json()["json"]
{'subject': 'Charged twice', 'priority': 1}

The json= argument did two things: it turned the dictionary into JSON text for the body, and it added the Content-Type: application/json header. In the body, priority stays a number, because JSON has numbers.

The status code tells the client the result. You met the groups in the Python course: 2xx success, 4xx a problem with the request, 5xx a problem on the server. An API must choose the most exact code, because programs act on it:

Code Name Use it when
200 OK The request worked, and the body has the result.
201 Created A POST made a new resource.
204 No Content It worked, and there is nothing to send back.
400 Bad Request The request is wrong in a general way.
401 Unauthorized No valid credentials, such as a missing API key.
403 Forbidden Valid credentials, but no permission for this action.
404 Not Found No resource at this path.
413 Content Too Large The body is larger than the server accepts.
422 Unprocessable Content The JSON is readable, but a value is not valid. FastAPI uses it for validation errors.
429 Too Many Requests The client sent too many requests. Wait, then try again.
500 Internal Server Error The server had an unexpected error: a bug.
503 Service Unavailable The server, or a part that it needs, is not available now. Try again later.

The difference between 4xx and 5xx decides what a client does next. After a 4xx, the client must change the request: sending it again does not help. After a 503, the same request can work later.

  1. Start interactive Python in a virtual environment that has httpx.

  2. Send a GET to https://postman-echo.com/get with the query parameters category=billing and page=3. Show r.request.url and r.json()["args"].

    You see ?category=billing&page=3 in the URL, and both values as text.

  3. Send a POST to https://postman-echo.com/post with the JSON body {"subject": "Locked out", "urgent": true}. Show r.json()["json"].

    The body comes back with 'urgent': True: JSON true became Python True.

  4. Send a request that gets a status code you choose: Postman Echo answers https://postman-echo.com/status/404 with status 404. Show r.status_code, then try 201 and 503.

The help desk wants three more features. For each one, write the method, the path, any query parameters, whether there is a body, and the status code for success. Use /v1/tickets as the base path.

  1. Get the open tickets, 20 at a time.
  2. Change only the status of ticket T-1042 to closed.
  3. Delete ticket T-1042. After that, what does a GET of the same ticket return?
Show one answer
  1. GET /v1/tickets?status=open&limit=20. No body. 200, with a list in the body. The filter and the page size are options, so they are query parameters.
  2. PATCH /v1/tickets/T-1042, with the body {"status": "closed"}. 200 with the changed ticket, or 204 with no body. The ID is in the path because it says which resource.
  3. DELETE /v1/tickets/T-1042. No body. 204. A later GET /v1/tickets/T-1042 returns 404, because the resource no longer exists.

Common mistake

The server ignores the JSON body

Symptom: you send correct JSON, but the server answers 422 and says that the body is not valid. The API in this course answers "fields": ["body"]. Some services, such as Postman Echo, guess the format, so the same request can work there and fail on your API.

Cause: the request has no Content-Type: application/json header, so the server does not know that the body is JSON. This happens when you write the body as text yourself, for example httpx.post(url, content='{"subject": "x"}'), or in curl without a -H option.

Fix:

  1. In httpx, use json= with a dictionary. It sets the header for you.
  2. If you send the body as text, add the header yourself: headers={"Content-Type": "application/json"}.
  3. Check the request that was really sent: r.request.headers.

Check your understanding

An endpoint GET /v1/tickets/delete?id=T-1042 deletes a ticket. What is wrong with this design?

Show the answer

It uses GET to change data. GET must be safe: browsers, link previews and caches send GET requests without asking the user, and each one would delete a ticket. Use DELETE /v1/tickets/T-1042. The action is the method, not a word in the path.

Check your understanding

Your API cannot reach the classifier that it depends on. Which status code should it return: 400, 404, 500 or 503? Why?

Show the answer

503 Service Unavailable. The request is correct (not 400 or 404), and the server has no bug (not 500). A part that the service needs is not available now, and the same request can work later. A client can safely try again after a short wait.

Recap

  • A request has a method, a path with optional query parameters, headers and an optional body. A response has a status code, headers and a body.
  • GET reads and is safe. POST, PUT, PATCH and DELETE change data.
  • A path parameter says which resource. A query parameter gives an option. All URL values are text.
  • Content-Type: application/json tells the other side that the body is JSON.
  • Choose the most exact status code. 4xx: change the request. 5xx: the problem is on the server.

Next, you look at real requests with your browser’s developer tools and with curl.

Next: Inspect a request