Every beginner eventually meets the term REST API, usually in the middle of a tutorial that assumes you already know what it means. This guide explains what a REST API is, what each part of a request and response does, and why the conventions exist — without assuming you have built one before.
Starting with the problem REST solves
Imagine two programs that need to talk. A mobile app needs product data. A website needs to create an order. A payment service needs to tell your server that a charge succeeded. None of these programs share memory, a language, or a machine. They need an agreed way to exchange requests and data over a network.
An API — application programming interface — is that agreement. A REST API is one very common style of the agreement, built on top of the same HTTP protocol your browser already uses. That last point is the key to why it became dominant: no new protocol to learn, and every language, proxy, cache and firewall already understands it.
Resources: the central idea
REST organises everything around resources, which are the nouns of your system: users, products, orders, comments. Each resource has a URL that identifies it:
https://api.example.com/products
https://api.example.com/products/42
https://api.example.com/products/42/reviews
The first is a collection. The second is one item in it. The third is a sub-collection belonging to that item. Notice that none of these URLs contain a verb. There is no /getProducts or /deleteProduct. The action is expressed separately, by the HTTP method.
This separation of noun and verb is the single most characteristic feature of REST, and once you see it the rest of the design follows naturally.
The HTTP methods and what they mean
| Method | Purpose | Example |
|---|---|---|
| GET | Read a resource | GET /products/42 |
| POST | Create a new resource | POST /products |
| PUT | Replace a resource entirely | PUT /products/42 |
| PATCH | Update part of a resource | PATCH /products/42 |
| DELETE | Remove a resource | DELETE /products/42 |
Two properties are worth understanding because they affect real behaviour. A method is safe if it does not change anything — GET is safe, which is why browsers and crawlers can fetch a URL freely. A method is idempotent if calling it five times has the same effect as calling it once. GET, PUT and DELETE are idempotent; POST is not. That is exactly why submitting a form twice can create two orders, and why retry logic must treat POST carefully.
Anatomy of a request
A request has four parts.
The method and URL say what to do and to what. Headers carry metadata: content type, authentication token, accepted formats. Query parameters modify a read without changing the resource identity. The body carries data for create and update operations.
POST /products HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOi...
{
"name": "Wireless Mouse",
"price": 2400,
"inStock": true
}
Query parameters handle filtering, sorting and pagination:
GET /products?category=audio&sort=-price&page=2&limit=20
A useful rule: if the parameter identifies which resource, it belongs in the path. If it modifies how you want the collection returned, it belongs in the query string.
Anatomy of a response
The response carries a status code, headers, and usually a body.
HTTP/1.1 201 Created
Content-Type: application/json
Location: /products/57
{
"id": 57,
"name": "Wireless Mouse",
"price": 2400,
"createdAt": "2026-03-14T09:22:11Z"
}
Status codes fall into ranges, and knowing the ranges is more useful than memorising individual codes.
- 2xx — success. 200 OK for a normal read or update, 201 Created after a POST, 204 No Content when there is nothing to return, typically after a DELETE.
- 3xx — redirection. 301 permanent, 302 temporary, 304 Not Modified for cached content.
- 4xx — the client made a mistake. 400 malformed request, 401 not authenticated, 403 authenticated but not allowed, 404 not found, 409 conflict, 422 validation failed, 429 too many requests.
- 5xx — the server made a mistake. 500 unhandled error, 502 bad gateway, 503 unavailable, 504 upstream timeout.
The 401 versus 403 distinction trips people up constantly. 401 means “I do not know who you are”. 403 means “I know who you are and you may not do this”.
Statelessness
REST requires each request to contain everything the server needs to handle it. The server does not remember that you logged in two requests ago. That is why the token or credentials are attached to every single request.
This sounds wasteful and is in fact the reason REST APIs scale well. Any server in a cluster can handle any request, because none of them holds session state that the others lack. Add a second server behind a load balancer and nothing breaks.
Authentication in practice
Most APIs use one of three approaches. An API key in a header is the simplest and suits server-to-server integrations. A bearer token, commonly a JWT, is standard for user-facing apps: the user logs in once, receives a signed token, and attaches it to subsequent requests. OAuth handles the case where a user grants your app limited access to their account on another service.
Whichever you use, two rules are non-negotiable. Always send credentials over HTTPS, because plain HTTP transmits them readably. And never embed a secret key in frontend JavaScript or a mobile binary — anything shipped to a user’s device is public, no matter how well obfuscated.
Calling a REST API from JavaScript
async function createProduct(product) {
const res = await fetch("https://api.example.com/products", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
},
body: JSON.stringify(product)
});
if (!res.ok) {
const error = await res.json().catch(() => ({}));
throw new Error(error.message || `Request failed: ${res.status}`);
}
return res.json();
}
The res.ok check matters more than it looks. fetch only rejects its promise on a network failure — a 404 or a 500 resolves normally. Code that omits this check treats error pages as valid data and fails in confusing ways later.
What makes an API well designed
Beyond the mechanics, a few conventions separate a pleasant API from a frustrating one.
Plural, consistent nouns. /products everywhere, not /product in one place and /items in another.
Predictable error bodies. Every error should have the same shape, with a machine-readable code and a human-readable message, so clients can handle failures generically.
Pagination on every collection. An endpoint that returns all records works fine with fifty rows and takes down the server at five million.
Versioning from day one. Either in the path (/v1/products) or in a header. Retrofitting versioning after external clients depend on you is painful.
Honest status codes. Returning 200 with {"error": "not found"} in the body defeats every caching layer, monitoring tool and generic client library between you and the user.
REST compared with the alternatives
REST is not the only option and is not always the best one. GraphQL exposes a single endpoint and lets the client specify exactly which fields it wants, which solves over-fetching but adds caching and complexity costs. gRPC uses a binary format and is popular for internal service-to-service communication where performance matters more than human readability. WebSockets handle the case REST handles worst: the server needs to push data to the client without being asked, as in chat or live dashboards.
For a typical web or mobile application talking to its own backend, REST remains the sensible default because the tooling, documentation and developer familiarity are unmatched.
Caching, rate limits and other things you meet in the wild
Two mechanics catch out most people consuming a real API for the first time.
Caching. Because GET requests are safe and idempotent, they can be cached, and servers signal this with headers. Cache-Control states how long a response stays fresh. ETag carries a fingerprint of the content: send it back in an If-None-Match header and the server can reply 304 Not Modified with an empty body, saving the transfer entirely. This is a large part of why REST scales well, and it is another reason to return honest status codes rather than wrapping errors in 200 responses.
Rate limiting. Most public APIs cap how many requests you may make in a window, and exceeding it returns 429. The response usually includes headers describing your remaining quota and when it resets, along with a Retry-After value. Well-behaved client code reads those headers rather than retrying in a tight loop, which only deepens the problem.
A sensible retry strategy uses exponential backoff — wait one second, then two, then four — with a small random jitter so that many clients recovering from the same outage do not all retry simultaneously. Retry on 429 and 5xx responses, never on 4xx, because a malformed request will be just as malformed the second time.
Frequently asked questions
Is every JSON API a REST API?
No, though the terms are used loosely. Many APIs described as REST use only the HTTP methods and JSON, without following the full set of constraints the original definition sets out. In everyday conversation “REST API” has come to mean roughly “an HTTP API organised around resources”, and that is usually what people mean.
What is the difference between PUT and PATCH?
PUT replaces the entire resource with what you send, so omitted fields are typically cleared. PATCH applies only the fields you include. If you send {"price": 3000} to a PUT endpoint, you may well wipe the product name.
Do I need a database to build a REST API?
Not to learn one. You can serve data from an in-memory array or a JSON file and every concept here still applies. Persistence is a separate concern from the API design.
How do I test an API without a frontend?
Use curl from the terminal, or a GUI client like Postman or Insomnia. Both let you set methods, headers and bodies directly, which is the fastest way to confirm whether a problem lies in the API or in your client code.
Conclusion
A REST API is a conversation over HTTP organised around resources, where the URL names the thing and the method names the action. Learn the method semantics, learn the status code ranges, understand why statelessness matters, and always check response status before parsing a body. Those fundamentals carry over to every API you will consume or build, regardless of language or framework.