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 usersGET /users/42— get user with ID 42POST /users— create a new userPUT /users/42— update user 42DELETE /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.
Request succeeded. Data is in the response body.
New resource created successfully. Common after POST.
Success, but no body. Common after DELETE or PATCH.
Your request is malformed. Missing field, wrong format, invalid value.
No credentials, or credentials are invalid. Check your API key.
Authenticated, but no permission for this resource.
The endpoint or resource does not exist. Check your URL.
You have hit the rate limit. Slow down and retry after the delay period.
The API crashed. The problem is on the server's side, not yours.
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:
- What Is an API? — the broader concept before REST specifically.
- What Is Async/Await? — the JavaScript syntax powering every fetch() call.
- What Is a Database? — where your REST API gets its data.
- What Is CRUD? — the four operations (Create, Read, Update, Delete) that every REST API implements.
- Security Basics for AI Coders — how to handle API keys, CORS, and auth safely.
- What Is JavaScript? — the language behind fetch() and JSON parsing.
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.