14.6 JSON and REST APIs
- Approved by
- Panaversity ·
In everyday life. A takeout window has a menu taped beside it. You ask for item 12 with no onions, and you get a bag with a receipt. You never see the kitchen. So you check the bag before you leave, because the kitchen can change a recipe without telling you.
What you will learn. How to read a request to another program and its answer, and check what arrives before you use it.
Why it matters. The other program belongs to someone else, who can change it without telling you. So your program checks every answer before it uses it.
What is JSON?
JSON is the text format most programs use to send records to each other. { } holds a dictionary and [ ] a list, text goes in quotes, and numbers, true, false and null are written plainly. JSON has no dates, so a date travels as text, as in {"number": "INV-8306", "dueDate": "2027-03-08"}.
What are the parts of a request?
An API, an application programming interface, is a way for one program to ask another for something. A REST API does it over the web, with one address, or URL, for each kind of thing. Each of those addresses is an endpoint. At the takeout window, the menu is the list of endpoints, your order is the request, and the bag with its receipt is the response.
| Part of a request or response | What it is | Brightline sample API |
|---|---|---|
| Method | What you want to do: GET reads, POST creates | GET |
| Endpoint | The address of the thing | /invoices |
| Query string | Options after a ?, joined by & | ?status=open&page=2 |
| Header | A labeled extra on the request or response | X-API-Key: ..., Content-Type: application/json |
| Status code | A number that says how it went | 200, 401, 503 |
| Body | The data, usually JSON | {"invoices": [...], "page": 2, "pages": 3} |
Pages. An API rarely returns thousands of records at once. It returns a page of them and tells you how many pages there are. A common bug in a client, the program that sends the requests, is reading only the first page. It gives no warning.
At work. Brightline's repository includes a small sample REST API that runs on your own computer. It needs the key from .env and serves five invoices per page.
Here is one order at the window, and what comes back. localhost in the address means your own computer. The key comes from the environment, as Where does the key go? explains. Predict each line.
// the order: GET /invoices, the open ones, page 2, with the key in a header
const response = await fetch("http://localhost:4010/invoices?status=open&page=2", {
headers: { "X-API-Key": process.env.AP_API_KEY ?? "" },
});
// the receipt: how it went, and what kind of body came back
console.log(response.status, response.headers.get("content-type"));
// the bag: the body, read as JSON
const body = await response.json();
console.log(body.page, body.pages, body.invoices.length, body.invoices[0].number);
// a record of your own, turned into JSON text
console.log(JSON.stringify({ number: "INV-8306", amountCents: 158200, dueDate: "2027-03-08" }));| Language | The marks in this code |
|---|---|
| TypeScript | fetch makes the request, and await waits for the answer, which 14.7 explains. process.env.AP_API_KEY reads the key from the environment, and ?? "" uses empty text if there is none. JSON.stringify turns a record into JSON text. |
| Python | httpx is the library this book uses to send web requests, and its params builds the query string. os.environ["AP_API_KEY"] reads the key from the environment. json.dumps turns a record into JSON text. |
200 application/json
2 3 5 INV-8306
{"number":"INV-8306","amountCents":158200,"dueDate":"2027-03-08"}What should the client do for each status code?
Act on each one on purpose. Neither fetch nor httpx.get raises an error for a 401 or a 503. Each hands back the answer with its code, so a program that ignores the code reads an error message as if it were invoices.
| Code | Means | What the client should do |
|---|---|---|
| 200 | OK | Read the body |
| 400 | Bad request: your request was wrong | Fix the request. Retrying will not help |
| 401 | Not authorized: no key, or the wrong key | Check the key. Retrying will not help |
| 404 | Not found | Check the endpoint |
| 429 | Too many requests | Wait, then try again |
| 503 | The server is busy or down for a moment | Wait briefly and retry |
Why did the tests pass and the live run fail?
Because the tests and the code shared one picture of the data, and the API had its own. Only the live answer shows what the API really sends, and the schema at the edge reads it.
At work. Ana's Python client passes every test against a fake API, a stand-in that answers like the real one. The agent built it with the field names she expected, such as vendor_id. Against the sample API, the client is refused on the first invoice, because that API's JSON says vendorId. The schema caught it before any invoice was used. She asks for the model to accept both names.
Where does the key go?
Into an environment variable, never into code, a test or a commit. An API key is a secret that proves who is calling. An environment variable is a named value the operating system gives a running program, usually loaded from a .env file that git ignores. If a key is ever committed, treat it as leaked, even if you delete it in the next commit, because git keeps every commit.
Try it
Set up the sample API. Choose a made-up key, such as a word and a number. Send: "Please copy .env.example to .env, and put my made-up key in it: [your key]. Then start the sample API in the background, and tell me how you will stop it."
Run. Send: "Please run the file in 14-6 and show me what it prints. Don't explain it yet."
Modify. Send: "Please change page 2 to page 4 in the file. Don't run it yet." There is no page 4, and this API treats that as your mistake, so it answers 400. From the table, what should a client do then? The file still goes on to read the invoices: what will happen? Then ask the agent to run it, and ask: "What did the body of that answer say?"
Make. Send: "Please change the file so it checks the status code before it reads the invoices, and prints the error when the code is not 200. Run it."
Stop. Send: "Please stop the sample API now, and show me that nothing is still running."
What you should see. 400, then a TypeError or KeyError, because the body holds an error, not a page of invoices. After your change, the error is printed plainly.
The operating rule: read an API call by its parts, and handle each status code on purpose. Read every page, keep keys in the environment, and validate the body with a schema before you use it, even when your tests pass.

Figure 14.7. One request, one response.
Next. 14.7 decides how long to wait for a call, and when to try again.
Check yourself
Question 1 / 8
0 answered
An API answers 401. What should the client do?
Sources
- Owner
- Panaversity