UTILYARD
guides

What is an API?

What APIs are, how REST works, what HTTP methods mean, and how to read and make a basic API request.

What is an API?

An API (Application Programming Interface) is a defined way for one piece of software to talk to another. It specifies what requests you can make, what parameters they accept, and what responses you'll receive — without you needing to know anything about the internal implementation.

The restaurant analogy is overused but accurate: you tell the waiter (API) what you want, the kitchen (server) prepares it, and the waiter brings it back. You don't need to know how the kitchen works — just what's on the menu (the API specification).

APIs appear at every level of software: the OS provides APIs for file I/O, browsers expose JavaScript APIs, third-party services like Stripe and Twilio expose web APIs. When developers say "API" without qualification, they usually mean a REST API over HTTP.

REST — how most web APIs work

REST (Representational State Transfer) is an architectural style for web APIs. A REST API organizes data into resources — nouns like /users, /orders, /products — and uses HTTP methods as verbs to act on them.

GET    /users          → list all users
GET    /users/42       → get user with id 42
POST   /users          → create a new user
PUT    /users/42       → replace user 42 entirely
PATCH  /users/42       → update specific fields of user 42
DELETE /users/42       → delete user 42

REST is stateless — each request contains all the information needed to process it. The server doesn't remember anything about previous requests from the same client.

HTTP methods

MethodActionNotes
GETReadNo body. Idempotent — calling it N times has the same result as calling it once. Should never modify data.
POSTCreateBody contains new data. Not idempotent — calling it twice creates two records.
PUTReplaceBody is the full new representation. Idempotent — replacing with the same data twice has the same result.
PATCHUpdateBody contains only changed fields. Not always idempotent depending on implementation.
DELETEDeleteIdempotent — deleting what's already gone has the same result.
HEADRead headersLike GET but returns only headers, no body. Used to check if a resource exists.
OPTIONSCapabilitiesReturns what methods a resource supports. Used in CORS preflight.

A complete API request and response

// Request
POST https://api.example.com/users
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

{
  "name": "Ada Lovelace",
  "email": "ada@example.com",
  "role": "admin"
}

// Response (201 Created)
HTTP/1.1 201 Created
Content-Type: application/json
Location: https://api.example.com/users/8312

{
  "id": 8312,
  "name": "Ada Lovelace",
  "email": "ada@example.com",
  "role": "admin",
  "createdAt": "2024-11-01T14:22:10Z"
}

HTTP status codes

Status codes in the response tell you what happened. They're grouped by the first digit:

2xx — Success:200 OK, 201 Created, 204 No Content
3xx — Redirect:301 Moved Permanently, 302 Found, 304 Not Modified
4xx — Client Error:400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 422 Unprocessable Entity, 429 Too Many Requests
5xx — Server Error:500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable

Making API requests in code

// JavaScript — fetch API
const response = await fetch('https://api.example.com/users/42', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer ' + token,
    'Content-Type': 'application/json',
  },
})

if (!response.ok) {
  throw new Error('Request failed: ' + response.status)
}

const user = await response.json()
console.log(user.name)

// POST example
const newUser = await fetch('https://api.example.com/users', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token },
  body: JSON.stringify({ name: 'Ada', email: 'ada@example.com' }),
}).then(r => r.json())

Idempotency: why retrying POST requests can be dangerous

GET requests are safe to retry — the server doesn't change anything. POST requests are not. If a network error occurs after your POST reaches the server but before the response comes back, you don't know whether the operation succeeded. Retrying blindly can create two records, charge a card twice, or send a duplicate email.

The solution is an idempotency key — a unique ID generated client-side and attached to the request. If the server receives two requests with the same key, it returns the result of the first one instead of executing the operation again. Stripe, PayPal, and most payment APIs require idempotency keys for charge endpoints precisely because double-charging is catastrophic.

// Generate a key before the request — persist it across retries
const idempotencyKey = crypto.randomUUID()

await fetch('/api/payments', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Idempotency-Key': idempotencyKey,  // server deduplicates on this
  },
  body: JSON.stringify({ amount: 5000, currency: 'usd' }),
})

Most consumer-facing REST APIs don't implement idempotency keys, which means client code should avoid auto-retrying POST requests without careful thought. For any operation with real-world consequences — charges, emails, account creation — either implement idempotency keys server-side or require explicit user confirmation before retrying.

Frequently asked questions

What is the difference between REST and GraphQL?
REST uses multiple endpoints, one per resource type (GET /users, GET /orders). GraphQL uses a single endpoint — clients send queries specifying exactly the fields they need, potentially fetching related data in one request. REST is simpler and more cacheable. GraphQL reduces over-fetching and under-fetching but adds complexity. REST is more common; GraphQL is popular for client-driven apps with complex data requirements.
What is an API key?
An API key is a secret token that identifies your application to an API. You include it in every request (usually in a header or query string) so the API can authenticate you, enforce rate limits, and bill you. API keys should be kept secret — never commit them to version control. Use environment variables instead.
What is CORS and why does it block my API calls?
CORS (Cross-Origin Resource Sharing) is a browser security policy that prevents a page on one domain from making API requests to a different domain without explicit permission. If your frontend at app.com tries to call api.otherdomain.com, the browser blocks it unless api.otherdomain.com includes CORS headers allowing app.com. The fix is on the server: add the appropriate Access-Control-Allow-Origin headers.
What is an API? — UtilYard