REST API testing starts with status code semantics
REST assigns meaning to status codes and clients depend on that meaning. Worth asserting explicitly rather than assuming.
201 with a location for creation, not 200 with a body and nothing else.
404 for missing, 403 for forbidden. Returning 404 for forbidden is a deliberate choice some teams make; returning 403 for missing is just a bug.
422 or 400 for invalid input, consistently. Pick one and check that every endpoint agrees.
Never 200 with an error inside the body. Common, and it makes every client write special handling.
Idempotency
PUT and DELETE are supposed to be idempotent. Calling them twice should leave the same state and not produce an error the second time. This is easy to get wrong and easy to check: call it twice and assert.
The same goes for a second DELETE returning 404 rather than 500. Clients retry, and a retry-unsafe endpoint turns a network blip into a user-visible failure.
The conventions your service claims
| Pagination | Does a full traversal return the count the collection claims. Off-by-one at page boundaries is extremely common. |
| Filtering and sorting | Does an unknown filter get rejected or silently ignored. Silent ignoring returns confidently wrong data. |
| Partial update | Does PATCH change only what was sent, or quietly null the rest. |
The check underneath all of them
Assert on the body, not just the status. A 200 with an empty list where a record should be passes every status assertion ever written. This single habit catches more real defects in REST API testing than any convention check.
The four problems
Four things decide whether an API suite survives its first year: sessions that expire, values that only exist at runtime, calls that depend on each other, and records nobody cleans up. TestSprite handles these as Auto-Authentication, Dynamic Variables, Dependency Chains and Auto-Cleanup, described in the API testing documentation.
The conventions worth writing down first
Before testing whether your API follows its conventions, somebody has to say what they are, and most teams discover during this exercise that they disagree.
Four questions settle most of it. What status does a create return, and does it include a location. Is a missing resource a 404 or a 403 when the caller would not be allowed to see it either way. Does an unknown query parameter get rejected or ignored. Does PATCH treat a missing field as unchanged or as null.
None of these have a universally right answer and all of them are choices your clients depend on. Writing the four answers down takes twenty minutes and turns a vague intention to be RESTful into something you can actually assert on, which is the prerequisite for any of it being tested.
Getting coverage without writing all of it
Generated plans cover functional, schema, authorization, error-handling and boundary categories from a specification or a discovery pass, which includes most of the convention checks above by default.
Terminal
npm install -g @testsprite/testsprite-cli
testsprite setup
If you would rather not install anything, the dashboard does the same thing. Everything else the command line can do is in the CLI repository.
If the pipeline belongs to another team, the GitHub App is the path of least resistance: it is a webhook, it changes nothing in your repository, and it fires when your build reports the new version live. If you want the check visible in the repo instead, a GitHub Actions step does that.
How TestSprite checks the conventions
Generated plans cover functional, schema, authorization, error-handling and boundary categories against the running service, which includes most of the convention checks above: status semantics per operation, rejection of bad input, behavior on a missing resource, and pagination that adds up.
Auto-Authentication keeps sessions alive across a run, Dynamic Variables carry values between calls, Dependency Chains derive execution order from what each case needs and produces, and Auto-Cleanup removes exactly what the run created. The last one matters more for REST than people expect, because idempotency checks mean calling the same destructive operation twice on purpose.
What you get is your own conventions asserted on every change rather than documented and hoped for, which is what stops one endpoint quietly returning 200 where every other one returns 201.
Does REST purity matter for testing?
Only where clients depend on it. Test the conventions your service claims to follow rather than the ones a textbook prescribes.
How do we test HATEOAS?
If you return links, assert they resolve and point where they claim. If you do not, skip it; very few services genuinely implement it.
What about GraphQL?
Different conventions entirely. Status codes carry much less meaning and errors arrive in the body by design, so the checks above mostly do not transfer.
Should we check response headers?
Content type always. Caching and rate limit headers if clients act on them, which is worth knowing before you assert on them.
Is versioning worth testing?
Yes, if you support more than one version. Assert that an old version still behaves as it did, because that is the promise a version number makes.
Test the promises your API makes.
REST API testing should verify the conventions your service claims: status semantics, idempotency, pagination totals, and whether unknown filters are rejected. Underneath all of them, assert on the body rather than the status.