REST API Consuming APIs with Code

Testing tools help you explore an API. Real apps call APIs from code. A weather widget fetches forecasts, a shop page loads products, and a script saves reports. Writing these calls well means handling success, failure, and slow networks.

The Phone Call Picture

Calling a friend involves dialing, waiting for an answer, speaking, listening, and hanging up. Sometimes the line is busy or nobody picks up. Code that calls an API follows the same steps. It builds the request, sends it, waits, reads the reply, and plans for the busy signal.

The Life of a Call

Build request ──→ Send ──→ Wait ──→ Receive response
                              │              │
                         timeout?       status code?
                              │              │
                        retry or fail   2xx: read JSON
                                        4xx: fix the request
                                        5xx: retry later

GET Request in JavaScript

The fetch function ships with modern browsers and Node.js.

async function loadBook(id) {
  const response = await fetch("https://api.example.com/books/" + id);
  if (!response.ok) {
    throw new Error("Request failed with status " + response.status);
  }
  const book = await response.json();
  return book;
}

Fetch only rejects its promise on network failure. A 404 or 500 status still counts as a completed call, so the code must check response.ok.

POST Request in JavaScript

const response = await fetch("https://api.example.com/books", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer " + token
  },
  body: JSON.stringify({ title: "Atlas", price: 450 })
});
const created = await response.json();

GET and POST in Python

The requests library keeps Python code short.

import requests

reply = requests.get("https://api.example.com/books/5", timeout=10)
reply.raise_for_status()
book = reply.json()

reply = requests.post(
    "https://api.example.com/books",
    json={"title": "Atlas", "price": 450},
    headers={"Authorization": "Bearer " + token},
    timeout=10,
)

The json argument converts the dictionary and sets the Content-Type header. The raise_for_status call turns 4xx and 5xx replies into exceptions.

Always Set a Timeout

Many libraries wait forever by default. A stuck call can freeze a page or hold a server thread. Pick a limit, such as 10 seconds, and handle the timeout error with a friendly message.

Reacting to Status Codes

StatusAction in Code
200, 201Read the body and continue
400, 422Show the validation messages to the user
401Refresh the token or ask the user to sign in
404Show a not found message
429Wait for the time in Retry-After and try again
500, 503Retry a few times with growing delays

Retry with Backoff

Attempt 1 fails ── wait 1 second
Attempt 2 fails ── wait 2 seconds
Attempt 3 fails ── wait 4 seconds
Attempt 4 fails ── stop and report the error

Retry only safe or idempotent requests. A repeated POST without an idempotency key can create duplicates.

Keep Secrets Out of Client Code

Code that runs in a browser is visible to every visitor. Store API keys and secrets on a server, and let the browser call your own backend. The backend adds the secret and forwards the request.

Browser ──→ Your Backend (holds the key) ──→ Third-Party API

Organize Your Code

  • Place every API call in one module, so URLs and headers live in one place.
  • Read the base URL from configuration.
  • Convert API responses into your own simple data objects.
  • Log failed calls with the status code and the request id.

Common Mistakes

Ignoring Failed Statuses

Code that parses the body of every response crashes on error replies. Check the status first.

Calling Inside Loops

Fetching one record per loop turn creates hundreds of calls. Use list endpoints, filters, or bulk operations.

Hard-Coded URLs

A URL repeated in twenty places turns a server move into a long search. Keep one constant.