HTTP fundamentals
- Lesson 2
- 35 min
- Module 1: Understand the web request
Lesson 2 of 21 · Build and Deploy Your First APIHTTP fundamentals
1. Understand the web request
2. Build a small FastAPI service
3. Design a usable contract
4. Document the API with OpenAPI and Swagger UI
5. Explore and test with Postman
6. Test and harden the service
7. Deploy and verify
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 ready
What you need to know
- The steps from Browser-to-server basics: The whole journey: DNS, connection, TLS, then HTTP.
What you need on your computer
- A terminal with a Python virtual environment that has httpx 0.28.1. For example, your
ticket-cleanerproject, 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 runpython -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.
A request, part by part
Section titled “A request, part by part”Here is the request that you will send to your own API later in the course, as text, exactly as HTTP sends it:
POST /v1/classify?lang=en HTTP/1.1Host: api.example.comContent-Type: application/jsonAccept: application/jsonX-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. |
Methods
Section titled “Methods”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.
Path parameters and query parameters
Section titled “Path parameters and query parameters”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
Section titled “Headers”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.
Bodies and JSON
Section titled “Bodies and JSON”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:
>>> import httpx>>> r = httpx.get("https://postman-echo.com/get", params={"status": "open", "limit": 2}, timeout=10)>>> r.request.urlURL('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:
>>> 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.contentb'{"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.
Status codes
Section titled “Status codes”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.
Guided practice: read the parts
Section titled “Guided practice: read the parts”-
Start interactive Python in a virtual environment that has httpx.
-
Send a
GETtohttps://postman-echo.com/getwith the query parameterscategory=billingandpage=3. Showr.request.urlandr.json()["args"].You see
?category=billing&page=3in the URL, and both values as text. -
Send a
POSTtohttps://postman-echo.com/postwith the JSON body{"subject": "Locked out", "urgent": true}. Showr.json()["json"].The body comes back with
'urgent': True: JSONtruebecame PythonTrue. -
Send a request that gets a status code you choose: Postman Echo answers
https://postman-echo.com/status/404with status404. Showr.status_code, then try201and503.
Your turn: design three requests
Section titled “Your turn: design three requests”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.
- Get the open tickets, 20 at a time.
- Change only the status of ticket
T-1042toclosed. - Delete ticket
T-1042. After that, what does aGETof the same ticket return?
Show one answer
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.PATCH /v1/tickets/T-1042, with the body{"status": "closed"}.200with the changed ticket, or204with no body. The ID is in the path because it says which resource.DELETE /v1/tickets/T-1042. No body.204. A laterGET /v1/tickets/T-1042returns404, 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:
- In httpx, use
json=with a dictionary. It sets the header for you. - If you send the body as text, add the header yourself:
headers={"Content-Type": "application/json"}. - 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.
Does your answer have the main points of ours?
Sign in and enroll in the course to save your answers. Every check counts toward the certificate.
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.
Does your answer have the main points of ours?
Sign in and enroll in the course to save your answers. Every check counts toward the certificate.
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.
GETreads and is safe.POST,PUT,PATCHandDELETEchange data.- A path parameter says which resource. A query parameter gives an option. All URL values are text.
Content-Type: application/jsontells 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