This guide explains REST APIs for developers who haven't worked with web applications before. We'll cover what REST is, how HTTP works, and how the frontend and backend communicate.
- What is an API?
- What is REST?
- HTTP Fundamentals
- HTTP Methods
- URLs and Endpoints
- Request and Response
- JSON Data Format
- Status Codes
- Headers
- Query Parameters
- Request Body
- Authentication
- Testing APIs
- Common Patterns
- Practice Exercises
API stands for Application Programming Interface. It's a way for programs to talk to each other.
Think of a restaurant:
- You (the client) want food
- The kitchen (the server) makes food
- The waiter (the API) takes your order and brings your food
You don't need to know how to cook. You just tell the waiter what you want.
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Frontend │ ──────▶ │ API │ ──────▶ │ Backend │
│ (Browser) │ request │ │ process │ (Server) │
│ │ ◀────── │ │ ◀────── │ │
│ │ response│ │ result │ │
└─────────────┘ └─────────────┘ └─────────────┘
The frontend says: "Give me all reports" The backend processes and returns: "[report1, report2, report3]"
REST stands for REpresentational State Transfer. It's a set of rules for building APIs.
- Client-Server: Frontend and backend are separate
- Stateless: Each request is independent (no memory between requests)
- Uniform Interface: Consistent URLs and methods
- Resource-Based: Everything is a "resource" (user, report, order)
GET /api/users ← Get all users
GET /api/users/123 ← Get user with ID 123
POST /api/users ← Create a new user
PUT /api/users/123 ← Update user 123
DELETE /api/users/123 ← Delete user 123
HTTP (HyperText Transfer Protocol) is the language browsers and servers use to communicate.
┌────────────────────────────────────────────────────────────────┐
│ HTTP Request │
├────────────────────────────────────────────────────────────────┤
│ GET /api/reports HTTP/1.1 │
│ Host: localhost:8000 │
│ Accept: application/json │
│ Authorization: Bearer token123 │
└────────────────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────┐
│ HTTP Response │
├────────────────────────────────────────────────────────────────┤
│ HTTP/1.1 200 OK │
│ Content-Type: application/json │
│ │
│ {"reports": [...], "total": 42} │
└────────────────────────────────────────────────────────────────┘
HTTP methods tell the server what action to perform:
| Method | Purpose | Example | Has Body |
|---|---|---|---|
GET |
Read/fetch data | Get all reports | No |
POST |
Create new data | Submit new analysis | Yes |
PUT |
Update (replace) | Update entire report | Yes |
PATCH |
Update (partial) | Update report title | Yes |
DELETE |
Remove data | Delete a report | Usually no |
| CRUD Operation | HTTP Method |
|---|---|
| Create | POST |
| Read | GET |
| Update | PUT / PATCH |
| Delete | DELETE |
GET /api/reports ← Read all reports
GET /api/reports/42 ← Read one report
POST /api/analysis/youtube ← Create new analysis
DELETE /api/reports/42 ← Delete a report
https://api.example.com:8000/api/reports?page=1&type=youtube#section
└──┬──┘ └──────┬──────┘└─┬─┘└────┬────┘└─────────┬─────────┘└──┬───┘
scheme host port path query fragment
An endpoint is a specific URL the API responds to:
Base URL: http://localhost:8000
Endpoints:
├── /api/reports ← Report operations
│ ├── GET / ← List all
│ ├── GET /{id} ← Get one
│ ├── DELETE /{id} ← Delete one
│ └── GET /search ← Search
│
├── /api/analysis ← Analysis operations
│ ├── POST /youtube ← Analyze YouTube
│ ├── POST /article ← Analyze article
│ ├── POST /arxiv ← Analyze paper
│ └── GET /jobs/{id} ← Check job status
│
├── /api/logs ← Activity logs
│ └── GET /today ← Get today's log
│
└── /api/health ← Health check
└── GET / ← Is server running?
┌─────────────────────────────────────────────────────────────────┐
│ POST /api/analysis/youtube HTTP/1.1 │ ← Request line
├─────────────────────────────────────────────────────────────────┤
│ Host: localhost:8000 │
│ Content-Type: application/json │ ← Headers
│ Accept: application/json │
├─────────────────────────────────────────────────────────────────┤
│ │
│ { │
│ "url": "https://youtube.com/watch?v=abc123", │ ← Body
│ "model": "sonnet" │
│ } │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ HTTP/1.1 201 Created │ ← Status line
├─────────────────────────────────────────────────────────────────┤
│ Content-Type: application/json │
│ Content-Length: 89 │ ← Headers
│ Date: Mon, 25 Dec 2024 10:30:00 GMT │
├─────────────────────────────────────────────────────────────────┤
│ │
│ { │
│ "job_id": "550e8400-e29b-41d4-a716-446655440000", │ ← Body
│ "status": "pending" │
│ } │
└─────────────────────────────────────────────────────────────────┘
JSON (JavaScript Object Notation) is the standard format for API data.
{
"name": "Alice",
"age": 30,
"isActive": true,
"email": null,
"hobbies": ["reading", "gaming"],
"address": {
"city": "NYC",
"zip": "10001"
}
}- Keys must be strings in double quotes
- Values can be: string, number, boolean, null, array, object
- No trailing commas
- No comments
// JavaScript object (relaxed)
const obj = {
name: 'Alice', // Single quotes OK
age: 30, // No quotes on key OK
};
// JSON (strict)
{
"name": "Alice",
"age": 30
}// Object to JSON string
const obj = { name: "Alice", age: 30 };
const jsonString = JSON.stringify(obj);
// '{"name":"Alice","age":30}'
// JSON string to object
const parsed = JSON.parse(jsonString);
// { name: "Alice", age: 30 }import json
# Dict to JSON string
data = {"name": "Alice", "age": 30}
json_string = json.dumps(data)
# '{"name": "Alice", "age": 30}'
# JSON string to dict
parsed = json.loads(json_string)
# {'name': 'Alice', 'age': 30}HTTP status codes tell you what happened:
| Code | Name | Meaning |
|---|---|---|
| 200 | OK | Request succeeded |
| 201 | Created | New resource created |
| 204 | No Content | Success, no response body |
| Code | Name | Meaning |
|---|---|---|
| 400 | Bad Request | Invalid request data |
| 401 | Unauthorized | Authentication required |
| 403 | Forbidden | Not allowed to access |
| 404 | Not Found | Resource doesn't exist |
| 422 | Unprocessable Entity | Validation error |
| 429 | Too Many Requests | Rate limit exceeded |
| Code | Name | Meaning |
|---|---|---|
| 500 | Internal Server Error | Server crashed |
| 502 | Bad Gateway | Server got bad response |
| 503 | Service Unavailable | Server overloaded |
- 2xx: It worked!
- 4xx: You (client) messed up
- 5xx: Server messed up
Headers are metadata about the request/response.
Host: localhost:8000 ← Server address
Content-Type: application/json ← Format of request body
Accept: application/json ← Desired response format
Authorization: Bearer token123 ← Authentication
User-Agent: Mozilla/5.0... ← Client identifier
Content-Type: application/json ← Format of response body
Content-Length: 1234 ← Size in bytes
Cache-Control: max-age=3600 ← Caching instructions
Set-Cookie: session=abc123 ← Set browser cookie
const response = await fetch('/api/reports', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer mytoken123'
},
body: JSON.stringify({ title: 'New Report' })
});Query parameters filter or modify GET requests.
/api/reports?page=2&type=youtube&search=habits
└───────────┬───────────────────┘
Query string
Parameters:
├── page = 2
├── type = youtube
└── search = habits
// Manual
const url = `/api/reports?page=${page}&type=${type}`;
// Using URLSearchParams (safer)
const params = new URLSearchParams({
page: 2,
type: 'youtube',
search: 'habits'
});
const url = `/api/reports?${params}`;
// '/api/reports?page=2&type=youtube&search=habits'@router.get("/reports")
async def get_reports(
page: int = 1,
type: str = None,
search: str = None
):
# page, type, search are extracted from query parameters
...POST, PUT, and PATCH requests send data in the body.
const response = await fetch('/api/analysis/youtube', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: 'https://youtube.com/watch?v=abc123',
model: 'sonnet'
})
});from pydantic import BaseModel
class AnalysisRequest(BaseModel):
url: str
model: str = "sonnet"
@router.post("/youtube")
async def analyze_youtube(request: AnalysisRequest):
# request.url and request.model are available
...APIs often require authentication.
Authorization: Bearer sk-ant-api03-...
fetch('/api/reports', {
headers: {
'Authorization': 'Bearer sk-ant-api03-...'
}
});/api/reports?api_key=sk-ant-api03-...
Authorization: Basic base64(username:password)
More complex; involves login flow and tokens.
The Anthropic API key is stored on the server:
# .env file
ANTHROPIC_API_KEY=sk-ant-api03-...
# Python code
import os
api_key = os.getenv("ANTHROPIC_API_KEY")The frontend doesn't need to send the key - the backend handles it.
# GET request
curl http://localhost:8000/api/reports
# GET with query params
curl "http://localhost:8000/api/reports?page=1&type=youtube"
# POST with JSON body
curl -X POST http://localhost:8000/api/analysis/youtube \
-H "Content-Type: application/json" \
-d '{"url": "https://youtube.com/watch?v=abc"}'
# DELETE
curl -X DELETE http://localhost:8000/api/reports/42- Open DevTools (F12)
- Go to Network tab
- Filter by "Fetch/XHR"
- Click a request to see details
FastAPI generates interactive docs:
- Start the backend
- Open http://localhost:8000/docs
- Try out endpoints directly!
Request:
GET /api/reports?page=2&page_size=20
Response:
{
"reports": [...],
"total": 150,
"page": 2,
"page_size": 20
}Request:
POST /api/analysis/youtube
{"url": "https://..."}
Response (201 Created):
{
"job_id": "abc-123",
"status": "pending"
}Request:
GET /api/reports/42
Response:
{
"id": 42,
"title": "Report Title",
"content": "..."
}Request:
GET /api/reports/99999
Response (404 Not Found):
{
"detail": "Report not found"
}async function waitForCompletion(jobId) {
while (true) {
const response = await fetch(`/api/analysis/jobs/${jobId}`);
const data = await response.json();
if (data.status === 'completed') {
return data.result_filepath;
}
if (data.status === 'failed') {
throw new Error(data.error_message);
}
// Wait 2 seconds before checking again
await new Promise(r => setTimeout(r, 2000));
}
}What does this request do?
PUT /api/users/123 HTTP/1.1
Host: api.example.com
Content-Type: application/json
{"name": "Bob", "age": 31}
Write a cURL command to:
- POST to
/api/reports - With JSON body:
{"title": "My Report"} - With header
Authorization: Bearer mytoken
Design REST endpoints for a todo list app with:
- List all todos
- Create a todo
- Mark a todo as complete
- Delete a todo
Given this response, what's the total number of reports, and what page are we on?
{
"data": [
{"id": 1, "title": "Report A"},
{"id": 2, "title": "Report B"}
],
"meta": {
"total": 50,
"page": 3,
"per_page": 2
}
}| Concept | What It Is |
|---|---|
| API | Interface for programs to talk |
| REST | Rules for designing APIs |
| HTTP | Protocol for requests/responses |
| Endpoint | Specific URL that does something |
| GET/POST/PUT/DELETE | What action to take |
| JSON | Data format for request/response |
| Status Code | Success (2xx), client error (4xx), server error (5xx) |
| Headers | Metadata about the request/response |
| Query Params | Filters in the URL (?key=value) |
| Body | Data sent with POST/PUT |
Now that you understand REST APIs, move on to:
- ASYNC_PROGRAMMING.md - Handle API calls properly
- FASTAPI_GUIDE.md - Build APIs with Python
REST API Basics - Created 2025-12-25
Built with Claude Code powered by Claude Opus 4.5