APIs and Protocols
Request Formats
Read aloud is unavailable in this browser.
Constructing the Perfect API Request
Every interaction with an API begins with a request. Whether you're fetching data, creating a resource, or triggering an action, your request must be precisely constructed to communicate your intent clearly to the server.
A well-designed request format enables several critical outcomes:
- Clarity: The server immediately understands what you're asking for
- Validation: Malformed requests can be rejected early with helpful error messages
- Efficiency: Properly structured requests minimize parsing overhead
- Security: Correct parameter placement prevents injection attacks
- Maintainability: Consistent patterns make APIs easier to evolve
This page provides a comprehensive exploration of how API requests are structured, the different ways data can be passed, and the best practices that distinguish amateur APIs from professional-grade systems.
By the end of this page, you will understand the complete anatomy of HTTP requests, the different types of parameters (path, query, header, body), popular request body formats, content negotiation, and best practices for designing request interfaces that are intuitive, secure, and maintainable.
Anatomy of an HTTP Request
An HTTP request is a structured text message that follows a precise format defined by the HTTP specification (RFC 9110). Understanding each component is essential for both consuming and designing APIs.
The Four Components of an HTTP Request:
12345678910111213141516
POST /api/v2/orders HTTP/1.1Host: api.example.comContent-Type: application/jsonAuthorization: Bearer eyJhbGciOiJIUzI1NiIs...Accept: application/jsonContent-Length: 156X-Request-ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890 { "customer_id": 42, "items": [ {"product_id": 101, "quantity": 2}, {"product_id": 205, "quantity": 1} ], "shipping_address_id": 789}- •Request Line (Line 1): Contains the HTTP method (
POST), the request target (/api/v2/orders), and the HTTP version (HTTP/1.1). This line tells the server what action to perform on which resource. - •Headers (Lines 2-7): Key-value pairs providing metadata about the request. Headers specify content types, authentication credentials, caching preferences, and custom information. Each header is on its own line in the format
Header-Name: value. - •Empty Line (Line 8): A blank line (CRLF only) separates headers from the body. This delimiter is mandatory; without it, the server can't distinguish headers from content.
- •Request Body (Lines 9-16): The payload containing the data to be processed. Not all requests have bodies—GET and DELETE typically don't. The body format is indicated by the
Content-Typeheader.
Request Target Formats:
The request target (URI) can take several forms:
| Form | Example | When Used |
|---|---|---|
| Origin-form | /api/users?active=true | Most common; path + query |
| Absolute-form | http://api.example.com/users | Used with proxies |
| Authority-form | api.example.com:443 | CONNECT method only (tunneling) |
| Asterisk-form | * | OPTIONS method for server capabilities |
In practice, virtually all API requests use the origin-form: the path portion of the URL optionally followed by a query string.
In HTTP/2 and HTTP/3, the request line doesn't exist as a separate line. Instead, the method, path, scheme, and authority are sent as pseudo-headers (:method, :path, :scheme, :authority). However, the logical components remain the same, and most HTTP clients abstract these differences.
Parameter Types: Where Data Lives in a Request
API requests can carry data in four distinct locations, each serving different purposes. Choosing the right location for each piece of data is a critical design decision.
1. Path Parameters
Path parameters are embedded directly in the URL path. They identify specific resources.
1234567891011
URL Pattern: /users/{userId}/orders/{orderId}Actual Request: /users/42/orders/1001 Path parameters: userId = 42 orderId = 1001 Use cases: - Resource identification: /products/SKU-12345 - Hierarchy navigation: /organizations/acme/teams/engineering/members - Versioning: /api/v2/users (though v2 is often a path segment, not a parameter)Path Parameter Best Practices:
- Use path parameters for required resource identifiers
- Keep paths readable: prefer
/users/42over/users?id=42 - Use nouns for resources, not verbs:
/orders/123not/getOrder/123 - Maintain consistent casing (kebab-case or snake_case)
- Avoid deep nesting beyond 3-4 levels
2. Query Parameters
Query parameters appear after the ? in the URL. They modify the request without changing the resource being addressed.
1234567891011121314151617
GET /api/products?category=electronics&min_price=100&max_price=500&sort=price&order=asc&limit=20&offset=40 Query parameters: category = electronics (filtering) min_price = 100 (filtering) max_price = 500 (filtering) sort = price (sorting) order = asc (sorting direction) limit = 20 (pagination: items per page) offset = 40 (pagination: skip items) Common query parameter patterns: Filtering: ?status=active&created_after=2024-01-01 Searching: ?q=wireless+headphones Pagination: ?page=3&per_page=25 or ?cursor=eyJpZCI6MTIzfQ== Field selection: ?fields=id,name,email Expansion: ?expand=author,commentsQuery Parameter Best Practices:
- Use query parameters for optional modifiers (filters, sorting, pagination)
- Keep parameters simple and atomic; avoid complex nested structures
- Use consistent naming:
sort_byvssortByvssort—pick one - Document default values clearly
- Consider URL length limits (~2000 chars practical maximum)
3. Header Parameters
Headers carry metadata about the request rather than the business payload.
| Header | Purpose | Example Value |
|---|---|---|
| Content-Type | Format of request body | application/json |
| Accept | Desired response format | application/json, text/html;q=0.9 |
| Authorization | Authentication credentials | Bearer eyJhbGciOiJIUzI1NiIs... |
| Accept-Language | Preferred language | en-US,en;q=0.9,es;q=0.8 |
| Accept-Encoding | Supported compression | gzip, deflate, br |
| Cache-Control | Caching behavior | no-cache |
| If-None-Match | Conditional request (ETag) | "abc123" |
| If-Modified-Since | Conditional request (date) | Sat, 15 Jan 2024 10:00:00 GMT |
| X-Request-ID | Request tracking | a1b2c3d4-e5f6-7890 |
| X-Api-Key | API key authentication | sk_live_abc123def456 |
The X- prefix for custom headers is deprecated (RFC 6648). Modern APIs should use app-specific prefixes like Stripe-Version or GitHub-Hookshot. Avoid generic names that might conflict with future standards.
4. Request Body
The request body carries the primary data payload for operations that create or modify resources. It's used with POST, PUT, and PATCH methods (and optionally DELETE).
| Location | When to Use | Examples |
|---|---|---|
| Path | Required resource identifiers | /users/42, /orders/1001 |
| Query | Optional modifiers, filters | ?status=active&limit=20 |
| Header | Request metadata, auth | Authorization, X-Request-ID |
| Body | Complex payloads, create/update data | JSON objects, form data |
Request Body Formats
The request body format is indicated by the Content-Type header. Different formats serve different purposes, and APIs may support multiple formats.
JSON (application/json)
JSON is the dominant format for modern API requests. It's human-readable, widely supported, and maps naturally to programming language data structures.
12345678910111213141516171819202122232425262728293031323334
// Simple object{ "username": "alice", "email": "alice@example.com", "password": "securePassword123"} // Nested structures{ "order": { "customer_id": 42, "items": [ { "product_id": 101, "quantity": 2, "options": { "color": "blue", "size": "L" } } ], "metadata": { "source": "mobile_app", "promo_code": "SAVE20" } }} // Arrays at root level (batch operations)[ {"id": 1, "status": "completed"}, {"id": 2, "status": "cancelled"}, {"id": 3, "status": "completed"}]Form URL-Encoded (application/x-www-form-urlencoded)
The traditional HTML form format. Values are encoded as key=value pairs, URL-encoded, and joined with &. Limited to flat key-value pairs.
1234
POST /oauth/token HTTP/1.1Content-Type: application/x-www-form-urlencoded grant_type=authorization_code&code=abc123&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback&client_id=app123Use cases for URL-encoded forms:
- OAuth token exchanges (specification requires this format)
- Simple login forms
- Legacy system integration
- Simple key-value submissions
Multipart Form Data (multipart/form-data)
Used when requests contain files alongside regular data. Each part is separated by a boundary string and can have its own content type.
12345678910111213
POST /api/users/42/avatar HTTP/1.1Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW ------WebKitFormBoundary7MA4YWxkTrZu0gWContent-Disposition: form-data; name="description" Profile photo update------WebKitFormBoundary7MA4YWxkTrZu0gWContent-Disposition: form-data; name="avatar"; filename="photo.jpg"Content-Type: image/jpeg <binary file content>------WebKitFormBoundary7MA4YWxkTrZu0gW--Binary Formats
For high-performance internal APIs, binary formats reduce payload size and parsing overhead:
| Format | Content-Type | Human Readable | Best For |
|---|---|---|---|
| JSON | application/json | Yes | Most API payloads |
| URL-encoded | application/x-www-form-urlencoded | Yes | OAuth, simple forms |
| Multipart | multipart/form-data | Partly | File uploads |
| XML | application/xml | Yes | Enterprise/SOAP APIs |
| Protocol Buffers | application/protobuf | No | gRPC, high-performance |
| MessagePack | application/msgpack | No | JSON-like but binary |
Content Negotiation
Content negotiation is the mechanism by which clients and servers agree on content formats. It allows a single endpoint to serve different representations of the same resource.
Request-Side Negotiation (Client Preferences):
Clients express preferences through headers:
12345
GET /api/products/123 HTTP/1.1Accept: application/json, application/xml;q=0.8, */*;q=0.1Accept-Language: en-US, en;q=0.9, es;q=0.8, fr;q=0.7Accept-Encoding: gzip, deflate, brAccept-Charset: UTF-8, ISO-8859-1;q=0.5- •Accept: Specifies acceptable response media types. Quality factors (
q=0.8) indicate relative preference. Higher values (max 1.0) are preferred. - •Accept-Language: Preferred languages for response content. Critical for internationalized APIs.
- •Accept-Encoding: Supported compression algorithms. Servers should compress responses when possible.
- •Accept-Charset: Character set preferences. UTF-8 is near-universal for modern APIs.
Quality Values (q-values):
Quality values range from 0 to 1 and indicate preference weight:
Accept: application/json, application/xml;q=0.8, text/plain;q=0.5, */*;q=0.1
Interpretation:
- application/json — q=1.0 (implicit, highest preference)
- application/xml — q=0.8 (acceptable, second choice)
- text/plain — q=0.5 (reluctantly acceptable)
- / — q=0.1 (fallback, anything is better than nothing)
Server-Side Response:
Servers respond with the negotiated format and indicate what was served:
1234567
HTTP/1.1 200 OKContent-Type: application/json; charset=utf-8Content-Language: en-USContent-Encoding: gzipVary: Accept, Accept-Language, Accept-Encoding {"id": 123, "name": "Widget", "price": 29.99}The Vary response header tells caches which request headers affect the response. If a response varies by Accept, caches must store separate versions for JSON and XML requests. Missing or incorrect Vary headers cause cache corruption.
406 Not Acceptable:
If the server cannot satisfy any of the client's content preferences, it should return 406 Not Acceptable with a list of available alternatives:
HTTP/1.1 406 Not Acceptable
Content-Type: application/json
{
"error": "not_acceptable",
"message": "Cannot provide response in requested format",
"available_formats": ["application/json", "application/xml"]
}
Request Validation
Robust APIs validate all incoming requests before processing. Good validation provides security, prevents errors, and gives clients actionable feedback.
Validation Layers:
- •Syntax Validation — Is the request well-formed? Valid JSON? Correct Content-Type? (400 Bad Request)
- •Authentication — Is the requester's identity verified? Valid token? (401 Unauthorized)
- •Authorization — Does the requester have permission for this operation? (403 Forbidden)
- •Schema Validation — Does the payload conform to the expected structure? Required fields? Correct types? (422 Unprocessable Entity)
- •Business Rule Validation — Does the request violate domain constraints? Duplicate entries? Invalid state transitions? (409 Conflict or 422)
JSON Schema for Validation:
JSON Schema provides a declarative way to specify validation rules:
1234567891011121314151617181920212223242526272829303132
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "required": ["email", "password"], "properties": { "email": { "type": "string", "format": "email", "maxLength": 255 }, "password": { "type": "string", "minLength": 8, "maxLength": 128 }, "username": { "type": "string", "pattern": "^[a-zA-Z][a-zA-Z0-9_]{2,29}$", "description": "3-30 chars, starts with letter, alphanumeric + underscore" }, "roles": { "type": "array", "items": { "type": "string", "enum": ["admin", "editor", "viewer"] }, "uniqueItems": true, "maxItems": 5 } }, "additionalProperties": false}Error responses should be specific, actionable, and secure. Indicate which field failed and why, but don't leak sensitive information. Don't say 'User with this email exists' if that reveals account existence—say 'Invalid credentials' for login failures.
Example Validation Error Response:
1234567891011121314151617181920212223
{ "error": "validation_error", "message": "Request validation failed", "details": [ { "field": "email", "code": "invalid_format", "message": "Must be a valid email address" }, { "field": "password", "code": "too_short", "message": "Must be at least 8 characters", "context": {"minimum": 8, "actual": 5} }, { "field": "roles[2]", "code": "invalid_enum", "message": "Must be one of: admin, editor, viewer", "context": {"allowed": ["admin", "editor", "viewer"], "received": "superuser"} } ]}Idempotency and Safe Requests
Understanding idempotency and safety is crucial for reliable API design. These properties affect how clients can retry requests and how servers handle duplicates.
Definitions:
- Safe: A method is safe if it doesn't modify server state. Safe requests can be repeated without side effects.
- Idempotent: A method is idempotent if making the same request multiple times has the same effect as making it once.
| Method | Safe | Idempotent | Explanation |
|---|---|---|---|
| GET | Yes | Yes | Reading data never modifies state |
| HEAD | Yes | Yes | Like GET, but no response body |
| OPTIONS | Yes | Yes | Queries server capabilities |
| PUT | No | Yes | Repeated PUTs to same resource yield same result |
| DELETE | No | Yes | Deleting twice = deleting once (resource is gone) |
| POST | No | No | Each POST may create a new resource |
| PATCH | No | No* | Depends on patch semantics |
Why Idempotency Matters:
Network communication is unreliable. Requests can timeout, connections can drop mid-response, and clients can't always know if the server processed a request.
Scenario without idempotency:
- Client sends POST to create an order
- Server creates the order and begins sending response
- Network interruption—client never receives response
- Client retries POST
- Server creates a duplicate order
Solution: Idempotency Keys
For non-idempotent operations, clients can provide idempotency keys:
12345
POST /api/orders HTTP/1.1Content-Type: application/jsonIdempotency-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890 {"customer_id": 42, "items": [{"product_id": 101, "quantity": 2}]}Server Implementation:
-
On first request with an idempotency key:
- Process the request
- Store the key → response mapping
- Return the response
-
On duplicate request with same key:
- Detect the key already exists
- Return the stored response
- Do not reprocess
Best Practices:
- Use UUIDs or ULIDs for idempotency keys
- Keys should be unique per logical operation (not per retry)
- Store keys with TTL (e.g., 24 hours)
- Return the identical response for replays
- Document idempotency guarantees in API specification
Where possible, make operations inherently idempotent through design. Use PUT with full state replacement instead of POST. Use 'set to X' instead of 'increment by Y'. When increment-style operations are necessary, idempotency keys are essential.
Request Size and Performance Considerations
Request design directly impacts performance. Large requests consume bandwidth, slow down mobile clients, and strain server resources.
Practical Limits:
| Component | Practical Limit | Impact of Exceeding |
|---|---|---|
| URL Length | ~2,000 chars | Some browsers/proxies reject; use POST instead |
| Total Headers | 8-16 KB | Server rejection (431 status) |
| Request Body | 1-10 MB typical | Server rejection (413 status); use chunked/streaming |
| Individual Header | 4-8 KB | May be rejected by proxies |
| Cookie Header | 4 KB per domain | Browser limitation; can't exceed |
Optimization Strategies:
1. Request Compression
Clients can compress request bodies to reduce transfer time:
12345
POST /api/data HTTP/1.1Content-Type: application/jsonContent-Encoding: gzip <gzip-compressed JSON body>2. Batch Requests
Instead of N individual requests, combine into one batch request:
1234567891011
// Instead of 100 separate POST /api/events requests:POST /api/events/batch { "events": [ {"type": "page_view", "page": "/home", "timestamp": "2024-01-15T10:30:00Z"}, {"type": "click", "element": "signup_button", "timestamp": "2024-01-15T10:30:05Z"}, {"type": "page_view", "page": "/signup", "timestamp": "2024-01-15T10:30:06Z"} // ... up to 100 events ]}- •Minimize payload size: Send only necessary fields; avoid verbose field names
- •Use compression: gzip reduces JSON by 70-90% typically
- •Batch related requests: Reduce round-trips, especially on high-latency connections
- •Prefer query params for filters: Cacheable GET with query beats POST with body
- •Use binary formats for internal APIs: Protocol Buffers are 2-10x smaller than JSON
- •Consider field selection: Let clients request only the fields they need (
?fields=id,name)
KEEP LEARNING
There’s more to understand.
You’re reading the introduction. Create a free account to read the complete lesson, save notes and keep track of your progress.