Learn how to test REST APIs with practical examples. Understand HTTP methods, status codes, headers, and authentication. Free online API testing tools included.
The first time I tested an API, I typed a URL into my browser and hit Enter. A wall of JSON came back, and I had no idea what I was looking at. Was that a success? Did the API work? I didn't know what a status code was, what headers did, or why the response body had fields I never asked for.
That was a long time ago. Since then, I've tested thousands of API endpoints — some mine, some from third-party services, some from public API directories with hundreds of providers. I've learned that API testing is not complicated, but it is precise. You need to know what you're sending, what you expect back, and how to tell the difference between "working" and "working correctly."
This guide covers everything a beginner needs to start testing REST APIs. We'll go from the basics — what REST even means — to practical curl commands you can run in your terminal right now. No fluff, no hand-waving, just the things that actually matter when you're staring at an endpoint and trying to figure out if it works.
REST stands for Representational State Transfer. It's an architectural style for building web APIs, not a protocol or a specification. There's no REST committee that certifies your API. There's a set of constraints, and most APIs follow some of them.
The core idea is simple: you have resources (users, products, orders, articles), and you interact with them using standard HTTP methods over standard URLs. A REST API exposes resources at predictable URLs and uses HTTP verbs to define what action you want to take.
Here's what makes an API "RESTful" in practice:
/users, /products/42, /orders/7/itemsMost APIs you'll encounter in the wild follow these conventions loosely. Some are strict about it, some are not. For testing purposes, what matters is understanding the HTTP layer underneath.
Every HTTP request has a method. The method tells the server what you want to do. There are more methods than most people realize, but these five cover 99% of what you'll test.
GET requests retrieve data without changing anything on the server. They're safe and idempotent — you can call them a hundred times and the result should be the same.
curl https://jsonplaceholder.typicode.com/posts/1This returns a single post. The server doesn't create, update, or delete anything. GET is the most common HTTP method, and it's what your browser uses every time you visit a URL.
POST sends data to the server to create a new resource. The request body contains the data for the new resource.
curl -X POST https://jsonplaceholder.typicode.com/posts \
-H "Content-Type: application/json" \
-d '{"title": "My New Post", "body": "This is the content.", "userId": 1}'The server should respond with the created resource, usually including a new id field. A successful POST typically returns a 201 Created status code.
PUT replaces an entire resource with the data you send. If the resource has ten fields and you send three, the other seven should be wiped out (in a strict REST implementation).
curl -X PUT https://jsonplaceholder.typicode.com/posts/1 \
-H "Content-Type: application/json" \
-d '{"id": 1, "title": "Updated Title", "body": "Updated body.", "userId": 1}'PATCH updates only the fields you specify. Everything else stays the same.
curl -X PATCH https://jsonplaceholder.typicode.com/posts/1 \
-H "Content-Type: application/json" \
-d '{"title": "Only the Title Changed"}'The difference between PUT and PATCH trips people up. PUT means "here's the complete new version." PATCH means "here's what changed." In practice, many APIs treat them the same way, but when testing you should verify which behavior the API actually implements.
DELETE removes a resource.
curl -X DELETE https://jsonplaceholder.typicode.com/posts/1A successful DELETE usually returns 200 OK with the deleted resource, or 204 No Content with an empty body. Some APIs return 202 Accepted if the deletion is queued for later processing.
Status codes are the first thing you should check when testing an API response. They tell you what happened before you even look at the body.
Location header.These mean you did something wrong.
These mean the server broke.
When testing, a 500 error is always a bug on the server side. A 400-level error might be intentional (you're testing that invalid input is rejected properly) or accidental (you made a typo in your request).
Headers carry metadata about the request and response. Most beginners ignore them and then spend hours debugging problems that a single header would explain.
Content-Type tells the server what format your request body is in:
curl -X POST https://api.example.com/data \
-H "Content-Type: application/json" \
-d '{"name": "test"}'Without this header, many APIs will reject your request with a 400 or 415 (Unsupported Media Type) error.
Accept tells the server what format you want the response in:
curl https://api.example.com/data \
-H "Accept: application/json"Authorization carries your credentials:
curl https://api.example.com/protected \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."Content-Type tells you the format of the response body. Check this — if you're expecting JSON but getting HTML, something is wrong.
X-RateLimit-Remaining and X-RateLimit-Reset tell you how many requests you have left before hitting the rate limit, and when the limit resets.
Location appears in 201 and 3xx responses, pointing to the URL of the created or redirected resource.
To see response headers with curl, add the -i flag:
curl -i https://jsonplaceholder.typicode.com/posts/1Or use -v for verbose output that shows both request and response headers:
curl -v https://jsonplaceholder.typicode.com/posts/1Most real-world APIs require authentication. There are several common patterns, and you need to know how to handle each one when testing.
The simplest form. You get a key from the API provider and include it in your request. Some APIs want it as a query parameter, others as a header.
# As a query parameter
curl "https://api.example.com/data?api_key=YOUR_KEY_HERE"
# As a header
curl https://api.example.com/data \
-H "X-API-Key: YOUR_KEY_HERE"You authenticate once (usually with username/password), get a token back, and include it in subsequent requests.
# Step 1: Get the token
curl -X POST https://api.example.com/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "secret"}'
# Response: {"token": "eyJhbGciOiJIUzI1NiIs..."}
# Step 2: Use the token
curl https://api.example.com/protected-resource \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."Username and password encoded in Base64. Simple but not secure over plain HTTP.
curl -u username:password https://api.example.com/dataOAuth is more complex. It involves redirecting users to an authorization server, getting an authorization code, exchanging it for an access token, and then using that token. For testing, you usually start with a token you've already obtained through the OAuth flow in a browser.
When testing authentication, always verify these scenarios:
curl is the Swiss Army knife of API testing. It's installed on virtually every operating system, it's scriptable, and it gives you complete control over every aspect of the HTTP request.
Let's walk through testing a full CRUD cycle. We'll use JSONPlaceholder, a free fake API for testing.
Create a resource:
curl -s -X POST https://jsonplaceholder.typicode.com/posts \
-H "Content-Type: application/json" \
-d '{"title": "Test Post", "body": "Testing the API.", "userId": 1}' \
| python3 -m json.toolThe -s flag silences the progress bar. Piping to python3 -m json.tool pretty-prints the JSON response.
Read the resource:
curl -s https://jsonplaceholder.typicode.com/posts/1 | python3 -m json.toolUpdate the resource:
curl -s -X PATCH https://jsonplaceholder.typicode.com/posts/1 \
-H "Content-Type: application/json" \
-d '{"title": "Updated Test Post"}' \
| python3 -m json.toolDelete the resource:
curl -s -X DELETE https://jsonplaceholder.typicode.com/posts/1Check status codes explicitly:
curl -s -o /dev/null -w "%{http_code}" https://jsonplaceholder.typicode.com/posts/1This outputs only the status code. Incredibly useful for scripting tests.
Most APIs support filtering, pagination, and sorting through query parameters:
# Filter posts by user
curl -s "https://jsonplaceholder.typicode.com/posts?userId=1" | python3 -m json.tool
# Pagination (common patterns)
curl -s "https://api.example.com/items?page=2&limit=10"
curl -s "https://api.example.com/items?offset=20&limit=10"Good API testing isn't just about verifying that things work. It's about verifying that things fail correctly.
# Request a resource that doesn't exist — expect 404
curl -s -o /dev/null -w "%{http_code}" https://jsonplaceholder.typicode.com/posts/99999
# Send invalid JSON — expect 400
curl -s -o /dev/null -w "%{http_code}" -X POST https://api.example.com/data \
-H "Content-Type: application/json" \
-d '{"invalid json'
# Send wrong data types — expect 400 or 422
curl -s -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"email": "not-an-email", "age": "not-a-number"}'When you need to chain requests — create a resource and then use its ID — you can capture the response:
# Create a resource and extract the ID
RESPONSE=$(curl -s -X POST https://jsonplaceholder.typicode.com/posts \
-H "Content-Type: application/json" \
-d '{"title": "Chain Test", "body": "Testing chained requests.", "userId": 1}')
POST_ID=$(echo $RESPONSE | python3 -c "import sys, json; print(json.load(sys.stdin)['id'])")
# Use the ID in the next request
curl -s "https://jsonplaceholder.typicode.com/posts/$POST_ID" | python3 -m json.toolWhen you're testing an API — whether it's one you built or one you're integrating with — there's a standard checklist of things to verify.
'; DROP TABLE users; -- in a field doesn't break anythingcurl -s -o /dev/null -w "DNS: %{time_namelookup}s\nConnect: %{time_connect}s\nTTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n" https://jsonplaceholder.typicode.com/postscurl is powerful, but it's not the only option. Depending on your workflow, you might want something more visual or more automated.
If you prefer a graphical interface over the command line, browser-based tools let you construct requests, inspect responses, and save collections of endpoints without installing anything.
On akousa.net, you'll find free online API testing tools in the developer tools section that let you build and send HTTP requests directly from your browser. You can set methods, headers, body content, and authentication — all without downloading desktop software. This is particularly useful when you're on a machine where you can't install applications or when you want to quickly test an endpoint without opening a terminal.
One of the best ways to learn API testing is to practice with real, public APIs. The akousa.net API directory catalogs over 505 public APIs across categories like weather, finance, entertainment, government data, and more. Each listing includes the base URL, authentication requirements, and a description of what the API offers.
Pick any API from the directory, read its documentation, and start sending requests. You'll learn more from thirty minutes of hands-on testing with a real API than from hours of reading about it.
Once you've tested an API manually and understand how it behaves, you'll want to automate those tests. Here's a minimal example using a simple shell script:
#!/bin/bash
BASE_URL="https://jsonplaceholder.typicode.com"
# Test 1: GET /posts returns 200
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/posts")
if [ "$STATUS" -eq 200 ]; then
echo "PASS: GET /posts returned 200"
else
echo "FAIL: GET /posts returned $STATUS (expected 200)"
fi
# Test 2: GET /posts/99999 returns 404
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/posts/99999")
if [ "$STATUS" -eq 404 ]; then
echo "PASS: GET /posts/99999 returned 404"
else
echo "FAIL: GET /posts/99999 returned $STATUS (expected 404)"
fi
# Test 3: POST /posts returns 201
STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X POST "$BASE_URL/posts" \
-H "Content-Type: application/json" \
-d '{"title": "Test", "body": "Test body", "userId": 1}')
if [ "$STATUS" -eq 201 ]; then
echo "PASS: POST /posts returned 201"
else
echo "FAIL: POST /posts returned $STATUS (expected 201)"
fiSave this as api-tests.sh, make it executable with chmod +x api-tests.sh, and run it. This is the foundation of API test automation — you're encoding your expectations into scripts that can run on every deployment.
For more sophisticated testing, frameworks like Jest, Vitest, Pytest, or Go's built-in testing package let you write structured test suites with assertions, setup/teardown hooks, and detailed reporting.
After years of working with APIs — building them, testing them, debugging them at 2 AM — here are the mistakes I see most often from people just getting started.
Every API has quirks. Maybe it uses snake_case for field names. Maybe pagination starts at 0 instead of 1. Maybe DELETE returns 200 instead of 204. You will waste hours guessing at things that are written down in the docs.
If your code only checks whether the response body looks right, you'll miss entire categories of bugs. An API might return a 200 with an error message in the body, or a 201 when you expected 200. Check the status code first, always.
When you move from testing to writing actual integration code, never hardcode API URLs or credentials. Use environment variables. This isn't just a best practice — it prevents you from accidentally committing your API keys to a public repository.
The happy path is the easy part. The real value of API testing is in the edge cases. What happens when you send an empty body? What happens when you send a field with 10,000 characters? What happens when the API is down? These are the scenarios that break production applications.
An API that returns correct data in 30 seconds is still broken for most use cases. Include response time in your test criteria, especially for endpoints that will be called frequently or in user-facing flows.
Here's the workflow I recommend for beginners who want to get serious about API testing:
This isn't theoretical advice. It's the exact process I follow every time I integrate a new API. The manual phase catches the weird edge cases. The automation phase makes sure they stay caught.
REST API testing comes down to understanding the HTTP protocol and being methodical about what you send and what you expect back. The tools don't matter nearly as much as the approach.
Know your HTTP methods: GET reads, POST creates, PUT replaces, PATCH updates, DELETE removes. Know your status codes: 2xx is good, 4xx is your fault, 5xx is the server's fault. Know your headers: Content-Type, Authorization, and Accept solve most debugging puzzles.
Start with curl. It forces you to understand every part of the request. Move to graphical tools when you need convenience. Move to automated test suites when you need reliability.
And practice with real APIs. The best way to get comfortable with API testing is to open up a public API directory, pick something that interests you, and start sending requests. You'll be surprised how quickly it clicks once you start doing it.