What is an API? How to use APIs well: from the concept to authentication, retries and cost control

APIs explained with everyday examples, then 8 principles for using them in production: timeouts, retry with backoff, idempotency keys, pagination, webhooks and monitoring, with working sample code.
Almost everything you use every day runs on APIs: a payment app calling a bank, a web shop calling a shipping carrier, a chatbot calling an AI model. Understanding what an API is helps you talk precisely with your engineering team, and knowing how to use APIs well makes systems faster, cheaper and less incident-prone.
This article goes from the concept to the principles of using APIs in production: safe authentication, status codes, pagination, rate limits, retries, idempotency keys, webhooks and monitoring.
The standards mentioned (RFC 9110, RFC 6585, RFC 9457, RFC 6749) are IETF documents, the idempotency key example rests on Stripe's documentation, and the ordering of priorities is Click Star's experience.
Key points
- An API (application programming interface) is a contract that says what one piece of software can ask another to do, in what format, and what it gets back. Most web APIs run over HTTP and exchange JSON data.
- The two most important pieces of each call are the method (GET, POST...) and the status code (2xx success, 4xx caller's error, 5xx server's error).
- Using an API well means: set a timeout, retry only the right kinds of errors with growing backoff, use idempotency keys for writes, paginate with cursors, cut calls with caching, and receive webhooks instead of polling.
- An API key is a secret. Keep it server-side only, grant least privilege, separate it per environment, and revoke it at once if you suspect it leaked.
- You need to measure: latency, error rate, cost and a request ID for each call, to know whether the system is healthy.

What an API is
API stands for Application Programming Interface. A simple picture: you sit in a restaurant, the menu lists what you may order, and the waiter takes your request to the kitchen and brings the dish back. You do not need to know how the kitchen cooks. An API is a piece of software's menu and waiter: it tells you what you may ask, how to ask, and what it promises to return.
There are many kinds of API (of an operating system, of a programming library), but when business people say "use the API" they usually mean a web API: you send an HTTP request to an address (an endpoint) and get a response, usually JSON. A call has 4 parts: the address and method (do what, to what), headers (who is calling, in what format), the body (the data you send, if any) and the response (a status code plus returned content).
# Fetch order number 1042
curl https://api.example.com/v1/orders/1042 \
-H "Authorization: Bearer $API_KEY" \
-H "Accept: application/json"
# Response: status code 200 and JSON content
# { "id": 1042, "status": "paid", "total": 150000, "currency": "VND" }
Concepts you need
HTTP methods and what "safe" and "idempotent" mean
RFC 9110 (HTTP semantics) defines the methods. GET reads data and is a safe method that changes nothing. PUT replaces a whole resource and DELETE removes it; both are idempotent, meaning that calling twice leaves the same end result as calling once. POST is usually for creating and is not idempotent: calling twice can create 2 records.
PATCH (defined in RFC 5789) edits part of a resource and is also not guaranteed to be idempotent. This is exactly what decides whether you may retry automatically, as the sections below explain.
Status codes
A status code tells you how far the call got. The 2xx group is success (200 success, 201 created, 204 success with no content). The 3xx group is redirection or "not changed" (304).
The 4xx group is the caller's error: 400 bad request, 401 not authenticated, 403 not permitted, 404 not found, 409 conflict, 422 invalid data, 429 too many requests (RFC 6585). The 5xx group is the server's error: 500 general error, 502 and 503 an intermediary or overloaded service.
The rule of use: a 4xx error (except 429) is your error, so retrying unchanged will fail again and you should fix the request. A 429 and 5xx are often temporary and can be retried in a controlled way.

Webhooks, GraphQL, gRPC
A webhook reverses the call: instead of you asking again and again "is the order paid yet", the provider calls your address when an event happens. GraphQL lets you pick exactly the fields you need in one call, and gRPC uses an efficient binary protocol for communication between internal services. REST over HTTP and JSON is still the most common choice for public APIs.
Authentication and security
- API key: simple and suitable for calls from your own server. Never embed a key in code that runs in a browser or mobile app, and never commit it to a repository. Use environment variables or a secrets store, separate keys by environment (test and live), and choose the narrowest-permission key you can.
- OAuth 2.0 (RFC 6749): use it when your app acts on behalf of a user, for example reading their calendar or email, without holding their password. The access token is usually sent as a bearer token (RFC 6750) in the Authorization header and is short-lived.
- Always use HTTPS, validate and sanitize input, and never write keys or access tokens to logs.
- Treat any key that ever appeared in public source code as leaked: revoke it and issue a new one, rather than just deleting the line that contains it from the repository.
- For an API you provide yourself: set rate limits per key, check permissions on each resource, and return errors with enough information for callers to fix their request but without exposing internal details.
Using APIs well: 8 principles
1. Read the limits before writing code
Every API has limits on requests per minute, data size, records per page and supported versions. Knowing the limits in advance lets you design correctly from the start instead of patching after an incident.
2. Set a timeout on every call
A call with no timeout can hang forever and drag your whole system down. Set a concrete timeout (for example 10 seconds for an ordinary request) and decide in advance what to do when it expires.
3. Retry the right way
Retry only temporary errors: network errors, timeouts, 429 and 5xx. Do not retry 400, 401, 403 or 404. Space the attempts exponentially (0.5 seconds, 1 second, 2 seconds...) and add some randomness so thousands of clients do not all call again at the same moment. If the response has a Retry-After header (RFC 9110), wait exactly that long.
Always cap the number of attempts. The code below packs these rules together:
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
const backoff = (n) => Math.min(30_000, 500 * 2 ** n) * (0.5 + Math.random() / 2)
async function callApi(url, options = {}, { retries = 4, timeoutMs = 10_000 } = {}) {
for (let attempt = 0; ; attempt++) {
let res
try {
res = await fetch(url, { ...options, signal: AbortSignal.timeout(timeoutMs) })
} catch (err) {
// network error or timeout
if (attempt >= retries) throw err
await sleep(backoff(attempt))
continue
}
if (res.ok) return res.json()
const retryable = res.status === 429 || res.status >= 500
if (!retryable || attempt >= retries) throw new Error(`HTTP ${res.status}`)
// Retry-After is a number of seconds (it can also be an HTTP date, in which case Number gives NaN and we use backoff)
const retryAfter = Number(res.headers.get('retry-after'))
await sleep(retryAfter > 0 ? retryAfter * 1000 : backoff(attempt))
}
}

4. Use idempotency keys for writes
Retrying a POST call that creates a payment can charge twice if the first call actually succeeded but the response was lost. The fix is an idempotency key: the client generates one unique value per logical operation and sends it along, and the server recognizes the retry and returns the first result instead of doing it again.
According to Stripe's documentation, the key goes in the Idempotency-Key header, is at most 255 characters, should be a V4 UUID or another sufficiently random string, and should not use sensitive data such as an email as the key.
In Stripe's API v1, every POST request accepts a key, keys may be removed after at least 24 hours, and if you send the same key with different parameters the system returns an error to prevent misuse. GET and DELETE are idempotent by definition and need no key.
The Idempotency-Key header is also being drafted into a standard by the IETF httpapi group; in the latest version Click Star could find it is still an Internet-Draft with no RFC number, so read each provider's own documentation.
// Generate the key ONCE per operation, and reuse that exact key on every attempt
await callApi('https://api.example.com/v1/payments', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.API_KEY}`,
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({ amount: 150000, currency: 'VND' }),
})
The callApi function above reuses the same options object for every attempt, so the key stays the same across attempts.
5. Paginate with cursors
Long lists come back in pages. Offset pagination can skip or repeat records when data changes between 2 calls. Cursor pagination uses a marker returned by the server and is more stable. Loop until there is no next page, and set a cap on the number of pages to avoid an endless loop.
6. Cut the number of calls
- Filter and select fields in the request instead of pulling everything and filtering on your side.
- Batch several operations into one call if the API supports it.
- Use HTTP caching: with an ETag and the If-None-Match header, the server can return 304 (not modified) without resending content (RFC 9110 and RFC 9111). For data that rarely changes, store the result on your side with a clear expiry.
- Avoid calling inside a per-record loop when you can call once for the whole list.
7. Use webhooks instead of polling
Polling burns calls and is still slow. When the provider supports webhooks, receive events: verify the signature to be sure the request came from them, return a 2xx quickly and process in the background, and design so that handling an event delivered twice still gives the right result, because webhooks are usually resent when you do not respond in time.
8. Observe the system
Record the request ID of each call to cross-check with the provider during an incident. Track latency, error rate by status code and monthly cost. Set alerts when the rate of 429 or 5xx rises, and record the API version you use so you know when to upgrade.
When you are the one providing the API
- Name resources with plural nouns (
/orders), use HTTP methods for what they mean, and put a version in the path (/v1/). - Return errors consistently in the Problem Details format of RFC 9457 (content type
application/problem+json): an error type, a title, a status code and a description so callers can handle it by machine. - Return limit headers and Retry-After with a 429 so clients know when to call again.
- Describe the API with the OpenAPI Specification to generate documentation, client code and automated contract tests, keeping documentation and real behavior in step.
- Support idempotency keys for create and payment operations.
APIs in the AI era: a few specifics
APIs of language models follow the same principles, with a few specifics. Limits are often counted in both requests and tokens, so track both. Responses can be long and slow, so set a wider timeout or use streaming.
Cost scales with tokens, so prompt caching, choosing a small model for simple work and capping output length save more than any other tip (see also the article on using GPT well). An AI service's API key should have a monthly spending limit, and do not write prompt content that contains personal data into logs.
A process for integrating a new API
- Read the provider's documentation, limits, terms of use and data policy.
- Get a key for the test environment and keep it in an environment variable or a secrets store.
- Try each call with curl or a tool such as Postman before writing code.
- Write a thin wrapper around the API: timeout, retry, idempotency key, logging, and translating errors into your own error type.
- Write tests with mock responses for success, 4xx, 429 and 5xx.
- Add monitoring: latency, error rate, cost and alerts.
- Run it on a preview environment with test data, and only then switch on the live key.
- Schedule reviews: periodic key rotation, version deprecation notices, cost limits.
6 common mistakes
- Putting the API key in browser-run code or committing it to a repository.
- Retrying forever with no backoff, overloading the server exactly when it is in trouble.
- Retrying a write without an idempotency key, causing duplicate orders or payments.
- Ignoring a 429 and the Retry-After header and calling on until you get blocked.
- Setting no timeout, so one slow service freezes the whole system.
- Not tracking cost and error rate, so you only learn of a problem when a customer reports it.
Related posts
Need tailored advice for your business?
Click Star helps you build a solution that fits your industry and scale. We answer every question during business hours.



