Leer en español · Lire en français · Auf Deutsch lesen
Section 0.6 of 0.8 · Beginner · 17 min read
APIs, JSON and how separate systems work together
Video lesson
5 min · narrated, with captionsListen only (audio)
Read the transcript
Think of a restaurant menu and its waiter. The menu tells you exactly what you can order. The waiter takes your order to the kitchen and brings back the dish. You never go into the kitchen, and the kitchen can change how it cooks without you noticing, as long as the dishes on the menu stay the same. Software works with APIs in just this way.
An API, or application programming interface, is the defined way one piece of software asks another to do something. The key word is defined. An API is a contract, and the implementation behind it stays hidden and free to change. Some APIs are called inside one program, as ordinary function calls. A web API is called over a network, often between different organisations, such as a shop talking to a payment provider. That difference matters. A network call is far slower, and it can fail in ways a function call cannot. The request can be lost, or the answer can arrive after the caller has given up.
Here's a web API call. A phone app, the client, sends a get request over HTTPS, asking a weather provider for the forecast for Kigali. The request reaches the weather API: the provider's documented contract. Behind it, the forecast data stays internal and hidden. The API answers with a status code, two hundred, okay, and a body of data in a format called JSON. Notice that the client sees only the contract. The provider can change everything behind it, and the app never needs to know.
Most web APIs today follow a style called REST. An API exposes resources, such as a customer or an order, and each has its own URL. The client acts on them with the standard HTTP methods, to fetch, create, replace, change or delete. The same few verbs work for every resource. Status codes tell the client what to do next. Retrying a four hundred error unchanged will fail again, but retrying a five oh three a little later may succeed. And the data usually travels as JSON, a simple text format of names and values, lists, numbers and text, which every mainstream language can read.
Let's follow a travel app that wants tomorrow's forecast. First, it reads the contract: which address to call, how to send its API key, which identifies the app, and a limit of sixty requests a minute. It sends the request over HTTPS, so the key and data are encrypted. Keys like this must never be written into source code. It checks the status. Two hundred means success. Four oh one means the key is wrong, four two nine means slow down, and five oh three means try again later. It reads just the fields it needs from the JSON, such as tomorrow's top temperature and the chance of rain. And the step beginners forget: if no answer comes within about three seconds, it shows the last forecast it saved, rather than a blank screen. Any network call can be slow or fail.
Sometimes a client needs to know when something happens, such as a payment succeeding. It could poll: ask, done yet, again and again. That wastes requests, and it still finds out late. A webhook reverses the direction. The provider sends one message to the shop's own address when the payment succeeds. It's prompt, but the shop must check each call is genuine, and be ready to see the same event twice.
So far, every call has been synchronous: the caller waits for the answer, like a phone call. That's right when you need the answer now, such as, was this card payment approved? The alternative is asynchronous messaging, more like posting a letter. When an order is placed, the order service drops a message into a queue and carries on. The email service picks it up when it's ready. If the email service is down for an hour, messages wait in the queue instead of being lost. The price is that the sender doesn't learn at once whether the work succeeded.
Once an API has users, changing it is delicate. Adding an optional field is usually safe. But renaming or removing one breaks every client that relied on it. So providers version their APIs, keep old versions running for a published period, and announce changes well in advance. Providers also set rate limits, such as a hundred requests a minute. Clients should spread their requests and back off when told to. Third-party APIs save months of work, but their outages become your outages. So hide each provider behind a small interface of your own, and plan for when it's down.
APIs are the contracts between the parts of a system, made concrete. The full section has the complete request and response, and more on keys and tokens. Next, in Section zero point seven, we look at servers and the cloud.
What you need to know
- An API is a contract between pieces of software: what can be requested, with what inputs, and what comes back, while the implementation behind it stays hidden and free to change.
- In-process APIs are function calls inside one program; web APIs cross a network, so they are slower and can fail in ways a function call cannot.
- REST APIs expose resources at URLs, act on them with HTTP methods (GET, POST, PUT, PATCH, DELETE), report outcomes with status codes and usually exchange JSON.
- API keys identify a calling application; access tokens are short-lived credentials, often on behalf of a user; both must travel only over HTTPS and stay out of source code.
- A webhook is the provider calling the client's URL when an event happens; receivers must verify signatures and handle duplicates.
- Synchronous calls wait for an answer; asynchronous messages go through a queue so that sender and receiver need not be available at the same time.
- Breaking changes hurt clients, so APIs are versioned, and rate limits (HTTP 429) protect providers; every third-party API is a dependency to isolate and plan around.
What an API is
An API (application programming interface) is the defined way one piece of software asks another to do something. It lists what can be requested, what must be supplied, what comes back and what can go wrong. The word that matters is defined: an API is a contract. The provider promises to keep it working; the consumer promises to use only what it describes.
A restaurant is the usual analogy. The menu lists what you can order and what each dish contains; the waiter carries your order to the kitchen and brings the result back. You do not walk into the kitchen, and you do not need to know whether the chef uses gas or induction. The kitchen can change its equipment, staff and suppliers at will, as long as the dishes on the menu still arrive as described. The menu is the API, the kitchen is the implementation, and the separation between them is what lets both sides change independently.
This is the same idea as the interfaces in organising code, applied at every scale. It is also why architects care so much about APIs: an API is where one team's work meets another's, and once others depend on it, it is expensive to change.
In-process APIs and web APIs
Some APIs are called inside one running program. When Python code calls json.loads(text), it uses the API of the standard library's json module: a function call that takes nanoseconds and fails only if the input is bad. A library's public functions and classes are its in-process API.
A web API is called over a network, usually with HTTP (see how computers talk). The caller and the provider can be on different machines, written in different languages and owned by different organisations. A mobile banking app talks to the bank's web API; the bank's servers talk to a card network's API; a shop talks to a payment provider's API.
The difference is not cosmetic. A network call is thousands to millions of times slower than a function call, and it can fail in ways a function call cannot: the network drops the request, the server is overloaded, the answer arrives after the caller has given up, or the request succeeded but the response was lost, so the caller cannot tell whether the payment went through. Much of distributed-systems design, covered in Chapter 6, is about living with these failures.
A web API call
REST: resources, URLs and verbs
Most web APIs today follow the style called REST (Representational State Transfer), described by Roy Fielding in his 2000 doctoral dissertation. Fielding defined REST as a set of constraints for large networked systems such as the web; in everyday use, "a REST API" means something simpler: an HTTP API organised around resources.
A resource is a thing the API exposes: a customer, an order, a forecast. Each has a URL, such as /orders/1042. A collection of them also has a URL, such as /orders. The client acts on resources with the standard HTTP methods, reads the result's status code, and exchanges data in the body, usually as JSON. The same few verbs therefore work for every resource, which makes well-designed REST APIs predictable.
One resource, the standard verbs
| Request | Meaning | Typical success status |
|---|---|---|
GET /orders?customer=42 | List orders, filtered by a query parameter. Reads never change anything. | 200 OK |
GET /orders/1042 | Fetch one order. | 200 OK (or 404 Not Found if there is no such order) |
POST /orders | Create a new order from the JSON in the request body. | 201 Created, with the new order's URL |
PUT /orders/1042 | Replace order 1042 with the representation sent. | 200 OK or 204 No Content |
PATCH /orders/1042 | Change some fields only, such as the delivery address. | 200 OK |
DELETE /orders/1042 | Cancel or remove the order. | 204 No Content |
Status codes carry meaning a client program can act on. A 4xx code says the client did something wrong: 400 for a malformed request, 401 when credentials are missing or invalid, 403 when the caller is known but not allowed, 404 when the resource does not exist, 429 when it has sent too many requests. A 5xx code says the server failed: 500 for an unexpected error, 503 when it is temporarily unavailable. The distinction matters because retrying a 4xx request unchanged will fail again, while retrying a 503 a little later may succeed.
Data travels as JSON (JavaScript Object Notation), a text format standardised as RFC 8259. It has only a few building blocks: objects (name-value pairs inside braces), arrays (lists inside brackets), strings in double quotes, numbers, true, false and null. Despite the name, every mainstream language can read and write it, which is why it became the common language of web APIs. Other styles exist and suit different needs: GraphQL lets the client specify exactly which fields it wants, and gRPC uses a compact binary format for fast service-to-service calls; Chapter 6 compares them.
Worked example
Worked example: calling a weather API
A travel app wants tomorrow's forecast for Kigali. The provider (here a made-up api.example.com) documents an endpoint (a URL that accepts a particular kind of request) and issues the app an API key.
| 1. Read the contract | The documentation says: GET /v1/forecast with query parameters city and days; send the key in an Authorization header; responses are JSON; limit 60 requests per minute. |
|---|---|
| 2. Send the request | The app sends the HTTP request shown below, over HTTPS so the key and data are encrypted in transit. |
| 3. Check the status | 200 means the body holds a forecast. 401 means the key is wrong; 429 means slow down; 503 means try again later. |
| 4. Parse the JSON | The app reads days[0].max_c and days[0].rain_chance and ignores fields it does not need. |
| 5. Handle failure | If no answer arrives within, say, 3 seconds, the app shows the last cached forecast rather than a blank screen. |
Every step depends on the contract, not on how the provider computes forecasts. The provider can move to new servers or a new weather model tomorrow without the app changing.
Step 5 is the step beginners forget: any call across a network can be slow or fail, so the caller must decide what to do when it does.
GET /v1/forecast?city=Kigali&days=2 HTTP/1.1
Host: api.example.com
Authorization: Bearer sk_test_4f9a...
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json
{
"city": "Kigali",
"units": "metric",
"days": [
{"date": "2026-10-23", "min_c": 16, "max_c": 27, "rain_chance": 0.6},
{"date": "2026-10-24", "min_c": 16, "max_c": 26, "rain_chance": 0.4}
]
}import os
import requests
API_KEY = os.environ["WEATHER_API_KEY"] # never hard-code secrets
resp = requests.get(
"https://api.example.com/v1/forecast",
params={"city": "Kigali", "days": 2},
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=3, # give up after 3 seconds
)
resp.raise_for_status() # raises an error for 4xx and 5xx
tomorrow = resp.json()["days"][0]
print(tomorrow["max_c"], "degrees, rain chance", tomorrow["rain_chance"])Who is calling? API keys and tokens
Most APIs need to know who is calling, to bill them, limit them and decide what they may see. Two ideas are worth separating from the start. Authentication answers "who are you?"; authorisation answers "what are you allowed to do?"
An API key is a long secret string that identifies the calling application, sent with each request, typically in a header. It is simple, and it identifies a program rather than a person. An access token is a short-lived credential issued after someone signs in, often on behalf of a particular user and with particular permissions ("read this user's calendar, but not their email"); it is usually sent as Authorization: Bearer <token>. Tokens expire, so a stolen one is useful for a limited time. The standard protocols behind them, OAuth 2.0 and OpenID Connect, are covered in identity and access.
Whichever is used, the rules are the same: send credentials only over HTTPS, keep them out of source code and out of mobile apps where anyone can extract them, store them in a secrets manager or environment settings, and be able to revoke and replace them quickly.
Webhooks: the API calls you back
Sometimes the client needs to know when something happens on the provider's side: a payment succeeds, a parcel is delivered, a document is signed. The client could poll, asking "has it happened yet?" every few seconds, which wastes requests and still notices late. A webhook reverses the direction: the client registers a URL of its own, and the provider sends an HTTP POST to that URL when the event occurs. The term was coined by Jeff Lindsay in 2007.
Webhooks bring responsibilities. The receiving endpoint is on the public internet, so it must check that each call really comes from the provider, usually by verifying a cryptographic signature in a header. Providers retry when the receiver does not answer, so the same event can arrive twice and the receiver must recognise duplicates (processing a duplicate must not ship the order twice). And events can arrive out of order.
Waiting for an answer, or not: synchronous and asynchronous
Everything so far has been synchronous: the caller sends a request and waits for the response before continuing, like a phone call. It is simple to reason about, and it is the right choice when the caller genuinely needs the answer now, such as "was this card payment approved?". Its weakness is that the caller is only as available and as fast as everything it is waiting on.
The alternative is asynchronous messaging, more like leaving a letter in a post box. The sender puts a message into a message queue and carries on. The receiver takes messages from the queue when it is ready. If the receiver is slow or down for an hour, messages wait instead of failing. When the shop confirms an order, sending the confirmation email does not need to hold up the customer's screen; a message saying OrderPlaced can go into a queue for the email service to handle a moment later.
Asynchrony is not free. The sender does not learn at once whether the work succeeded, the system becomes harder to trace and test, and messages may be delivered more than once. Deciding which interactions should be synchronous and which asynchronous is one of the central decisions in distributed systems, treated properly in communication and APIs and queues and back pressure.
Decision guide
Decision guide: call now or send a message?
A first-pass guide for one interaction between two parts of a system. Chapter 6 refines it.
- Does the caller need the result before it can answer its own user (for example, was the card approved)?YesUse a synchronous call, with a timeout and a plan for when the other side is slow or down.NoGo to the next question.
- Can the work happen a few seconds or minutes later without anyone noticing (sending an email, updating a report)?YesSend an asynchronous message through a queue, so a slow or failed receiver does not hold up the caller.NoGo to the next question.
- Is the work slow (seconds to hours), such as generating a large export?YesAccept the request at once (for example with 202 Accepted), do the work from a queue, and tell the client when it is done by a webhook or a status URL it can check.NoA simple synchronous call is usually fine; keep it fast and set a timeout.
Living with other people's APIs: SDKs, versions and limits
Many providers publish an SDK (software development kit): a library for each popular language that wraps the HTTP calls in ordinary functions, so a developer writes client.refunds.create(payment_id) instead of building requests by hand. SDKs save time and handle details such as retries and signatures, but they are another dependency to keep up to date.
Once an API has users, changing it becomes delicate. Adding a new optional field to a response is usually safe. Renaming a field, removing one, changing its type or meaning, or making an optional input compulsory is a breaking change: every client that relied on the old behaviour breaks, often in production and often for a provider who cannot see or update those clients (think of mobile apps that users never update). Providers therefore version their APIs, for example /v1/ and /v2/ in the URL or a version header, keep the old version running for a published period, and announce deprecations (notices that a feature will be withdrawn) well in advance. Hyrum Wright's observation, known as Hyrum's law, adds a warning: with enough users, someone will depend on every observable behaviour of an API, including ones the documentation never promised.
Providers also protect themselves with a rate limit: a cap such as 100 requests per minute per key. Requests above the limit are refused with status 429 Too Many Requests, often with a Retry-After header saying when to try again. Clients should respect it, spread their requests, cache answers that do not change often, and back off when told to.
Using third-party APIs: build or buy
Modern applications are assembled as much as written. A food-delivery app might use a payment provider's API to take card payments, a mapping API for addresses and routes, an email or SMS API for receipts, and a push-notification service for "your rider is nearby". Each saves months of specialist work and brings expertise (card security, global map data, email deliverability) that few teams could match.
Each also brings a dependency the team does not control: the provider's outages become your outages, its price changes become your costs, its data-handling becomes part of your compliance story, and leaving it may mean rewriting code. Professionals manage this deliberately. They isolate each provider behind a small interface of their own, so that only one part of the code knows the provider's API; set timeouts and decide what happens when the provider is down; watch the provider's status page and change log; and keep a note of what it would take to switch.
Why this matters for architecture
Architecture is largely about the parts of a system and the contracts between them, and APIs are those contracts made concrete. An API that is easy to use correctly and hard to break lets teams work in parallel and change their internals freely; a leaky or unstable one couples everyone to everyone. The choice between synchronous calls and asynchronous messages shapes how failures spread. Chapter 3 explains the principles behind good interfaces (information hiding and abstraction), and Chapter 6 shows how API and messaging choices make or break distributed systems.
Check your understanding
In the restaurant analogy, what corresponds to the API and what to the implementation?
The menu (and the waiter's way of taking orders) is the API: the agreed list of what can be requested and what comes back. The kitchen is the implementation, free to change as long as the menu's promises are kept.
What is the difference between a 404 and a 503 response, and which is worth retrying later?
404 Not Found says the requested resource does not exist, a client-side problem that retrying will not fix. 503 Service Unavailable says the server is temporarily unable to respond, so retrying after a delay may succeed.
Why must a webhook receiver be ready to see the same event twice?
Providers retry when they do not get a timely success response, even if the first delivery was actually processed, so duplicates are normal. The receiver must recognise an event it has already handled and not act on it again.
Is adding a new optional field to a JSON response normally a breaking change?
Usually not, because well-behaved clients ignore fields they do not recognise. Removing or renaming a field, or changing its type, is breaking.
Sources and further reading
Have a question about this section? ArchitectBot, the course’s AI study assistant, answers from the course first.
Ask: “When should one part of my system call another with a synchronous API request, and when should it send a message through a queue instead?”