BACK

Apr 6, 2025

0 words
0 m

APIs Are Just Conversations

APIs feel scary at first.
They are not.

An API is just a way for two sides to talk.
You ask for something.
It replies.
That is it.

If you can hold a conversation, you can work with APIs.
Let me show you.

The shape of a conversation

Human talk:

  • You say hello.

  • You ask a question.

  • You get an answer.

API talk:

  • You open a connection.

  • You send a request.

  • You get a response.

That is all we are doing.

A real request and response

Here is a tiny example that asks a public API for a user.

GET /users/42 HTTP/1.1
Host: api.example.com
Accept: application/json

A normal reply looks like this:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": 42,
  "name": "Tara",
  "email": "tara@example.com"
}

Read it like a chat.
You asked for user 42.
The server said OK and sent you a JSON object.

Do it in the browser with fetch

<script>
async function getUser(id) {
  const res = await fetch(`https://api.example.com/users/${id}`, {
    headers: { "Accept": "application/json" }
  });

  if (!res.ok) {
    throw new Error(`Request failed with ${res.status}`);
  }

  const data = await res.json();
  console.log(data);
  return data;
}

getUser(42).catch(err => console.error(err.message));
</script>

Do it in Node.js

import fetch from "node-fetch";

async function createUser(payload) {
  const res = await fetch("https://api.example.com/users", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Accept": "application/json"
    },
    body: JSON.stringify(payload)
  });

  if (!res.ok) {
    const text = await res.text();
    throw new Error(`Failed ${res.status}: ${text}`);
  }

  return res.json();
}

await createUser({ name: "Tara", email: "tara@example.com" });

Do it in Python

import requests

def get_posts(page=1):
  res = requests.get(
    "https://api.example.com/posts",
    params={"page": page},
    headers={"Accept": "application/json"}
  )
  res.raise_for_status()
  return res.json()

print(get_posts())

GET, POST, PUT, DELETE

Think of these as verbs.

  • GET asks for data.

  • POST creates something.

  • PUT replaces something.

  • PATCH updates part of something.

  • DELETE removes it.

Pick the verb that matches what you want to do.

Path and query

Two ways to pass details.

  • Path is the thing itself.
    GET /users/42 means the user with id 42.

  • Query is about filters, pages, and options.
    GET /posts?page=3&limit=20

Headers are like tone and context

  • Accept: application/json says you want JSON back.

  • Content-Type: application/json says you are sending JSON.

  • Authorization: Bearer <token> says who you are.

Auth without pain

const res = await fetch("https://api.example.com/me", {
  headers: {
    "Accept": "application/json",
    "Authorization": `Bearer ${token}`
  }
});

Keep tokens out of client code if they are secret.
Use env vars on the server.
Rotate them if they leak.

Errors are part of the chat

  • 400 Bad Request - your message was wrong.

  • 401 Unauthorized - you are not logged in.

  • 403 Forbidden - you are logged in but not allowed.

  • 404 Not Found - the thing is not there.

  • 429 Too Many Requests - slow down.

  • 500+ - the server is broken.

Always handle the common ones.

Pagination is just the server saying, one page at a time

Most lists are paged.
The server gives you a slice and a pointer to the next.

async function getAllPosts() {
  let cursor = null;
  const all = [];

  while (true) {
    const url = new URL("https://api.example.com/posts");
    if (cursor) url.searchParams.set("cursor", cursor);

    const res = await request(url.toString());
    all.push(...res.items);
    cursor = res.nextCursor;

    if (!cursor) break;
  }
  return all;
}

Rate limits are the server saying, slow down

function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }

async function safeRequest(url, opts) {
  for (let attempt = 1; attempt <= 5; attempt++) {
    try {
      return await request(url, opts);
    } catch (e) {
      if (e.status === 429 || e.status >= 500) {
        const wait = Math.min(1000 * 2 ** (attempt - 1), 8000);
        await sleep(wait);
        continue;
      }
      throw e;
    }
  }
  throw new Error("Gave up after retries");
}

Idempotent actions save you from double clicks

POST /charges
Idempotency-Key: a2b1-unique-123
Content-Type: application/json

{ "amount": 500, "currency": "usd" }

If the same key is sent again, the server returns the same result.

Caching is just remembering the answer

const cache = new Map();

async function cachedGet(url, ttlMs = 10_000) {
  const hit = cache.get(url);
  if (hit && Date.now() < hit.expires) return hit.data;

  const data = await request(url);
  cache.set(url, { data, expires: Date.now() + ttlMs });
  return data;
}

Make your UI honest

  • Disable buttons during requests.

  • Show loading text or a spinner.

  • Show clear success or error messages.

  • Do not lie.

<button id="create" type="button">Create</button>
<p id="status" role="status" aria-live="polite"></p>

<script>
const btn = document.getElementById("create");
const statusEl = document.getElementById("status");

btn.addEventListener("click", async () => {
  btn.disabled = true;
  statusEl.textContent = "Working...";
  try {
    const res = await request("https://api.example.com/items", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ name: "Sample" })
    });
    statusEl.textContent = "Done";
  } catch (e) {
    statusEl.textContent = "Failed. Try again.";
  } finally {
    btn.disabled = false;
  }
});
</script>

A quick mental model you can keep

  • Path says what you are talking about.

  • Verb says what you want to do.

  • Headers set the tone and context.

  • Body carries the content.

  • Status code is the reply mood.

  • JSON is the language you both agreed to use.

APIs are not magic.
They are just conversations.
Ask clearly.
Listen to the reply.
Handle the awkward parts with grace.
You will be fine.

Taseen Tanvir

1:12:27 UTC

Create a free website with Framer, the website builder loved by startups, designers and agencies.