Skip to content

Join the Seedly owners community →

API Fundamentals

Making API Calls

Calling APIs from your code

Written by 12 min read1 activity
Buzz, your presenter

Buzz presents

now we call real APIs from code with fetch, async and await!! and we handle errors nicely too, no worries at all.now we call real APIs from code with fetch, async and await!! and we handle errors nicely too, no worries at all.

Buzz sits on a dock reeling a crate of fruit out of the water with a fishing rod
Fetch sends a request and waits for the data to come back

You've learned what APIs are, how they work and how to authenticate. Now it's time to actually CALL one. This lesson is all about making API requests with JavaScript's fetch() function.

What is Fetch?#

fetch() is a built-in JavaScript function that makes HTTP requests. It's the standard way to call APIs from web browsers and Node.js.

The basic idea is pretty simple.

  1. Tell fetch where to go (the URL)
  2. Tell it what to do (GET, POST, etc.)
  3. Wait for the response
  4. Use the data

Your First Fetch Request#

Let's start with the simplest API call there is, a plain GET request.

fetch("https://api.example.com/products")
  .then(response => response.json())
  .then(data => {
    console.log(data);
  });

Here's what each piece is doing.

Step 1. Call fetch with a URL#

fetch("https://api.example.com/products") sends a GET request to that URL.

Step 2. Convert the response to JSON#

.then(response => response.json()) takes the response and turns it into a JavaScript object you can work with.

Step 3. Use the data#

.then(data => { console.log(data); }) lets you do something with the data. Here we just print it out.

Using Async/Await#

There's a cleaner way to write fetch calls using async and await. This is the modern way most people do it.

async function getProducts() {
  const response = await fetch("https://api.example.com/products");
  const data = await response.json();
  console.log(data);
}

Same result, but it reads a lot more like regular top-to-bottom code. The await keyword just pauses until the fetch finishes.

Making a GET Request#

GET requests are for reading data, and they're the simplest kind.

async function getUser(userId) {
  const response = await fetch(`https://api.example.com/users/${userId}`);
  const user = await response.json();
  return user;
}
 
// Usage
const user = await getUser(42);
console.log(user.name);

Making a POST Request#

POST requests send data to create something new, so you have to include that data in the request body.

async function createUser(name, email) {
  const response = await fetch("https://api.example.com/users", {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      name: name,
      email: email
    })
  });
 
  const newUser = await response.json();
  return newUser;
}
 
// Usage
const user = await createUser("Sarah", "[email protected]");
console.log(user.id); // The new user's ID

The important bits.

  • method: "POST" tells fetch this is a POST request
  • headers tells the server we're sending JSON
  • body holds the data, converted to a JSON string

Making a PUT Request#

PUT requests update existing data. They look a lot like POST. Remember PUT replaces the WHOLE thing, so you send every field, even the ones that didn't change.

async function updateUser(userId, name, newEmail) {
  const response = await fetch(`https://api.example.com/users/${userId}`, {
    method: "PUT",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      name: name,
      email: newEmail
    })
  });
 
  const updatedUser = await response.json();
  return updatedUser;
}

Making a DELETE Request#

Buzz sorts a parcel with a green tick from a dented parcel placed in a basket
Check that the response is ok before using the data

DELETE requests remove data. They usually don't need a body at all.

async function deleteUser(userId) {
  const response = await fetch(`https://api.example.com/users/${userId}`, {
    method: "DELETE"
  });
 
  if (response.ok) {
    console.log("User deleted");
  }
}

Adding Authentication#

Most APIs want you to prove who you are. Here's how you add an API key or token.

async function getPrivateData() {
  const response = await fetch("https://api.example.com/private", {
    headers: {
      "Authorization": "Bearer your_token_here"
    }
  });
 
  const data = await response.json();
  return data;
}

And you can stack headers together.

const response = await fetch("https://api.example.com/users", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer your_token_here"
  },
  body: JSON.stringify({ name: "Sarah" })
});

Handling Errors#

Stuff goes wrong sometimes. The server's down, or you sent bad data, or the WiFi at the coffee shop decided to take a nap. Your code should be ready for it.

async function getUser(userId) {
  try {
    const response = await fetch(`https://api.example.com/users/${userId}`);
 
    if (!response.ok) {
      // Something went wrong
      if (response.status === 404) {
        console.log("User not found");
        return null;
      }
      throw new Error(`HTTP error: ${response.status}`);
    }
 
    const user = await response.json();
    return user;
 
  } catch (error) {
    console.log("Request failed:", error.message);
    return null;
  }
}

The important bits.

  • response.ok is true if the status code is 200-299
  • response.status gives you the actual status code
  • try/catch handles network errors (like no internet connection)

A Complete Example#

Let's put it all together into something that looks like real life.

const API_URL = "https://api.mystore.com";
const API_KEY = process.env.API_KEY;
 
async function getProducts(category) {
  const url = category
    ? `${API_URL}/products?category=${category}`
    : `${API_URL}/products`;
 
  const response = await fetch(url, {
    headers: {
      "Authorization": `Bearer ${API_KEY}`
    }
  });
 
  if (!response.ok) {
    throw new Error("Failed to fetch products");
  }
 
  return response.json();
}
 
async function addToCart(productId, quantity) {
  const response = await fetch(`${API_URL}/cart/items`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${API_KEY}`
    },
    body: JSON.stringify({
      productId: productId,
      quantity: quantity
    })
  });
 
  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.message);
  }
 
  return response.json();
}
 
// Usage
const products = await getProducts("electronics");
console.log(`Found ${products.length} products`);
 
await addToCart(products[0].id, 2);
console.log("Added to cart!");

Once you can do this, you can automate stuff that used to eat your whole afternoon. In one demo, Claude Code spun up 13 subaccounts, crawled the sites and rebuilt the forms in just under 12 minutes (lil humblebrag, I know). That kind of thing gets a whole lot less mysterious once calls like these stop scaring you.

TL;DR#

  • fetch() is JavaScript's built-in way to make API calls
  • Use async/await for cleaner, easier-to-read code
  • GET requests just need a URL
  • POST, PUT, DELETE need a method and often a body
  • Include Content-Type: application/json when sending JSON
  • Add authentication with the Authorization header
  • Check response.ok so errors don't slip past you

What's Next?#

You know how to call an API now. But what if you want the API to call YOU? That's what webhooks do, and they're up next...

This lesson ends with a short activity.