Foundations · Lesson 01
What an API really is
Strip away the buzzword and an API is one thing: a contract between two pieces of software. One side promises, "ask me like this and I'll answer like that." Everything else in this course is a variation on that single idea.
By the end you'll be able to
- Explain an API as a contract — interface vs implementation — in one sentence.
- Read a real HTTP API call and name every part of it.
- Say why "good" APIs hide their internals, and what breaks when they don't.
The one idea: an interface hides a machine
Think of a vending machine. You see a keypad and a slot. You press B4, money goes in, a snack comes out. You have no idea whether a robotic arm, a spiral coil, or a tiny gnome fetched it — and you don't need to. The keypad is the interface; the mechanism behind it is the implementation. As long as "press B4 → get the B4 snack" stays true, the company can swap the entire internals and you'd never notice.
An API — Application Programming Interface — is exactly this, but the one pressing the buttons is another program instead of a human. It's the keypad that software offers to other software.
A contract has two halves: the request and the response
Every API call is a tiny negotiation. The client makes a request in an agreed shape, and the server returns a response in an agreed shape. Here's a real one — fetching a user from a web API:
# The request — what the client promises to send
GET /v1/users/42 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer abc123
# The response — what the server promises to return
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 42,
"name": "Ada Lovelace",
"created_at": "1843-10-15T00:00:00Z"
}
Read it like a sentence: "Using the GET method, fetch the resource at /v1/users/42; I'll prove who I am with this token, and I'd like the answer as JSON." The server answers: "Status 200 — here it is, as JSON." Those labelled parts (method, path, headers, status, body) are the vocabulary of nearly every web API you'll ever touch. Lesson 02 unpacks them in full.
When an interviewer says "design the API for X," they almost never mean code. They mean: name the resources, the operations on them, the request and response shapes, and the failure cases. Start by stating the contract, not the database. Saying "the interface is the product; the implementation is hidden" up front signals senior thinking.
Why hiding the internals is the whole point
The vending machine could replace its coils with robot arms because customers only ever depended on the keypad. Software gets the same superpower: as long as the contract holds, the team behind the API can rewrite the database, add a cache, move to a new language — and no client has to change. This decoupling is why APIs let huge systems evolve without everyone moving in lockstep. It's also why a well-known, stable contract is worth more than clever internals.
Do design the response around what the client needs ({ "name": "Ada" }). Don't leak your storage layer ({ "usr_tbl_col_nm": "Ada" }). The moment your internal column names appear in the contract, you can never refactor them without breaking callers — the contract has swallowed your implementation.
Where you've already met APIs
The word covers more than web services. They're a family:
| Kind | The "keypad" | Example |
|---|---|---|
| Web / HTTP API | URLs + methods over the network | Stripe's /v1/charges |
| Library API | Functions you import | Math.max() |
| Operating-system API | System calls | open(), read() |
| Hardware API | The CPU instruction set | x86 / ARM opcodes |
All four are the same pattern — a published way to ask for a service while the provider keeps the right to change how it's done. In this course "API" means the web/HTTP kind unless we say otherwise, because that's what interviews focus on.
Confusing the API with the server. The server is a running machine; the API is the promise it exposes. Two completely different servers can offer the identical API (that's how load balancers and gateways work), and one server can expose several APIs at once. Keep "the contract" and "the thing running the contract" separate in your head.
Under the hood: how an HTTP API call actually works
When your code calls fetch("https://api.example.com/v1/users/42"), a precise sequence of events unfolds before the response arrives. Here is every step, end to end, at the wire level.
- DNS resolution. The name
api.example.commust be converted to an IP address. Your OS checks its local cache; if it misses, it queries a recursive resolver, which walks the DNS hierarchy and returns e.g.93.184.216.34. - TCP connect. The kernel opens a socket and sends a SYN to
93.184.216.34:443. The server replies with SYN-ACK; the client completes the three-way handshake with ACK. One round trip spent purely on setup. - TLS handshake. For HTTPS, client and server negotiate a cipher suite, exchange certificates, and derive a shared session key — roughly one more round trip before any plaintext is exchanged.
- HTTP request on the wire. The client sends a UTF-8 text stream over the TLS-encrypted TCP connection. The request line, headers, blank line, and body are sent as raw bytes.
- Server processes. The server's application code reads the request, queries its data store, and serialises a response.
- HTTP response on the wire. The server sends the status line, response headers, a blank line, and the response body back down the same TCP connection.
- Connection reuse or close. With
Connection: keep-alive(the HTTP/1.1 default), the TCP socket stays open for the next request. Without it, aFIN/FIN-ACKfour-way teardown happens.
Concrete traced example — the exact bytes that cross the wire for GET /v1/users/42:
━━━ REQUEST (client → server, after TLS) ━━━━━━━━━━━━━━━━━━━━━━━━━
GET /v1/users/42 HTTP/1.1 ← request line: method · path · version
Host: api.example.com ← required in HTTP/1.1; tells a shared server which vhost
Accept: application/json ← content negotiation: "I want JSON back"
Authorization: Bearer abc123 ← credential; never in the URL (ends up in logs)
User-Agent: MyApp/2.1 ← who's calling (optional but courteous)
← blank line signals end of headers; GET has no body
━━━ RESPONSE (server → client) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
HTTP/1.1 200 OK ← status line: version · code · reason phrase
Content-Type: application/json ← tells client how to parse the body
Content-Length: 67 ← body size in bytes
Cache-Control: max-age=60 ← client may cache this response for 60 s
Connection: keep-alive ← keep the TCP socket open for the next request
← blank line; body follows
{
"id": 42,
"name": "Ada Lovelace",
"created_at": "1843-10-15T00:00:00Z"
}
The sequence below shows every phase as a timing swimlane, so you can see which phases can be eliminated by keep-alive or a CDN:
How to debug & inspect it
The single most useful tool for seeing the raw HTTP exchange is curl -v. It prints every phase — DNS, TLS, request headers, response headers, and body — with clear labels. Read the > lines (sent) and < lines (received):
To see per-phase timing in one line, use curl -w with a format string. This is exactly how you measure DNS, connection, TLS, and transfer time separately:
A symptom-to-cause lookup for the most common HTTP-level errors you'll hit when inspecting raw calls:
| Symptom | Likely cause | Fix / next step |
|---|---|---|
curl: (6) Could not resolve host | DNS lookup failed — wrong hostname, DNS outage, or misconfigured /etc/resolv.conf | Try dig api.example.com to isolate DNS; check the hostname spelling |
curl: (7) Failed to connect | TCP connect refused — server not listening on that port, or a firewall drops the SYN | Check the port (ss -lntp on the server); test with nc -zv host port |
curl: (35) SSL connect error | TLS handshake failed — expired cert, mismatched SNI, or cipher mismatch | Add -v to see the TLS alert; try openssl s_client -connect host:443 |
400 Bad Request | Malformed request line or a required header is missing | Inspect raw request with -v; check Host header is present and correct |
401 Unauthorized | Missing or expired Authorization header, or wrong credentials | Check the token is in the Authorization: Bearer … header, not the URL |
404 Not Found | The path doesn't match any route on the server | Check path capitalisation and trailing slash; compare against the API docs |
| Response body is empty / truncated | Content-Length mismatch, or server closed the connection early | Run with --verbose --trace-ascii /tmp/dump to see raw bytes |
Inspection checklist:
- Start with
curl -v URLto see the raw request and response headers. - If it fails before the request, add
--resolveto bypass DNS or-kto skip TLS verification (for diagnosing only — never in production). - Use
-w "…"timing to identify which phase is slow (DNS, TCP, TLS, TTFB, or transfer). - Compare the
>request headers against the API's expected contract — missingHost, wrongContent-Type, or noAuthorizationare the most common culprits. - Check the
<response status code andContent-Typefirst; they immediately tell you whether the problem is routing (404/405), auth (401/403), or logic (400/422/500).
🧠 Quick check
1. The clearest one-sentence definition of an API is:
An API is the agreed interface — the request/response contract — independent of any particular server or storage behind it.
2. A team rewrites their service from Python to Go but keeps every endpoint, request, and response identical. What happens to existing clients?
Clients depend on the contract, not the implementation. If the interface is identical, the language swap is invisible to them. That decoupling is the core benefit of APIs.
3. Which response design will hurt you most over time?
Leaking internal names couples every client to your storage layer, so you can never refactor it without a breaking change. The contract has absorbed your implementation details.
✍️ Drill: define an API for a coffee machine (try before opening)
An interviewer asks you to "design an API to operate a networked coffee machine." Sketch the contract — resources, operations, one request and one response — before reading on.
A solid answer names the contract, not the wiring:
# Resource: a brew job
POST /v1/brews # start a brew
→ body: { "drink": "latte", "size": "M", "sugar": 0 }
← 202 Accepted { "id": "brew_88", "status": "brewing" }
GET /v1/brews/brew_88 # check progress
← 200 { "status": "ready" }
Rubric: ✓ models a resource (brews) ✓ picks correct methods (POST creates, GET reads) ✓ shows request and response shapes ✓ uses an async status because brewing takes time ✓ never mentions the machine's internal hardware. Hitting all five = a strong "intro" answer.
Key takeaways
- An API is a contract: ask in an agreed shape, get an agreed shape back.
- Interface ≠ implementation. Clients depend only on the contract; the internals stay free to change.
- A web API call has a fixed vocabulary: method, path, headers, status, body.
- Good contracts model what the client needs and hide storage/internal details.
- "API" is a family — web, library, OS, hardware — all the same pattern.
Sources & further reading
These are starting points to go deeper — all original explanation above, grounded in public references:
- MDN — API (glossary)
- Roy Fielding — Architectural Styles and the Design of Network-based Software Architectures (the REST dissertation)
- OpenAPI Specification — the standard way to write an API contract down