How to Write API Test Cases: Template, Examples and 50+ Scenarios
“Write test cases for this API” is both a daily task and a favourite interview exercise. Most beginners write five happy-path cases and stop. Experienced testers use a repeatable method that produces 30 to 50 meaningful cases for a single endpoint in under an hour. In this lesson you’ll get a ready-to-use API test case template, the five test design techniques that generate cases systematically, and a complete worked example on a real endpoint you can execute right away.
Where API Test Cases Come From
Before writing a single case, collect the inputs:
- API specification (OpenAPI/Swagger, WSDL, Postman docs): endpoints, fields, types, required flags, status codes.
- Business requirements / user stories: rules like “age must be 18 to 100”, “only admins can delete”.
- Acceptance criteria and conversations with developers and product owners.
- Existing bugs and production incidents: past failures tell you where to look.
POST /addresses with pincode (6 digits). From one sentence and one field you already get boundary cases (5th and 6th address), format cases (5- and 7-digit pincodes, letters) and auth cases (another customer’s address).The API Test Case Template
| Field | Description | Example |
|---|---|---|
| Test case ID | Unique id | TC_OBJ_POST_012 |
| Title | What is verified, in one line | Create object fails when ttl is below minimum |
| Type | Positive / Negative / Boundary / Auth / Security / Performance | Boundary |
| Priority | P1 (critical) to P3 | P2 |
| Preconditions | State and data needed | API reachable; no auth required |
| Method + Endpoint | Verb and path | POST /objects |
| Headers | Required headers | Content-Type: application/json |
| Request body / params | Exact test data | {"name":"Bag","ttl":9} |
| Expected status | Exact code | 422 |
| Expected response | Body, headers, error format | error.code = "validation_failed", details mention ttl |
| Post-conditions | What must (not) change | No object created |
| Actual result / Status | Filled during execution | Pass / Fail + bug id |
Keep this in a spreadsheet, a test management tool (Jira Xray, Zephyr, TestRail) or directly as a Postman collection where each request is a case and its scripts are the expected results.
5 Techniques That Generate API Test Cases
1. Equivalence Partitioning (EP)
Group inputs that should behave the same and test one value per group. For role with allowed values admin/editor/viewer: valid partition {admin}, invalid partitions {unknown value “manager”}, {wrong type 5}, {empty “”}, {null}.
2. Boundary Value Analysis (BVA)
Bugs cluster at the edges. For a range min..max test min-1, min, max, max+1 (and optionally min+1, max-1).
| Field (practice API rule) | Boundary values | Expected |
|---|---|---|
ttl (10 to 1800 s) | 9, 10, 1800, 1801 | 422, 201, 201, 422 |
age in /validate/user (18 to 100) | 17, 18, 100, 101 | 422, 200, 200, 422 |
name in /validate/user (2 to 50 chars) | 1, 2, 50, 51 chars | 422, 200, 200, 422 |
per_page (1 to 50) | 0, 1, 50, 51 | 400, 200, 200, 400 |
3. Decision Tables
For rules that combine conditions. Example: delete an object on /token/objects.
| Token present | Token valid | Role | Object exists | Expected |
|---|---|---|---|---|
| No | – | – | – | 401 |
| Yes | No | – | – | 401 |
| Yes | Yes | viewer | Yes | 403 |
| Yes | Yes | admin | Yes | 204 |
| Yes | Yes | admin | No | 404 |
4. State Transition
Resources have lifecycles. An object: does not exist → created (v1) → updated (v2, v3…) → deleted or expired. Test valid transitions and invalid ones: update after delete (404), delete twice (404), read after TTL expiry (404), update with a stale version (412).
5. Error Guessing (Experience Checklist)
Empty body, malformed JSON, wrong Content-Type, very long strings, Unicode and emojis, leading/trailing spaces, duplicate requests, special characters, SQL/HTML strings, numbers as strings, nulls, extra fields, huge numbers, negative numbers, decimals where integers expected.
Worked Example: Test Cases for POST /objects
Specification (from the practice API docs): name required, 1 to 100 characters; data any JSON up to 5 KB; ttl 10 to 1800 seconds, default 600; other fields → 422; success → 201 with Location and ETag; body over 10 KB → 413.
https://sqatools.in/wp-json/sqa-api/v1/objectscurl -i \
-X POST \
-H "Content-Type: application/json" \
-d '{"name":"Test case demo","data":{"qty":2},"ttl":60}' \
"https://sqatools.in/wp-json/sqa-api/v1/objects"| ID | Title | Input | Expected | Type |
|---|---|---|---|---|
| 01 | Create with all fields | name, data, ttl=60 | 201, Location, ETag “v1”, version 1 | Positive |
| 02 | Create with name only | {"name":"A"} | 201, ttl_seconds 600 (default) | Positive |
| 03 | Created object is readable | GET Location | 200, same data | Positive |
| 04 | Appears in list | GET /objects | Contains new id | Positive |
| 05 | Missing name | {"data":{}} | 422, details.field = name | Negative |
| 06 | Empty name | {"name":""} | 422 | Boundary |
| 07 | Name 100 chars | 100 x “a” | 201 | Boundary |
| 08 | Name 101 chars | 101 x “a” | 422 | Boundary |
| 09 | Name wrong type | {"name":123} | 422 | Negative |
| 10 | ttl 9 | ttl=9 | 422 | Boundary |
| 11 | ttl 10 | ttl=10 | 201 | Boundary |
| 12 | ttl 1800 | ttl=1800 | 201 | Boundary |
| 13 | ttl 1801 | ttl=1801 | 422 | Boundary |
| 14 | ttl as string | ttl=”60″ | 422 | Negative |
| 15 | Client sends id | {"id":"x","name":"A"} | 422 (server-generated ids) | Negative |
| 16 | Unknown field | {"name":"A","colour":"red"} | 422 | Negative |
| 17 | Malformed JSON | {"name": | 400 | Negative |
| 18 | Empty body | (none) | 400 | Negative |
| 19 | JSON array body | [{"name":"A"}] | 400 | Negative |
| 20 | Wrong Content-Type | text/plain | 415 | Header |
| 21 | Body over 10 KB | 11 KB data | 413 | Boundary |
| 22 | Unicode name | “आशा 😀” | 201, stored exactly | Positive |
| 23 | Injection text | ' OR 1=1 <script> | 201, stored as plain text | Security |
| 24 | Wrong method | PATCH /objects | 405 + Allow | Negative |
| 25 | Expiry | ttl=10, GET after 12 s | 404 | State |
| 26 | Two creates, same body | POST twice | Two different ids (not idempotent) | Positive |
| 27 | Response time | Any valid create | < 2 s | Performance |
| 28 | Response schema | Any valid create | id, name, data, version, created_at, expires_at, ttl_seconds | Contract |
28 cases from one endpoint, and every one of them is concrete and runnable. The practice page has 141 more, grouped exactly like this: API Testing Practice scenarios.
The Generic Checklist for Any Endpoint
| Category | Questions to turn into cases |
|---|---|
| Functional | Does each documented behaviour work? Are values, calculations and side effects correct? |
| Validation | Required, type, format, length, range, enum, pattern, cross-field rules? |
| Status codes | Correct and specific for each outcome? |
| Headers | Content-Type, Location, caching, CORS, rate limit headers? |
| Auth | No/invalid/expired token, each role, other users’ data? |
| State | Valid and invalid transitions, concurrency, idempotency? |
| Data | Persistence (GET after write), no duplicates, Unicode, large data? |
| Contract | Matches schema and documentation, no extra fields? |
| Non-functional | Response time, rate limits, payload limits, error message quality? |
Prioritising: You Can’t Run Everything Every Time
- P1 (smoke/critical): main happy paths, auth works, money calculations. Run on every deployment.
- P2 (regression): validation, roles, state transitions. Run on every build/nightly.
- P3 (extended): rare edge cases, large data, Unicode. Run before releases.
Risk-based rule: anything touching money, personal data, permissions or irreversible actions gets higher priority.
Writing Good Test Case Titles
| Weak | Strong |
|---|---|
| Test create API | POST /objects returns 201 with Location header for valid input |
| Negative test 3 | POST /objects rejects ttl=1801 with 422 and ttl error detail |
| Check delete | Viewer token gets 403 on DELETE /token/objects/{id}; object remains |
Format: [Method] [endpoint] [expected outcome] when/for [condition]. Strong titles become readable automated test names too.
From Test Cases to Automation
A well-written table converts directly into data-driven Playwright tests: each row becomes an object, and one loop runs them all.
import { test, expect } from '@playwright/test';
const BASE = 'https://sqatools.in/wp-json/sqa-api/v1';
const cases = [
{ id: 'TC_10', title: 'ttl 9 is rejected', body: { name: 'A', ttl: 9 }, status: 422 },
{ id: 'TC_11', title: 'ttl 10 is accepted', body: { name: 'A', ttl: 10 }, status: 201 },
{ id: 'TC_12', title: 'ttl 1800 is accepted', body: { name: 'A', ttl: 1800 }, status: 201 },
{ id: 'TC_13', title: 'ttl 1801 is rejected', body: { name: 'A', ttl: 1801 }, status: 422 },
{ id: 'TC_05', title: 'name is required', body: { data: {} }, status: 422 },
{ id: 'TC_15', title: 'client id is refused', body: { id: 'x', name: 'A' }, status: 422 },
];
for (const c of cases) {
test(`${c.id} POST /objects: ${c.title}`, async ({ request }) => {
const res = await request.post(`${BASE}/objects`, { data: c.body });
expect(res.status()).toBe(c.status);
});
}
Common Mistakes in API Test Design
- Only positive cases. Aim for more negative than positive.
- “Expected: error” instead of the exact status and message.
- Not verifying side effects (was it saved? was nothing saved on failure?).
- Test cases that depend on each other’s data and order.
- Ignoring headers and response time.
- Copying the developer’s unit tests instead of thinking like a user and an attacker.
Interview Questions: API Test Cases
How do you write test cases for an API?
Read the specification and requirements, list each endpoint and its rules, then apply equivalence partitioning, boundary values, decision tables, state transitions and error guessing. Cover positive, negative, auth, header, contract, performance and security cases, each with exact input, expected status, body, headers and side effects.
What fields should an API test case contain?
ID, title, type, priority, preconditions, method and endpoint, headers, request data, expected status, expected body and headers, post-conditions, and actual result with status.
Write test cases for a login API.
Valid login (200 + token), wrong password and unknown user (401 with the same message), missing fields (400/422), empty strings, SQL/HTML in fields, case sensitivity of username, locked account, rate limiting after repeated failures, token expiry and validity on protected endpoints, password not returned or logged, HTTPS only.
How do you apply boundary value analysis to APIs?
For every numeric range, length limit, page size or list limit, test min-1, min, max and max+1. For example, for age 18 to 100 test 17, 18, 100 and 101 and expect 422, 200, 200 and 422.
What is the difference between a test scenario and a test case?
A scenario is a high-level situation to verify, such as “create object with invalid ttl”. A test case is a concrete, executable instance with exact data and expected results, such as “POST /objects with ttl 1801 returns 422 with a ttl error”.
How many test cases would you write for one endpoint?
It depends on the rules, but a typical create endpoint has 20 to 50 meaningful cases once validation, boundaries, auth, headers and state are covered. Prioritise them so critical ones run on every build.
How do you prioritise API test cases?
By risk: business-critical flows, money, personal data, permissions and irreversible actions first (P1), then validation and regression cases (P2), then rare edge cases (P3).
How do you test that a failed request has no side effects?
After a rejected create or update, query the resource or list endpoint (or the database) to confirm nothing was created or changed, and that counters, balances or stock were not affected.
What is state transition testing for APIs?
Testing a resource’s lifecycle: valid transitions (created to updated to deleted) and invalid ones (update after delete, cancel after delivery, stale version updates), checking the API enforces the allowed state changes.
Where do you store API test cases?
In a test management tool such as Jira with Xray or Zephyr, TestRail, a spreadsheet for small projects, or as executable Postman collections and automated test code linked to requirements.