You now know the verbs, status codes, and headers. This lesson zooms in on the body — the actual payload that flows back and forth — and on the data formats that wrap it. JSON is the dominant format for modern APIs and you'll meet it in 90% of your testing work. XML is the older format you'll meet in banking, government, and any system old enough to have been written before 2010. Knowing how to read both is part of being a fluent API tester.
A request, end to end
A complete HTTP request has four parts:
- The start line — the verb, the path, and the HTTP version.
- The headers — metadata.
- A blank line — separates headers from the body.
- The body — optional, depending on the method.
A typical authenticated POST looks like this:
POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJI...
Accept: application/json
{
"name": "Alice Smith",
"email": "alice@test.com",
"role": "admin"
}Read that top to bottom. The first line says what (POST) and where (/api/users). The headers say how (JSON, with this token, expecting JSON back). Then the empty line. Then the body — what you're actually sending.
GET requests have no body — everything they need fits in the URL and headers.
A response, end to end
The response mirrors the structure:
- The status line — HTTP version, status code, reason phrase.
- The headers — metadata about the response.
- A blank line.
- The body — the actual data.
HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/users/42
X-Request-Id: req_abc123
Content-Length: 138
{
"id": 42,
"name": "Alice Smith",
"email": "alice@test.com",
"role": "admin",
"createdAt": "2026-01-15T10:30:00Z"
}Three things matter for tests: the status line (was it successful?), the headers (where's the new resource? what did the server actually return?), and the body (does it contain the right data?).
JSON — the format you'll live in
JSON stands for JavaScript Object Notation. Despite the name it's language-agnostic — every language has a JSON parser. The structure is intentionally minimal:
- Objects: curly braces with
"key": valuepairs.{ "name": "Alice", "age": 30 } - Arrays: square brackets with comma-separated values.
[ "admin", "tester", "viewer" ] - Types:
string,number,boolean,null,object,array. That's all six. No dates (use ISO strings), no integers separate from floats, no comments.
Real-world payloads nest objects and arrays as deep as needed:
{
"id": 42,
"name": "Alice Smith",
"active": true,
"roles": ["admin", "tester"],
"address": {
"city": "London",
"postcode": "EC1A 1AA"
},
"orders": [
{ "id": 9001, "total": 49.99 },
{ "id": 9002, "total": 12.50 }
]
}Two rules that catch testers out:
- Keys must be quoted strings.
{name: "Alice"}is not valid JSON.{"name": "Alice"}is. - No trailing commas.
{"a": 1,}is not valid JSON. Some languages accept them; the JSON spec doesn't.
XML — the format you'll occasionally meet
XML stands for eXtensible Markup Language. It looks like HTML's stricter cousin: opening and closing tags around every value.
<user>
<id>42</id>
<name>Alice Smith</name>
<email>alice@test.com</email>
<roles>
<role>admin</role>
<role>tester</role>
</roles>
</user>XML supports attributes (<user id="42">) and namespaces and DTD schemas — features JSON doesn't have. The trade-off is verbosity: an XML payload is typically two to three times the size of the equivalent JSON.
You'll mostly meet XML in:
- SOAP APIs — common in banking, insurance, legacy enterprise systems.
- Configuration files — Maven
pom.xml, AndroidAndroidManifest.xml. - Government and regulatory feeds — XBRL filings, healthcare HL7.
If a server returns XML when you expected JSON, check the Content-Type header and the Accept header you sent. Some endpoints will switch format based on what you ask for.
JSON vs XML at a glance
Same data, two formats
JSON
Lightweight, minimal syntax
Curly braces, square brackets, key/value pairs. ~50% the size of equivalent XML.
Native to JavaScript
Browsers and Node.js parse JSON without any library.
Six types only
string, number, boolean, null, object, array — no attributes, no namespaces.
Used by 90% of modern APIs
REST and GraphQL APIs default to JSON. The format you'll work with daily.
XML
Verbose, tag-based syntax
Every value wrapped in opening and closing tags. Larger payloads, but human-readable.
Supports attributes & namespaces
Each tag can carry attributes and belong to a namespace — useful for complex schemas.
Strong schema validation
XSD schemas describe exact structure and types. Tooling enforces compliance.
Used by SOAP and legacy APIs
Banking, insurance, and government systems still rely heavily on XML.
As a working QA engineer: get fluent in JSON. Recognise XML and know how to read it; you don't need to write XML by hand to test an API that uses it.
A real call with curl
Putting everything together — verb, URL, headers, body — into one curl command:
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJI..." \
-d '{"name": "Alice", "email": "alice@test.com"}'Read the flags:
-X POST— the method.-H "..."— a header (use one-Hper header).-d '...'— the body. Single quotes around the JSON keep your shell from interpreting the inner double quotes.- The URL goes last.
That command is functionally identical to whatever Postman or your test framework eventually sends. When you debug a failing test, dropping back to a raw curl line is one of the fastest ways to isolate "is this the test or the API?"
Reading bodies in Postman, Insomnia, and code
Whatever tool you use, the body is presented in a pretty-printed form — indented, syntax-highlighted, often with a tree view. The raw bytes on the wire are usually one long line; the tool is doing you a favour by formatting them. If you need to see the exact bytes (e.g. to debug a whitespace bug), most tools have a "raw" or "source" toggle.
In code, every language has a JSON parser:
- Python:
import json; json.loads(text) - JavaScript:
JSON.parse(text) - Java: Jackson, Gson, or built-in
JsonReader - Ruby:
JSON.parse(text)
Test code typically calls a helper that gives you the response as a parsed object — response.json() in most clients — and lets you assert against it like any other data structure.
⚠️ Common mistakes
- Mixing single and double quotes inside JSON. JSON requires double quotes around strings. Single quotes are not valid JSON, even if your shell prefers them. The JSON
'{"name": "Alice"}'is the value you pass to curl — the inner quotes aroundnameandAlicemust be double quotes. - Confusing form-encoded with JSON bodies.
name=Alice&email=alice@test.comis form-encoded;{"name": "Alice"}is JSON. APIs accept one or the other (sometimes both), and getting it wrong usually returns a 400 or 415. - Trusting
Content-Typeto be set. Some clients omitContent-Type: application/jsonwhen sending a JSON body. The server may then refuse to parse it. Always confirm both the header and the body match.
🎯 Practice task
Read and compose real payloads. 20-25 minutes.
- Run
curl -i https://jsonplaceholder.typicode.com/users/1. Copy the response. Identify each part: status line, headers, body. Confirm the body is valid JSON. - Pretty-print the body. Pipe it through
python3 -m json.tool(or paste it into jsonlint.com). Walk through the keys — which are strings, which are numbers, which are nested objects? - Compose a POST. Run
curl -X POST https://jsonplaceholder.typicode.com/posts -H "Content-Type: application/json" -d '{"title":"my first post","body":"hello","userId":1}'. Inspect the response. - Break the JSON deliberately. Repeat the POST but use a single quote inside the body (
-d "{'title': 'broken'}"). What happens? - Stretch: find an XML API in the wild — try the BBC News RSS feed. Save the response with
curl > news.xml. Open the file and identify two ways the structure differs from JSON.
You now know how to read and write the payloads that ride inside every API call. Chapter 2 shifts gears: from what APIs are to why and how you actually test them.