How to Test REST APIs Step by Step
This tutorial walks through a practical REST API testing workflow using the Simple API. The Simple API does not require authentication, so we can concentrate on requests, responses, data, status codes, headers, and the evidence we collect while testing.
Our aim is to learn how the API behaves, compare that behaviour with the documentation and our expectations, and build a useful model of the application.
What You Will Practise
In this tutorial we will:
- read the API documentation
- send a first
GETrequest - inspect a response
- create a new item with
POST - verify that the item was really created
- vary the request data and headers
- test validation errors
- update and delete an item
- decide what evidence to keep
- decide what is worth automating
Each step builds on the previous one.
It is important to remember that API testing is stateful. A POST request might create data. A PATCH request might change data. A DELETE request might remove data. We are not just issuing requests, we are testing a System and we need to check that the system state changes and is reflected in the responses.
Start With a Practice API
For this tutorial, use the Simple API:
- Simple API overview
- Simple API documentation
- Simple API Swagger UI
- Simple API OpenAPI files
- Simple API practice guide
The main endpoint we will use is:
/simpleapi/items
The Simple API models store inventory items. Each item has fields such as:
idtypeisbn13pricenumberinstock
The id is generated by the server. When we create an item, we should not send an id in the request body.
The isbn13 value must be unique. This makes it a good field for testing because it creates a real data rule that we can check.
Read the Documentation First
Before sending requests, read the documentation and build a small model of what the API claims to support.
Look for:
- endpoints
- HTTP methods
- required fields
- optional fields
- field types
- status codes
- response formats
- error responses
- headers such as
AcceptandContent-Type
The SimpleAPI has two official documentation sources:
For the Simple API, the documentation tells us that we can work with a collection of items and individual item resources.
Collection endpoint:
/simpleapi/items
Individual item endpoint:
/simpleapi/items/{id}
There is also a helper endpoint that returns a random ISBN:
/simpleapi/randomisbn
Use the documentation as a starting point, not as proof, and certainly not as a limit (always test beyond the documentation). Having said that,one of the jobs of API testing is to compare the documented behaviour with the actual behaviour.
If you want more background on the concepts, read the reference pages:
- Web Application and HTTP Basics
- HTTP Basics
- HTTP Methods and Verbs
- REST API Basics
- API Testing Concepts and Coverage
Send a First GET Request
Start with a read-only request.
GET https://apichallenges.eviltester.com/simpleapi/items
Accept: application/json
Try it in the built-in HTTP client:
GET /simpleapi/items to list Simple API items
Use the In Browser tab to send the request from this page. The client also has cURL and wget tabs if you want to generate a command-line version of the same request.
Check the response headers as well as the body. Headers are part of the API behaviour.
A successful response should return a status code like:
HTTP/1.1 200 OK
Then inspect the response.
Ask:
- did the API return
200? - did the response include
Content-Type: application/json? - is the response body valid JSON?
- does the body contain an
itemscollection? - do the items have the fields described in the documentation?
- are the field types what we expected?
This first request gives us a baseline. We now know the API is reachable, it can return data, and we have some example items to compare with the documentation.
Generate Test Data
To create a new item, we need a unique isbn13.
The Simple API has an endpoint for this:
GET https://apichallenges.eviltester.com/simpleapi/randomisbn
Try it in the built-in HTTP client:
GET /simpleapi/randomisbn to generate an ISBN
Copy the returned ISBN and use it in the next request.
Good test data is deliberate. We are not just filling in fields so the request works. We are choosing values that help us learn something.
For a first valid create request, keep the data simple:
type: choose one valid type such asbookisbn13: use a unique valueprice: use a small positive numbernumberinstock: use a small whole number
Create an Item with POST
Use POST on the collection endpoint to create a new item.
POST https://apichallenges.eviltester.com/simpleapi/items
Content-Type: application/json
Accept: application/json
{
"type": "book",
"isbn13": "1234567890123",
"price": 2.00,
"numberinstock": 3
}
Try it in the built-in HTTP client:
POST /simpleapi/items to create a Simple API item
Use the fresh ISBN value returned by /simpleapi/randomisbn when you run the request. The values above are examples of the shape of the request. If the ISBN already exists, the API should reject it.
The built-in client uses a fresh generated ISBN for the POST body. After a successful POST, it remembers the created item id so the following example clients can use it.
For a successful create request, check:
- status code is
201 Created - response body is JSON
- response body contains the new item
- the server generated an
id - the response includes a
Locationheader - the
Locationheader points to the new item - the
content-typeheader isapplication/json - there is a
content-lengthheader that seems accurate
The Location header is useful because it tells us where the new resource can be found.
For example:
Location: /simpleapi/items/123
Do not stop at the POST response. The response says the item was created, but good testing verifies the system state afterwards.
Verify the Created Item with GET
Use the id from the POST response, or the path from the Location header, to retrieve the item.
GET https://apichallenges.eviltester.com/simpleapi/items/{id}
Accept: application/json
Try it in the built-in HTTP client after you have created an item:
GET /simpleapi/items/{id} to retrieve the created item
The built-in client uses the last item id created from this page. If you are using another client, replace {id} with the real id from the POST response.
Check:
- status code is
200 OK idmatches the created itemtypematches the value we sentisbn13matches the value we sentpricematches the value we sentnumberinstockmatches the value we sent- also double check the headers are what you expect
This is a small but important testing pattern:
- perform an action
- observe the response (the full response, body and headers)
- make another request to check state
For create, update, and delete operations, the follow-up check often finds issues that the first response does not reveal.
Vary One Thing at a Time
After the happy path works, start varying the request.
Change one thing at a time so that the result is easier to understand.
Useful variations include:
- remove a required field
- send the wrong data type
- use a duplicate
isbn13 - send an invalid
type - send a negative
price - send a very high
price - send
numberinstockas a string instead of a number - omit the
Content-Typeheader - ask for XML with the
Acceptheader - use an HTTP verb that is not supported on the endpoint
For example, numberinstock should be an integer. This request sends it as a string:
POST https://apichallenges.eviltester.com/simpleapi/items
Content-Type: application/json
Accept: application/json
{
"type": "book",
"isbn13": "1234567890124",
"price": 2.00,
"numberinstock": "3"
}
The API should reject this because "3" is text, not a JSON number.
Try the invalid request in the built-in HTTP client:
POST /simpleapi/items with numberinstock as a string
When the API rejects input, inspect the response just as carefully as you inspect a successful response.
Ask:
- is the status code appropriate?
- does the response body explain the problem?
- is the error message useful?
- is the error response format consistent?
- did the API avoid creating or changing data?
Variation testing with invalid data (often called 'Negative testing') is not only trying to triggering an error condition. We want to make sure the API can handle it and responds with the correct status codes and messages describing the error. We don't really want to see the API fail - if it does then we raise a defect.
Test Duplicate Data
The Simple API requires isbn13 to be unique.
That gives us a useful data-state test:
- create an item with a new
isbn13 - create another item with the same
isbn13 - check that the second request is rejected
- check that only one item exists with that ISBN
The second POST should not silently create duplicate data.
Try the duplicate ISBN check in the built-in HTTP client after you have created an item:
POST /simpleapi/items with the previous ISBN to trigger duplicate validation
This type of test is common in real APIs.
Examples:
- email address must be unique
- username must be unique
- product SKU must be unique
- booking reference must be unique
- account number must be unique
Whenever the API has a uniqueness rule, test both the allowed case and the rejected case.
Test Update Behaviour
After creating an item, try changing it.
The Simple API supports update operations on an individual item endpoint:
/simpleapi/items/{id}
Use PUT when you want to send a full replacement representation:
PUT https://apichallenges.eviltester.com/simpleapi/items/{id}
Content-Type: application/json
Accept: application/json
{
"type": "dvd",
"isbn13": "1234567890123",
"price": 4.56,
"numberinstock": 8
}
Use the same isbn13 value as the item you created earlier unless your test is deliberately checking ISBN validation.
Try the PUT in the built-in HTTP client:
PUT /simpleapi/items/{id} to replace the created item
Use PATCH when you want to change only part of the item:
PATCH https://apichallenges.eviltester.com/simpleapi/items/{id}
Content-Type: application/json
Accept: application/json
{
"price": 9.99
}
Try the PATCH in the built-in HTTP client:
PATCH /simpleapi/items/{id} to change the price
After each update, send a follow-up GET.
GET /simpleapi/items/{id} after update to verify the current item
For PUT, check that the full item now matches the replacement data.
For PATCH, check that only the intended field changed. If you changed price, then type, isbn13, and numberinstock should still be what they were before.
This is where API testing becomes more than response checking. We are testing the behaviour of the system behind the API.
Test Delete Behaviour
Use DELETE on an individual item endpoint:
DELETE https://apichallenges.eviltester.com/simpleapi/items/{id}
Try the DELETE in the built-in HTTP client:
DELETE /simpleapi/items/{id} to remove the created item
A successful delete may return:
HTTP/1.1 204 No Content
204 means the request succeeded and there is no response body.
After the delete, send a GET request for the same item:
GET https://apichallenges.eviltester.com/simpleapi/items/{id}
Accept: application/json
Try the follow-up GET in the built-in HTTP client:
GET /simpleapi/items/{id} after delete to confirm it is gone
The API should no longer return the deleted item.
Ask:
- does
DELETEreturn a suitable status code? - does the response body match that status code?
- can the deleted item still be retrieved?
- can the same item be deleted twice?
- what happens when deleting an id that never existed?
Deletion tests are often good at revealing state problems, caching issues, and inconsistent error handling.
Test Headers and Formats
Headers are part of the request data.
Two important headers for API testing are:
AcceptContent-Type
Accept tells the server what response format the client wants.
Content-Type tells the server what format the request body uses.
For example:
Accept: application/json
Content-Type: application/json
The Simple API supports both JSON and XML, so it is useful for practising format variation.
Try questions like:
- what happens when we ask for JSON?
- what happens when we ask for XML?
- what happens when we send JSON but ask for XML back?
- what happens when
Content-Typeis missing? - what happens when
Content-Typesays JSON but the body is not valid JSON?
Use this full client when you want to experiment with different verbs, paths, headers, query strings, and request bodies:
Experiment with Simple API headers, formats, methods, and paths
This connects directly to HTTP Basics because the API behaviour is affected by the full HTTP message, not just the URL and body.
Test Unsupported Methods
An endpoint should only support the HTTP methods that make sense for that resource.
For example, POST belongs on the collection endpoint when creating an item:
POST /simpleapi/items
But DELETE belongs on an individual item endpoint:
DELETE /simpleapi/items/{id}
It is useful to try unsupported methods and check that the API handles them cleanly.
Try an unsupported collection DELETE in the built-in HTTP client:
DELETE /simpleapi/items to check unsupported collection delete
Ask:
- does the API return
405 Method Not Allowed? - does it return
501 Not Implementedfor methods it does not understand? - does it accidentally perform an action?
- does it return a server error?
- does the documentation match the actual supported methods?
Read HTTP Methods and Verbs for more detail on what the common HTTP methods are expected to mean.
Keep Useful Evidence
API testing creates evidence quickly.
At minimum, keep enough information to reproduce important observations:
- request method
- URL
- headers
- request body
- response status code
- response headers
- response body
- data state before the request
- data state after the request
An API client can save requests. A proxy can capture the actual HTTP traffic. Automation can preserve repeatable checks.
If you are using the insitu HTTP client on the page then you can use the Developer Tools in the browser to show the network tab. And you can even export the network requests as a HAR file (HTTP Archive) for later review or using in other tools.
The important point is that your evidence should show what was actually sent and what actually came back.
If a REST client hides details or changes the request, use a proxy to inspect the real HTTP traffic:
The Mirror Mode can also help when learning a tool because it shows you the request that reached the server.
Decide What to Automate
I generally recommend to perform exploratory testing first.
Use the API manually, read the responses, vary the data, and learn how the system behaves. Then automate for requests and assertions that are stable and useful.
Good first automation candidates for the Simple API include:
GET /simpleapi/itemsreturns a valid item collectionPOST /simpleapi/itemscreates an item with a unique ISBNGET /simpleapi/items/{id}retrieves the created item- duplicate
isbn13values are rejected - invalid field types are rejected
PATCH /simpleapi/items/{id}changes only the intended fieldDELETE /simpleapi/items/{id}removes the itemGET /simpleapi/items/{id}returns a missing-resource response after delete
I have created an example set of Java @Test methods using RestAssured which automate basic Simple API CRUD coverage.
Basic Simple API CRUD Coverage
A Simple REST API Testing Checklist
Use this as a starter checklist when practising with a new endpoint:
- read the documentation
- identify the resource
- identify supported methods
- send a valid read request
- inspect status code, headers, and body
- create valid data
- verify created state with a follow-up request
- test required fields
- test field types
- test malformed payloads
- test boundary values
- test duplicate values
- test unsupported methods
- test unsupported formats
- test update behaviour
- test delete behaviour
- record enough evidence to reproduce findings
- automate stable, important checks
This checklist is deliberately small. It gives you a starting point, but real testing still depends on the risks and rules of the API in front of you.
Where to Go Next
Use this tutorial as the practical path, then deepen specific areas with the reference pages:
- Web Application and HTTP Basics
- HTTP Basics
- HTTP Methods and Verbs
- REST API Basics
- API Testing Concepts and Coverage
- OpenAPI for API Testing
- Swagger UI and Tools for API Testing
- API Testing Tutorial Summary
For more hands-on practice with the same API, use:
Summary
REST API testing is a practical loop: read the documentation, send a request, inspect the response, check the state, vary the input, and record useful evidence.
The Simple API gives you a safe first place to practise because it does not require authentication and it supports common REST-style operations.
Start with GET, then create data with POST, verify the created resource, update it with PUT or PATCH, and delete it with DELETE. Along the way, test validation, headers, formats, unsupported methods, and duplicate data rules.
Practice. Keep practicing. Go deep in understanding the HTTP messages. Vary everything. Trust nothing.