So far you have designed and built APIs. Just as often you will be on the other side, calling someone else's API from a browser, a Node.js service or a Python script. In this lesson you will learn to make requests with the fetch API and Python's requests library, handle errors and timeouts, and package the calls into a small reusable client.
fetch is built into every modern browser and into Node.js 18 and later, so one API covers both. It returns a promise of a Response; you then read the body with .json(), .text() or .blob().
const params = new URLSearchParams({ genre: "sci-fi", limit: 5 });
const res = await fetch(`https://api.example.com/books?${params}`, {
headers: { Accept: "application/json" },
});
if (!res.ok) {
throw new Error(`Request failed with ${res.status}`);
}
const books = await res.json();
console.log(books.length, res.headers.get("X-Total-Count"));Two things surprise newcomers: fetch only rejects on network failure, so a 404 or 500 resolves normally and you must check res.ok (true for 200-299); and the body can be read only once. To send data, set the method, a Content-Type header and a serialized body:
const res = await fetch("https://api.example.com/books", {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
body: JSON.stringify({ title: "Dune", author: "Frank Herbert" }),
});
if (res.status === 201) console.log("Created at", res.headers.get("Location"));fetch has no timeout by default; a hung connection can wait forever. Pass a signal option: AbortSignal.timeout(8000) creates a signal that fires after 8 seconds, and a manual AbortController lets you cancel when, for example, the user navigates away by calling controller.abort(). An aborted request rejects with an error whose name is "AbortError" (or "TimeoutError" for AbortSignal.timeout), so you can tell it apart from a real failure.
Repeating headers, JSON parsing and error checks in every call is how bugs creep in. Wrap them once:
async function api(path, { method = "GET", body, token } = {}) {
const res = await fetch(BASE + path, {
method,
headers: {
Accept: "application/json",
...(body && { "Content-Type": "application/json" }),
...(token && { Authorization: `Bearer ${token}` }),
},
body: body ? JSON.stringify(body) : undefined,
signal: AbortSignal.timeout(8000),
});
if (res.status === 204) return null;
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.error ?? res.statusText}`);
return data;
}Retries belong in this layer too: retry GET on 429, 503 and network errors with backoff, honoring Retry-After. Libraries such as axios and ky add interceptors, but a plain fetch wrapper is often enough.
requests is the standard HTTP client for Python (pip install requests). It parses JSON, encodes query parameters and raises exceptions for HTTP errors on request:
import requests
session = requests.Session()
session.headers.update({"Accept": "application/json", "Authorization": f"Bearer {token}"})
resp = session.get("https://api.example.com/books", params={"genre": "sci-fi", "limit": 5}, timeout=8)
resp.raise_for_status() # raises requests.HTTPError for 4xx and 5xx
for book in resp.json():
print(book["title"])
created = session.post("https://api.example.com/books", json={"title": "Dune"}, timeout=8)
print(created.status_code, created.headers.get("Location"))A Session reuses the TCP connection and applies default headers to every call. Always pass timeout; without it a request can block forever. The json= argument serializes the body and sets Content-Type. For async code, httpx offers the same interface with async/await.
res.ok in JavaScript and treating a 500 as success because the promise resolved.res.json() on an empty 204 response, which throws a parse error.A `fetch` call to an endpoint that returns `404` will:
fetch works in browsers and Node.js 18+; check res.ok because HTTP errors do not reject.AbortSignal.timeout and cancel with AbortController.requests.Session, params=, json=, timeout= and raise_for_status().Next lesson: CORS: Cross-Origin Requests and Preflight — understand why the browser blocks your API call and how to configure the server correctly.