Web Technologies

What is a REST API? (Complete Guide 2026)

By the Scrappey Research Team

What is a REST API? (Complete Guide 2026) — conceptual illustration
On this page

A REST API is a standard way for programs to read and change data over the web using ordinary HTTP requests. This is the complete 2026 guide.

Quick facts

RESTRepresentational State Transfer
TransportHTTP verbs on resource URLs
FormatUsually JSON
StatelessEach request is self-contained
AuthAPI keys, OAuth, tokens

What is REST?

REST (Representational State Transfer) is a set of conventions for building web APIs. Instead of inventing a custom protocol, you expose your data as "resources" (like users or posts) and let clients act on them with normal HTTP requests. Because it reuses HTTP, REST is simple to build, easy to scale, and works with any language that can make a web request.

Core Principles

1. Stateless Communication

  • Each request contains all necessary information
  • No client context stored on server
  • Improves scalability and reliability
  • Easier to cache and debug

Stateless means the server remembers nothing between requests: every call must carry everything needed to handle it (like a login token). Any server in a pool can answer any request, which is why this scales well.

2. Standard HTTP Methods

Each HTTP method maps to a basic data operation, summarized as CRUD (Create, Read, Update, Delete):

# CRUD Operations
GET    /api/users     # Read users
POST   /api/users     # Create user
PUT    /api/users/1   # Update user
DELETE /api/users/1   # Delete user

# Additional Methods
PATCH  /api/users/1   # Partial update
HEAD   /api/users     # Get headers only

Implementation Examples

Here is a minimal API written with Flask, a small Python web framework. Each function below is an "endpoint" — a URL the client can call.

1. Basic REST API in Python

from flask import Flask, jsonify, request

app = Flask(__name__)

# GET endpoint
@app.route('/api/users', methods=['GET'])
def get_users():
    return jsonify({
        'users': users,
        'total': len(users)
    })

# POST endpoint
@app.route('/api/users', methods=['POST'])
def create_user():
    user = request.json
    users.append(user)
    return jsonify(user), 201

# PUT endpoint
@app.route('/api/users/<int:user_id>', methods=['PUT'])
def update_user(user_id):
    user = next((u for u in users if u['id'] == user_id), None)
    if user:
        user.update(request.json)
        return jsonify(user)
    return jsonify({'error': 'User not found'}), 404

2. Response Formats

Responses are usually JSON. A good API keeps a consistent shape: data on success, a clear error object on failure.

// Success Response
{
    "data": {
        "id": 1,
        "name": "John Doe",
        "email": "[email protected]"
    },
    "meta": {
        "timestamp": "2025-01-20T10:00:00Z"
    }
}

// Error Response
{
    "error": {
        "code": "NOT_FOUND",
        "message": "User not found",
        "details": "No user exists with ID 123"
    }
}

Best Practices

1. URL Structure

Treat each URL as a path to a resource. Use plural nouns, nest related resources, and put options in query parameters (the part after ?):

# Resource Hierarchy
/api/v1/users                    # User collection
/api/v1/users/{id}              # Specific user
/api/v1/users/{id}/posts        # User's posts
/api/v1/users/{id}/posts/{id}   # Specific post

# Query Parameters
/api/v1/users?role=admin        # Filtering
/api/v1/users?sort=name         # Sorting
/api/v1/users?page=2&limit=10   # Pagination

2. Authentication

Authentication proves who is calling. A common approach is JWT (JSON Web Token) — the server hands back a signed token at login, and the client sends it on every later request to prove access:

# JWT Authentication Example
from flask_jwt_extended import jwt_required, create_access_token

@app.route('/api/login', methods=['POST'])
def login():
    username = request.json.get('username')
    password = request.json.get('password')
    
    if authenticate_user(username, password):
        access_token = create_access_token(identity=username)
        return jsonify({'token': access_token})
    
    return jsonify({'error': 'Invalid credentials'}), 401

@app.route('/api/protected', methods=['GET'])
@jwt_required()
def protected_route():
    return jsonify({'message': 'Access granted'})

3. Rate Limiting

Rate limiting caps how many requests a caller can make in a given window, protecting the server from overload or abuse:

from flask_limiter import Limiter

limiter = Limiter(
    app,
    key_func=get_remote_address,
    default_limits=["200 per day", "50 per hour"]
)

@app.route('/api/users')
@limiter.limit("1 per second")
def get_users():
    return jsonify(users)

Common Features

1. Pagination

When a collection is large, return it in pages instead of all at once. The client asks for a page number and size; the server returns that slice plus totals:

@app.route('/api/users')
def get_users():
    page = int(request.args.get('page', 1))
    limit = int(request.args.get('limit', 10))
    start = (page - 1) * limit
    end = start + limit
    
    return jsonify({
        'data': users[start:end],
        'meta': {
            'total': len(users),
            'page': page,
            'limit': limit,
            'pages': ceil(len(users) / limit)
        }
    })

2. Filtering and Sorting

Let clients narrow and order results through query parameters, so they fetch only what they need:

@app.route('/api/users')
def get_users():
    # Filtering
    role = request.args.get('role')
    if role:
        filtered_users = [u for u in users if u['role'] == role]
    
    # Sorting
    sort_by = request.args.get('sort')
    if sort_by:
        filtered_users.sort(key=lambda x: x[sort_by])
    
    return jsonify(filtered_users)

Security Considerations

1. Input Validation

Never trust incoming data. Validate it against a schema (a definition of allowed fields and types) before using it, and reject anything malformed:

from marshmallow import Schema, fields

class UserSchema(Schema):
    name = fields.Str(required=True)
    email = fields.Email(required=True)
    age = fields.Int(validate=lambda n: n >= 0)

@app.route('/api/users', methods=['POST'])
def create_user():
    schema = UserSchema()
    try:
        data = schema.load(request.json)
        # Process validated data
        return jsonify(data), 201
    except ValidationError as err:
        return jsonify(err.messages), 400

2. CORS Handling

CORS (Cross-Origin Resource Sharing) is the browser rule that controls which websites may call your API from their own pages. Configure it to allow only the origins, methods, and headers you trust:

from flask_cors import CORS

# Configure CORS
CORS(app, resources={
    r"/api/*": {
        "origins": ["https://allowed-domain.com"],
        "methods": ["GET", "POST", "PUT", "DELETE"],
        "allow_headers": ["Content-Type", "Authorization"]
    }
})

Remember: A well-designed REST API should be intuitive, consistent, and secure while following established conventions and best practices.

Related terms

Concept map

How REST API? (Complete Guide 2026) connects

The terms most directly tied to this one. Hover a node to see its neighbours, click to preview, drag to rearrange.

0 terms · 0 connections
You are here · Web Technologies
Building map…

Frequently asked questions

Should I use a REST API instead of scraping?

If an official API gives you the data you need, use it — APIs are more stable, faster, and explicitly allowed. Reach for scraping only when no API exists, or when the API leaves out data you actually need.

What makes an API "RESTful"?

Four things: each resource has its own URL, you act on it with standard HTTP verbs (GET/POST/PUT/DELETE), every request is stateless (self-contained), and the server returns meaningful status codes. Responses are conventionally sent as JSON.

How do I find a site's hidden API?

Open your browser's network tab and watch the XHR/fetch requests (the background calls a page makes) as it loads. Many sites fill their pages from internal JSON endpoints that you can often call directly yourself.

Last updated: 2026-05-31