TL;DR: A REST API is a standardized way for two programs to exchange data over HTTP. Your app sends a request to a URL (endpoint) using an HTTP method (GET, POST, PUT, DELETE), and the server sends back a response with a status code and usually JSON data. Understanding this cycle lets you read, debug, and write AI-generated API code with confidence.

Why AI Coders Need to Know This

Almost every real-world application connects to at least one REST API. Weather apps, payment systems, maps, social logins, SMS notifications, translation services, AI models — they all expose their functionality through APIs. When you ask AI to "connect my app to weather data" or "add Stripe payments," the first thing it writes is API code.

If you do not understand REST APIs, that code is a black box. It might work the first time. But when it fails — and external APIs fail constantly, for a hundred different reasons — you will have no idea what to look for. Is the endpoint wrong? Is the method wrong? Did the token expire? Did the server return a 429 rate limit error? Did you forget to parse the JSON?

These are all answerable questions. But only if you understand the conversation happening between your code and the API.

The goal of this article is simple: by the end, the fetch() calls AI generates for you will make sense. You will be able to read them, test them, debug them, and ask for better ones.

Real Scenario

You open Cursor and type:

Prompt I Would Type

I'm building a weather dashboard app. Connect it to the Open-Meteo API
(https://api.open-meteo.com) to get current temperature and wind speed
for a given latitude and longitude.

Show me:
1. How to call the API using fetch()
2. How to handle loading and error states
3. How to display the result

Add comments explaining each part of the fetch() call.

Open-Meteo is a good learning API: it is free, requires no API key, and returns clean JSON. Claude or Cursor will generate a complete fetch call within seconds. Let's look at what it produces and understand every line.

What AI Generated

// The base URL for Open-Meteo's forecast endpoint
// REST APIs organize functionality by URL paths — this one is for weather forecasts
const API_BASE = 'https://api.open-meteo.com/v1/forecast';

async function getWeather(latitude, longitude) {
  // Query parameters customize the API request
  // They are appended to the URL after a ? as key=value pairs
  const params = new URLSearchParams({
    latitude,                              // required: where to get weather for
    longitude,                             // required: paired with latitude
    current: 'temperature_2m,wind_speed_10m', // which fields we want in the response
    wind_speed_unit: 'mph',               // optional: unit preference
  });

  // Build the full URL: base + ? + parameters
  const url = `${API_BASE}?${params}`;
  // Result: https://api.open-meteo.com/v1/forecast?latitude=47.7&longitude=-116.8¤t=...

  try {
    // fetch() sends an HTTP GET request to the URL
    // By default, fetch() uses GET — the method for retrieving data
    const response = await fetch(url);

    // response.ok is true when the status code is 200-299 (success range)
    // A response can arrive without throwing an error AND still be a failure
    if (!response.ok) {
      // response.status is the HTTP status code (200, 404, 500, etc.)
      throw new Error(`API error: ${response.status} ${response.statusText}`);
    }

    // The response body arrives as a stream of text
    // .json() reads that stream and parses it into a JavaScript object
    const data = await response.json();

    // data now contains the parsed JSON from the API
    // We only need the current weather values
    return {
      temperature: data.current.temperature_2m, // e.g. 54.2
      windSpeed: data.current.wind_speed_10m,    // e.g. 12.8
      unit: data.current_units.temperature_2m,   // e.g. "°F"
    };

  } catch (error) {
    // Two kinds of errors land here:
    // 1. Network failures (no internet, DNS failure, server unreachable)
    // 2. Errors we threw ourselves in the !response.ok check above
    console.error('Weather fetch failed:', error.message);
    throw error; // re-throw so the caller can handle it
  }
}

// Example usage: calling the function and rendering the result
async function displayWeather() {
  const loadingEl = document.getElementById('loading');
  const resultEl = document.getElementById('result');
  const errorEl = document.getElementById('error');

  // Show loading state while the API request is in flight
  loadingEl.style.display = 'block';

  try {
    // Coeur d'Alene, Idaho — latitude 47.6777, longitude -116.7805
    const weather = await getWeather(47.6777, -116.7805);

    resultEl.textContent =
      `${weather.temperature}${weather.unit} | Wind: ${weather.windSpeed} mph`;
    resultEl.style.display = 'block';
  } catch (err) {
    // Show error message if anything went wrong
    errorEl.textContent = `Could not load weather: ${err.message}`;
    errorEl.style.display = 'block';
  } finally {
    // Always hide loading, whether we succeeded or failed
    loadingEl.style.display = 'none';
  }
}

That is 60 lines of code that talks to a real external API. Now let's understand each concept behind it.

Understanding Each Part

What Is an Endpoint?

An endpoint is a specific URL that represents a resource or action in an API. Think of it as a department in a large building. The building's address is the base URL (https://api.open-meteo.com), and each department is a path (/v1/forecast).

Well-designed REST APIs use URLs to describe resources in a predictable way:

  • GET /users — get all users
  • GET /users/42 — get user with ID 42
  • POST /users — create a new user
  • PUT /users/42 — update user 42
  • DELETE /users/42 — delete user 42

This predictability is what makes REST "RESTful." The URL tells you what you're working with, and the HTTP method tells you what you're doing to it.

HTTP Methods: GET, POST, PUT/PATCH, DELETE

HTTP methods are the verbs of REST APIs. Each one expresses a different intent:

GET

Retrieve data. Should never modify anything. Safe to call multiple times. Used for reading, searching, and fetching lists.

fetch('/api/products')

POST

Create new data. Sends a request body with the new resource. Not safe to repeat — calling it twice creates two records.

fetch('/api/products', { method: 'POST', body: ... })

PUT / PATCH

Update existing data. PUT replaces the whole resource; PATCH updates specific fields. Most APIs accept both but prefer PATCH for partial updates.

fetch('/api/products/5', { method: 'PATCH', body: ... })

DELETE

Remove data. Usually idempotent — calling it twice on a deleted resource should return a 404, not an error.

fetch('/api/products/5', { method: 'DELETE' })

Query Parameters vs. Request Body

There are two common ways to send data with an API request:

Query parameters are appended to the URL after a ?. They are used with GET requests where you want to filter or customize results without changing anything. In our weather example, ?latitude=47.6&longitude=-116.8 tells the API where to look — it does not create or modify any data.

Request body is used with POST, PUT, and PATCH requests. The data is sent as a separate payload (usually JSON) attached to the request. The URL identifies what resource to act on; the body provides the data to use.

// Sending JSON in a POST request body
const response = await fetch('/api/tasks', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json', // tell the server we're sending JSON
  },
  body: JSON.stringify({               // convert JS object to JSON string
    title: 'Write REST API article',
    priority: 'high',
  }),
});

HTTP Status Codes

Every API response includes a three-digit status code. This is not optional documentation — it is how the API communicates the outcome of your request. AI often forgets to check status codes, which is one of the most common sources of silent bugs in fetch-based code.

200 OK

Request succeeded. Data is in the response body.

201 Created

New resource created successfully. Common after POST.

204 No Content

Success, but no body. Common after DELETE or PATCH.

400 Bad Request

Your request is malformed. Missing field, wrong format, invalid value.

401 Unauthorized

No credentials, or credentials are invalid. Check your API key.

403 Forbidden

Authenticated, but no permission for this resource.

404 Not Found

The endpoint or resource does not exist. Check your URL.

429 Too Many Requests

You have hit the rate limit. Slow down and retry after the delay period.

500 Internal Server Error

The API crashed. The problem is on the server's side, not yours.

503 Service Unavailable

The API is down or overloaded. Retry later.

JSON: The Language APIs Speak

JSON (JavaScript Object Notation) is the standard data format for REST APIs. It is plain text that represents structured data using keys and values, arrays, and nested objects.

// A typical JSON API response
{
  "current": {
    "temperature_2m": 54.2,
    "wind_speed_10m": 12.8
  },
  "current_units": {
    "temperature_2m": "°F",
    "wind_speed_10m": "mph"
  }
}

// In JavaScript, response.json() converts that text into an object
// Then you access values with dot notation or bracket notation
const temp = data.current.temperature_2m;   // 54.2
const unit = data.current_units['temperature_2m']; // "°F"

The most common beginner mistake: forgetting to await response.json(). Without the await, you get back a Promise, not the actual data — and every property access returns undefined.

Authentication Headers

Many APIs require authentication — proof that you are allowed to use them. The most common method is an API key sent in a request header.

// Bearer token authentication (most common for modern APIs)
const response = await fetch('https://api.example.com/data', {
  headers: {
    'Authorization': `Bearer ${process.env.MY_API_KEY}`,
    'Content-Type': 'application/json',
  },
});

// Some APIs use a custom header name
const response2 = await fetch('https://api.example.com/data', {
  headers: {
    'X-API-Key': process.env.MY_API_KEY,
  },
});

Security Warning

Never put an API key directly in your frontend JavaScript. It will be visible in your page source to anyone who looks. API keys belong in environment variables on your server. If you are making API calls from a browser, route them through your own backend endpoint that holds the secret key.

What AI Gets Wrong About REST APIs

Not checking response.ok before parsing

This is the most common AI mistake with fetch(). The fetch() function only throws an error for network failures — not for 4xx or 5xx responses. So if the API returns a 404, fetch() resolves normally. If you skip the response.ok check and call response.json() anyway, you might parse an error message as if it were valid data.

// Bad — no status check
const data = await fetch('/api/user/99').then(r => r.json());
// If user 99 does not exist, data might be { error: "Not found" }
// but you would not know it failed

// Good — always check response.ok
const response = await fetch('/api/user/99');
if (!response.ok) {
  throw new Error(`Failed: ${response.status}`);
}
const data = await response.json();

Exposing API keys in frontend code

AI frequently generates fetch() calls with API keys directly in the JavaScript. In a tutorial context that is fine. In production, it is a security incident waiting to happen. Any key visible in your browser's source code or network tab is compromised. Always route sensitive API calls through your own backend.

No error handling on the network call

AI often generates fetch() calls with no try/catch at all. In a development environment on a fast local network, this "works." In production, networks fail, APIs timeout, and users lose data. Every fetch() call needs error handling.

Forgetting Content-Type headers on POST requests

When sending JSON in a POST body, you must include 'Content-Type': 'application/json'. Without it, many APIs treat the body as plain text and return a 400 Bad Request. AI sometimes omits this header, especially in shortened examples.

Wrong HTTP method for the action

AI sometimes uses POST when PUT is appropriate, or GET when POST is needed. The method matters — some APIs treat them as semantically different and behave unexpectedly when the wrong one arrives. Always read the API documentation for the correct method on each endpoint.

How to Debug REST APIs With AI

Use the Network tab in DevTools

Open Chrome DevTools → Network tab. Every API call your app makes shows up here. Click on a request to see:

  • Headers: What your app sent (method, headers, any cookies).
  • Payload: The request body you sent (for POST/PUT).
  • Preview / Response: The raw data the API returned.
  • Status: The HTTP status code.

This is the single fastest way to debug API issues. Before you paste code into Claude, look at the Network tab and find the specific request that failed. The response body usually tells you exactly what went wrong.

Test the API directly with curl

When you're not sure if the problem is your code or the API itself, test the endpoint directly in your terminal:

# Test a GET endpoint directly
curl -s "https://api.open-meteo.com/v1/forecast?latitude=47.68&longitude=-116.78¤t=temperature_2m" | head -c 500

# Test a POST endpoint with JSON body
curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your-token" \
  -d '{"email": "test@example.com"}'

If curl works and your fetch() does not, the problem is in your JavaScript. If curl fails too, the problem is the API or your credentials.

Cursor tips

  • Paste the full Network tab request details (URL, headers, status code, response body) into Cursor and ask: "Why did this API call fail?"
  • Ask Cursor to add structured error logging: "Add try/catch to this fetch call and log the status code, response headers, and body text on failure."

Windsurf tips

  • Ask Windsurf to generate an API testing utility — a small function you can call from the browser console to test any endpoint without touching your main app code.
  • When CORS errors appear, describe the exact error message. Windsurf is good at diagnosing which headers need to change and where (client vs. server).

Claude Code tips

  • Claude Code can run curl commands directly in your terminal during a session. Ask it to test the API and then explain the response before writing any JavaScript.
  • When building your own API, ask Claude Code to "generate an API test script that validates all endpoints and checks their status codes and response shapes." This creates a regression test suite almost for free.

What to Learn Next

REST APIs are the connection layer between your frontend and the data that powers it. The next logical steps:

Next Step

Try the Open-Meteo weather example — it requires no API key. Open the Network tab in DevTools as you run it and watch the request happen in real time. Seeing the raw JSON response from a live API makes everything in this article click into place.

FAQ

A REST API is a way for two computer programs to talk to each other over the internet using standard HTTP requests. Your app sends a request to a URL (the endpoint), the API processes it, and sends back data — usually in JSON format. REST stands for Representational State Transfer, which describes the architectural rules that make APIs predictable and reusable.

The four main HTTP methods are: GET (retrieve data without changing anything), POST (create new data), PUT/PATCH (update existing data — PUT replaces the whole thing, PATCH updates part of it), and DELETE (remove data). These map roughly to the four CRUD operations: Create, Read, Update, Delete.

Status codes are three-digit numbers in every HTTP response. 2xx means success (200 OK, 201 Created). 3xx means redirect. 4xx means client error — something wrong with your request (400 Bad Request, 401 Unauthorized, 404 Not Found). 5xx means server error — the API itself failed (500 Internal Server Error, 503 Service Unavailable).

JSON (JavaScript Object Notation) is a lightweight text format for structured data. It uses keys and values, arrays, and nested objects — similar to a JavaScript object. APIs use JSON because it is human-readable, easy to parse in any programming language, and compact enough to send efficiently over HTTP.

An API key is a secret token you include in your requests to prove your identity to the API. It lets the provider track your usage, rate-limit your requests, and charge you if appropriate. Never put an API key directly in your frontend JavaScript — it will be visible to anyone who views your source code. Store it on a server or in an environment variable.