An API request contains more than the JSON a client sends. Its target identifies what the client wants to access, its headers describe the message, and its body supplies data. A response has the same separation between protocol information and application data.
Understanding those parts makes API documentation, network traces, and unexpected responses easier to read.
This chapter examines the structure of requests and responses through a fictional bookstore API, including successful responses, errors, and messages without bodies.
An HTTP/1.1 message starts with a line describing the request or response, then continues with header fields, a blank line, and an optional body. A header field carries named metadata. The body carries content such as JSON, text, or file bytes.
The following diagram shows the order of those parts for messages with bodies:
The blank line ends the header section. It does not, by itself, tell the receiver how much body data follows.
The complete message examples use HTTP/1.1's readable syntax before HTTPS encryption. Its start line and header lines end with carriage return and line feed, which protocol descriptions write as CRLF. Markdown displays them as ordinary line breaks.
HTTP/2 and HTTP/3 carry the same basic information in binary frames, which are structured units of protocol data. They do not send these textual start lines or use a blank line to separate headers from content. Their request control fields include :method, :scheme, :authority, and :path; responses use :status. These pseudo-header fields represent protocol control information rather than ordinary application headers. A developer tool can display a readable reconstruction of either format.
Suppose a signed-in customer submits an order to https://api.bookstore.example/orders. This example assumes the bookstore identifies the customer from a bearer token, checks the account's permission, and accepts book identifiers and quantities as order input. EXAMPLE_TOKEN is a nonfunctional placeholder.
The request is:
The request line, POST /orders HTTP/1.1, contains three parts. POST is the method, /orders is the request target, and HTTP/1.1 is the protocol version. Method names are case-sensitive; use the defined uppercase spelling for standard methods.
In this API, submitting to /orders asks the service to create an order. The bookstore defines the accepted JSON fields and the business checks that creation requires. The version token describes HTTP, not a version of the bookstore's API.
Read the headers according to their individual roles:
HTTP/1.1 requires a Host field in requests. When the URL uses an explicit non-default port, the host field includes it, such as api.bookstore.example:8443. Reaching the correct IP address does not remove the need to identify the intended host.
Header names are case-insensitive: Content-Type and content-type name the same field. Values follow each field's own rules, so do not lowercase arbitrary values such as credentials. HTTP/2 and HTTP/3 require lowercase field names in their encoded messages.
In application code, normally let the HTTP library construct transport details such as Host and body length. Supply the URL, credentials, content, and supported options through its interface. Manually calculated lengths can become incorrect when the body changes.
The request target is not always a full URL. For a typical HTTP/1.1 request that a client sends directly to an origin server, it contains the path and any query string. Other target forms exist for forward proxies and special operations; the examples here use the ordinary path-and-query form.
Consider a public catalog search with this illustrative URL:
Its components have different destinations in the request:
The scheme determines secure access, the hostname becomes Host, and the path and query appear in the request line. The client does not send the fragment, the part after #, in the HTTP request target.
The resulting request is:
Here, /books selects the catalog collection. The bookstore defines author as a filter and limit as a maximum result count. Those parameter names and their meanings are API choices.
%20 is a percent-encoded space. Percent encoding represents a byte using % and then two hexadecimal digits. A URL builder should encode parameter values so that characters inside a value do not accidentally become URL delimiters. For example, an ampersand in an author's name must remain part of that value rather than introduce another parameter.
The fragment could help a browser navigate within a representation, but #results cannot tell the server to filter books. Likewise, putting a credential in a fragment would not send it to the API as authentication.
The order request body contains one item with a quantity of two. HTTP carries those bytes; the bookstore's contract defines items, bookId, and quantity.
A media type identifies a content format. application/json tells the receiver to interpret this body as JSON. It does not prove that the JSON is valid or that the order satisfies business rules. A body containing quantity: 0 can be valid JSON while failing the bookstore's requirement that quantity be at least one.
Accept and Content-Type describe different directions. In the order request, Content-Type describes what the client sends, while Accept describes what it accepts back. Sending Accept: application/json does not label an unlabeled request body as JSON.
HTTP also carries other formats, including uploaded images and form data. Do not assume that every body is JSON just because people call the service an API.
Each JSON body occupies one line without a trailing newline. Content-Length counts bytes, not characters, and excludes the start line and headers. The order body has 47 bytes. Non-ASCII characters can require multiple bytes in UTF-8, so counting visible characters is not a reliable length calculation.
The catalog search has no body. Its inputs are already in the request target. A request without a body does not need an empty JSON object to make it a complete message.
Assume the bookstore validates the order request, creates order_731, and returns the following response:
The status line contains the HTTP version, the numeric status code, and a reason phrase. Here, 201 indicates creation. Created is human-readable text; client logic should use the numeric code rather than match the phrase. HTTP/2 and HTTP/3 do not carry a reason phrase.
Status codes fall into five broad classes:
The individual code provides the specific meaning. A client can receive informational responses before the final response, so not every status line marks the completed result.
For this order, Location identifies the created resource. Its relative value resolves to https://api.bookstore.example/orders/order_731. This 201 response does not tell the client to redirect automatically or make another request immediately.
Date records when the message originated. Content-Type describes the response body's format, and Cache-Control: no-store tells caches not to store this response. The bookstore uses that policy for these private order messages.
The JSON field status: "pending" is the order's business state. In this example it means the created order awaits fulfillment. It does not override the HTTP status or mean that order creation is still unfinished. Reading the protocol status and the body together avoids that ambiguity.
Errors use the same message structure as successful responses. What changes is the status and the content describing the outcome.
Suppose the customer changes the submitted quantity from 2 to 0. The request remains valid JSON, but this bookstore rejects it with:
This API chooses 422 for that validation failure. INVALID_QUANTITY and the surrounding JSON shape are application conventions, not HTTP-defined fields. The service creates no order in this case.
Now assume the original request is well formed and the token identifies an account that lacks order-creation permission. The bookstore returns:
This is an authorization failure: the identified account lacks permission to perform the operation. Changing the quantity would not address it. The example assumes a valid credential, rather than a missing or invalid token.
Both error bodies happen to be JSON because that is this service's contract. A gateway-generated error could use a different media type. A client that blindly parses every response as its successful order schema can hide the actual failure behind a parsing error.
Message framing tells the receiver where a message ends. In the order examples, Content-Length supplies the body length. HTTP/1.1 can also use chunked transfer coding, where each body chunk has a length and a final zero-length chunk ends the data, with optional trailer fields afterward. Trailers are metadata the sender transmits after the body. A sender must not send both Transfer-Encoding and Content-Length in the same message.
Framing is separate from the JSON's structure. Finding a closing brace is not how an HTTP receiver determines the message boundary. HTTP/2 and HTTP/3 use their framing and stream mechanisms instead of HTTP/1.1 chunked transfer coding.
Some responses cannot contain a body. For example, assume a separate operation successfully removes a book from the customer's wishlist and the API promises no result content:
This response ends after the header section. A 204 must not contain a body or a Content-Length field, including Content-Length: 0. Appending {} would violate the response's semantics.
Responses to HEAD also have no body, even when their headers describe the representation a corresponding GET would return. These rules show why a client must consider the request method and response status before deciding whether to parse content.
For a JSON-oriented API client, the interpretation flow can look like this:
“This response permits a body” does not guarantee that content exists. A zero-byte body needs no JSON parsing. When content does exist, successful transfer does not guarantee valid JSON, and valid JSON does not guarantee the expected application fields. Check separately that the whole message arrived, that you can decode its content, and that the decoded data follows the API's rules.
An HTTP request identifies an operation and target, supplies metadata through headers, and may carry a body. A response reports a protocol outcome, supplies its own headers, and may return application data. HTTP/1.1 expresses those parts with start lines and separators, while newer versions encode them differently.
Read each part according to its role: the request target selects what to access, media types describe content, status codes report HTTP outcomes, and body fields carry application meaning. Message framing and body restrictions determine what the client can safely read and parse.