Open an API for the first time and you can tell within five minutes whether somebody cared about the person using it. Clear names, a helpful error message, a request that just works: it feels like a good menu in a good restaurant. A confusing one, with half the options missing and no price list, makes you want to leave.
That difference has a name: API design. This guide gives you 12 plain-English rules for getting it right. They apply whether you are building an API for your own app, for customers, or to publish on a marketplace.
If you are brand new to the idea, an API is simply a way for one program to ask another program for something. An endpoint is one specific thing it can ask for, at its own web address. For more background, read what a REST API is first, then come back.
The 12 API design principles at a glance#
- Design for the person using it
- Name things with nouns, and keep addresses readable
- Use HTTP methods for what they mean
- Return the right status code
- Make errors helpful
- Be consistent everywhere
- Make lists safe: paginate, filter and sort
- Version from day one, and never break users
- Make requests safe to retry
- Be secure by default
- Tell people your limits
- Document with real examples
Now the detail for each one.
1. Design for the person using it#
The best API designers think like a customer, not like an engineer. Your database might have a table called usr_acct_tbl, but nobody wants to see that in a response.
Ask these questions before you write any code:
- Who will use this? A beginner copying an example? A team that integrates it into a billing system?
- What are they trying to get done? Design around the job, not around your internal structure.
- What is the first thing they will try? Make that work in one request.
A useful test is the "first five minutes" test. Give your API to someone who has never seen it. If they cannot make a successful request within five minutes using only your docs, the design needs work.
Many teams also use a design-first approach: write down the endpoints and example responses (often in an OpenAPI file) and show them to users before building. Changing a document is cheap. Changing a live API is not.
2. Name things with nouns, and keep addresses readable#
In a REST API, the web address names a thing, and the HTTP method says what to do with it. So addresses should be nouns, not verbs.
| Avoid | Better |
|---|---|
/getAllOrders | GET /orders |
/createOrder | POST /orders |
/deleteOrder?id=42 | DELETE /orders/42 |
/user_get_by_id/42 | GET /users/42 |
A few simple naming rules:
- Use plural nouns for collections:
/orders,/users. - Nest to show ownership, but only one or two levels:
/users/42/orders. - Pick one style and stick with it. Lower-case words separated by hyphens (
/order-items) are common for addresses, and one consistent style inside your JSON. - Make addresses readable.
/apis/weather/endpoints/current-weathertells a human, and a search engine, what the page is about. A long random ID does not. - Allow the odd action when needed. Some things do not fit a method, like cancelling an order.
POST /orders/42/cancelis fine, as long as it is the exception and not the habit.
3. Use HTTP methods for what they mean#
Every web request starts with a method, and each one promises something. Following those promises means developers can guess how your API behaves without reading every page.
| Method | Meaning | Safe to repeat? |
|---|---|---|
GET | Read something | Yes |
POST | Create something | No |
PUT | Replace something completely | Yes |
PATCH | Change part of something | Not by default |
DELETE | Remove something | Yes |
The two mistakes we see most often are using GET to change data (a search engine or a link preview can then trigger it by accident) and using POST for everything. Our full guide to API request methods explains each one with examples.
The official definitions live in RFC 9110, the HTTP semantics standard. You do not need to read it, but it is the reference when you are unsure.
4. Return the right status code#
The status code is the first thing a program reads. It decides what happens next: show the data, ask the user to log in, try again later.
- 2xx means success.
200 OKfor a normal read,201 Createdwhen you made something,204 No Contentwhen it worked and there is nothing to return. - 4xx means the caller made a mistake.
400for a bad request,401for "you are not logged in",403for "you are logged in but not allowed",404for "not found",409for a conflict,422for data that is well-formed but not valid, and429for too many requests. - 5xx means we made a mistake.
500for an unexpected failure,503when you are temporarily unavailable.
The classic mistake is returning 200 OK with {"error": "something went wrong"} inside. It breaks every tool that trusts the status code, including monitoring, retries and caches. If it failed, say so with the status code.
5. Make errors helpful#
When something goes wrong, your error message is the only help the developer gets. A good error answers three questions: what happened, why, and what to do about it.
Compare these two:
{ "error": "Invalid request" }{
"type": "https://docs.example.com/errors/invalid-email",
"title": "Your request parameters didn't validate.",
"status": 422,
"detail": "The 'email' field must be a valid email address, but 'john@gmial' was given.",
"instance": "/signups/7f3a",
"errors": [{ "field": "email", "message": "Not a valid email address" }]
}The second one follows RFC 9457 "Problem Details for HTTP APIs" (published in 2023, replacing the older RFC 7807). It is a standard error format, sent with the content type application/problem+json, so you do not have to invent your own. Tools and developers already know how to read it.
Tips for good errors:
- Use a stable
type: a code or a docs link that never changes, so programs can react to it. - Write
detailfor a human. Say which field and what is wrong. - Never leak internals: no stack traces, no database names, no file paths.
- Include a request ID so you can find the problem in your logs when a customer reports it.
6. Be consistent everywhere#
Consistency is what makes an API feel designed instead of grown. When one thing works a certain way, everything else should too. Developers learn your patterns once, then guess the rest correctly.
Check these:
- Naming style in JSON: pick
snake_caseorcamelCaseand never mix them. - Dates and times: use ISO 8601 in UTC, like
2026-10-15T09:30:00Z, everywhere. - Money: send the amount and the currency together, and decide whether you use whole units or the smallest unit (cents, paise) and never switch.
- IDs: always the same field name and type.
- Empty values: decide how you show "nothing" (an empty list,
null, or leaving the field out) and use it everywhere. - Response shape: if one list endpoint wraps results in
{"data": [...]}, they all should.
A small habit helps a lot: keep a one-page style guide, and check every new endpoint against it before release.
7. Make lists safe: paginate, filter and sort#
If an endpoint returns a list, plan for that list growing. An endpoint that returns 50 items today might return 5 million next year, and a single huge response slows your server and crashes your users' apps.
Always paginate. There are two common ways:
- Page and size (
?page=3&limit=50): simple, and good for small data. - Cursor-based (
?cursor=abc123&limit=50): better for big or fast-changing data, because it does not skip or repeat items when new records arrive.
{
"data": [{ "id": "ord_1" }, { "id": "ord_2" }],
"next_cursor": "eyJpZCI6Im9yZF8yIn0",
"has_more": true
}Set a sensible default and a maximum page size (for example, 50 by default and never more than 100), so no one can ask for everything at once.
Give users filters and sorting with plain parameters: ?status=paid&sort=-created_at (the minus meaning newest first). It stops people downloading everything just to find one item.
8. Version from day one, and never break users#
This is the most important rule on the list. People build their products on yours. If you rename a field, change a type or remove an endpoint, you can break their app overnight, and you may never hear why they left.
Breaking changes (never do these inside an existing version):
- Removing or renaming a field or endpoint
- Changing a field's type or meaning
- Making an optional input required
- Changing error codes people rely on
Safe changes (fine at any time):
- Adding a new endpoint
- Adding a new optional field to a response
- Adding a new optional input
A few practical rules:
- Put the version in the address, such as
/v1/orders. It is easy to see, test and cache. - Release
/v2only for breaking changes, and keep/v1running for a stated period. - Warn early. The Deprecation response header (RFC 9745, published in 2025) tells programs a resource is being retired, and a Sunset header (RFC 8594) can carry the date it stops working. Also tell people by email and in the docs.
- Tell clients to ignore what they do not know, so adding fields never breaks them.
9. Make requests safe to retry#
Networks fail. A customer clicks "Pay", the connection drops, and their app does not know whether the payment went through. So it tries again. Without protection, you have charged them twice.
Some methods are idempotent by nature, which means repeating them gives the same result: GET, PUT and DELETE. POST is not. The fix is an idempotency key: the client sends a unique ID with the request, and your server remembers it.
await fetch("https://api.example.com/v1/payments", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(), // reuse the SAME key when retrying
},
body: JSON.stringify({ amount: 4999, currency: "INR" }),
});If the server sees the same key twice, it returns the original result instead of doing the job again. Payment APIs have used this pattern for years. A standard Idempotency-Key header is being worked out at the IETF, but it is still a draft, so document exactly how your version behaves.
10. Be secure by default#
Security is not a feature you add at the end. Build these in from the start:
- HTTPS only. Never accept plain HTTP.
- Authenticate every request with an API key, a token or OAuth, depending on who is calling.
- Check permission on every record. This is the big one. The OWASP API Security Top 10 (2023) lists broken object level authorization as the number one API risk. In plain words: a user logs in, then changes
/orders/1042to/orders/1043and sees someone else's order. Your server must check, every time, that this user is allowed to see this exact record. Random IDs make guessing harder, but they are not a substitute for the check. - Give each key the minimum it needs (a read-only key for read-only jobs).
- Validate all input: types, lengths and allowed values.
- Return only what is needed. Do not send back a whole user record when the caller needs a name.
- Never put secrets in the address. Web addresses end up in logs. Send keys in a header.
11. Tell people your limits#
Every API has limits, because without them one customer can slow everyone else down. The kind thing to do is to make those limits visible and predictable.
- Return
429 Too Many Requestswhen someone goes over. - Add a
Retry-Afterheader saying how many seconds to wait. - Many APIs also send headers showing the limit, the number left and when it resets. These
RateLimitheaders are still being standardised, so describe yours clearly in the docs. - Write the limits down: requests per minute, daily or monthly quota, maximum body size and timeout.
On APIHiver, each plan shows its request quota on the pricing page, for exactly this reason: nobody should find out their limit by hitting it.
12. Document with real examples#
An API without good documentation is, in practice, an API without users. Developers skim. They look for one working example they can copy.
Great docs have:
- A quick start that gets a first successful request in minutes
- Every endpoint with its method, address, inputs, outputs and possible errors
- Real example requests and responses, not placeholders like
string - Code in the languages your users use (at least JavaScript and Python, plus a plain
curlexample) - A changelog, so people see what changed and when
- A place to try it live without writing code
A machine-readable description such as an OpenAPI file is worth having too: it can generate docs, test tools and client libraries from one source of truth.
A quick design review in 30 minutes#
Before you release a new endpoint, run it past this list:
- The address is a clear noun and reads well
- The right HTTP method is used
- Success and failure return the right status codes
- Errors say what happened and how to fix it
- Names, dates and money match the rest of the API
- Lists are paginated with a maximum size
- It sits under a version, and nothing existing was broken
- Creating things is safe to retry
- It requires authentication and checks permission on each record
- Limits are enforced and documented
- There is at least one real example in the docs
Common mistakes to avoid#
- Leaking your database into the API. Customers should not see your internal table names or structure.
- Returning 200 for errors. It breaks monitoring, retries and caching.
- Changing things without a version. The fastest way to lose users.
- Unbounded lists. They work in testing and fail at scale.
- Vague errors. "Something went wrong" costs hours of support time.
- Forgetting the second check. A valid login is not permission to see every record.
- Documenting last. If docs are an afterthought, users feel it.
Summary#
- Design for the user. Think about the first five minutes, not your database.
- Nouns for addresses, the right method for the action, the right status code for the result.
- Errors should explain. Use the RFC 9457 Problem Details format.
- Stay consistent, paginate every list, and version from the start.
- Never break existing users. Add freely, change only through a new version with warning.
- Make retries safe with idempotency keys, secure every record (not just the login), and publish your limits.
- Document with real examples, and run every new endpoint through the review checklist.
Next, put these rules to work with our guides to API request methods, REST API vs SOAP API and what a REST API is. If you run an API in production, our guide to production habits for image APIs shows these ideas (retries, limits, fallbacks) in practice. Building for AI agents? See MCP vs CLI vs Skills: Live Web Data for AI Agents, and for clean, predictable data see 6 International Standard Formats Every API Should Use. To see how to judge an API's limits and pricing before you depend on it, read our GS1-128 barcode reader API comparison.