Accessing remote APIs

Using the Fetch API to retrieve live data and images from external public servers.

Learning Goals

At the end of this Tutorial, you will be able to:

  • Connect to public, third-party APIs using the fetch() method.
  • Understand how APIs provide image data as URL strings.
  • Extract nested data from complex JSON objects.
  • Dynamically output mixed text and image data to the DOM.

For this Tutorial, in your javascript/exercises folder, create a new HTML file named list-remote.html.

Create a new empty text file named list-remote.js and save it in this same folder.

Add a link to list-remote.js in your list-remote.html file using a <script> tag with the defer attribute.

Introduction to remote APIs

In the previous tutorial, you successfully used the Fetch API to load data from a local JSON file (data/users.json). That is a great start, but the real power of JavaScript is its ability to communicate with live servers across the internet.

A web API (Application Programming Interface) is essentially a URL that, instead of returning an HTML web page meant for humans, returns raw data (usually in JSON format) meant for computer programs.

For these exercises, we will use free, public APIs that require no registration or "API keys". All the asynchronous `fetch` logic and `try...catch` error handling you just learned will work exactly the same way—we are simply changing the URL string from a local file path to a remote web address.

Understanding image URLs in JSON

A common point of confusion for beginners is how to "download" an image from an API. It is important to understand that APIs do not send actual image files (like .jpg or .png files) in their JSON responses.

Instead, the JSON data simply contains a URL string pointing to where the image lives on the internet.

To display the image, you fetch the JSON, extract the URL string, and then inject that string into the src attribute of an standard HTML <img> tag using a template literal.

Example 1: The Dog API (Single Object)

Let's start with the simplest possible example: fetching a single random image of a dog.

If you open this endpoint URL in your browser: https://dog.ceo/api/breeds/image/random, you will see a tiny JSON response that looks like this:

{
  "message": "https://images.dog.ceo/breeds/retriever-golden/n02099601_3004.jpg",
  "status": "success"
}

The image URL is stored inside the message property.

In your HTML file, add an empty image tag and a button:

<button id="btn-dog">Get Random Dog</button>
<div id="dog-container">
    <img id="dog-image" src="" alt="A random dog" width="300">
</div>

In your JavaScript file, add the following code:

async function fetchDog() {
    try {
        // 1. Fetch from the live remote URL
        const response = await fetch("https://dog.ceo/api/breeds/image/random");
        
        if (!response.ok) {
            console.log(`Error: ${response.status}`);
        } else {
            // 2. Parse the JSON
            const data = await response.json();
            
            // 3. Update the DOM by targeting the image's src attribute
            document.getElementById("dog-image").src = data.message;
        }
    } catch (error) {
        console.error(`Fetch error: ${error}`);
    }
}

// Add event listener to the button
document.getElementById("btn-dog").addEventListener("click", fetchDog);

Example 2: Random User API (Array of Objects)

Now let's look at something more realistic. The Random User Generator creates fake user profiles. This is exactly like the local users.json file you worked with previously, but it includes nested objects and profile pictures.

The endpoint https://randomuser.me/api/?results=10 returns an object containing a results array with 10 users. Here is a simplified look at one user in that array:

{
  "results": [
    {
      "name": {
        "first": "Brad",
        "last": "Gibson"
      },
      "email": "brad.gibson@example.com",
      "picture": {
        "large": "https://randomuser.me/api/portraits/men/75.jpg"
      }
    }
  ]
}

Because the data is nested, to get Brad's first name, you don't type user.first. You must follow the path down the tree: user.name.first. To get the image, you use user.picture.large.

First, add an empty container to your HTML:

<div id="users-grid" style="display: flex; flex-wrap: wrap; gap: 20px;"></div>

Then, write the fetch logic and the DOM output logic in your JS file:

async function fetchRemoteUsers() {
    try {
        const response = await fetch("https://randomuser.me/api/?results=10");
        
        if (!response.ok) {
            console.log(`Error: ${response.status}`);
        } else {
            const data = await response.json();
            
            // Notice we pass data.results, because the array is inside the 'results' property
            displayUsers(data.results);
        }
    } catch (error) {
        console.error(`Fetch error: ${error}`);
    }
}

function displayUsers(usersArray) {
    const container = document.getElementById("users-grid");
    let htmlOutput = "";

    // Loop through the array of users
    usersArray.forEach(user => {
        // Build HTML, carefully extracting nested data and using the image URL
        htmlOutput += `
            <div style="border: 1px solid #ccc; padding: 15px; text-align: center;">
                <img src="${user.picture.large}" alt="Profile picture" style="border-radius: 50%;">
                <h3>${user.name.first} ${user.name.last}</h3>
                <p>${user.email}</p>
            </div>
        `;
    });

    container.innerHTML = htmlOutput;
}

// Fetch the users immediately when the page loads
fetchRemoteUsers();

Registering for API keys

There are a few services that, after signing up for a single free account, grant you access to thousands of remote data sources. Two of the most popular are:

  • API Ninjas: A fun place to start with 100+ APIs covering pets, cars, celebrity net worth, quotes, historical events, weather, and jokes. The free tier allows 10,000 to 50,000 API calls per month.
  • RapidAPI: The world's largest API website. It hosts tens of thousands of APIs from both independent developers and large organisations. But because the APIs are made by different people, the JSON data structures may be completely different from one API to the next.

Each of the above will provide you with an API key to include in your network request. An API key tells the server exactly who is making the request so it can track your usage and apply rate limits.

About HTTP headers

When a browser sends a request to a server, it doesn't just send the URL. It also sends hidden metadata called headers that provide additional information about the request.

When using an API Key, you must attach that key to your request using a specific header, typically named X-Api-Key (or similar, depending on the service).

Up until now, you have passed just a simple URL string to fetch(). To use an API key, you must pass a second argument — a configuration object that includes your personal API key in the headers. This is typically managed using the options variable.

Using API Ninjas as an example, here is what that code looks like:

const url = "https://api.api-ninjas.com/v1/facts?limit=1";

const options = {
    headers: {
        'X-Api-Key': 'YOUR_API_KEY_GOES_HERE'
    }
};

// Pass both arguments to fetch
const response = await fetch(url, options);

As practise, sign up for the above two services, obtain a personal API key, select some data sources you find interesting, and create code to retrieve and display the data.

Try it yourself

---

Task: The Rick and Morty Character Grid

Use the public Rick and Morty API to build a character grid on your webpage.

  1. Create an HTML file with an empty container `<div id="character-grid">`.
  2. In your JS file, write a `fetch` function to get data from this endpoint:
    https://rickandmortyapi.com/api/character
  3. Log the data to the console first. Notice that the array of characters is located inside data.results.
  4. Pass the array to a display function. Use .forEach() to output a card for each character.
  5. Include the character's name (character.name), their status (character.status), and display their image using an `<img>` tag (character.image).

---

More learning resources

Tutorial Quiz

  Take the test

Tutorial Podcast

Sample AI prompts

Explain how to read complex JSON structures from an API. How do I know if the data I want is an Object or an Array, and how do I navigate "nested" data paths using dot notation?
What does "Rate Limiting" mean when working with public APIs? Give me an example of what HTTP status code a server sends if I hit the rate limit.
Can you suggest 3 fun, free public APIs that do not require an API key, similar to the Dog API or Pokemon API, that I can use to practice fetching data?