API Testing with Postman
What an API is, how REST requests and responses work, and how to test an API with Postman, from your first GET request to environments, collections and automated checks.
A food delivery app had a neat little quantity stepper: the – button stopped at 1, so you could never order fewer than one pizza. The UI was tested carefully and worked perfectly.
Then someone sent the request to the server directly, with "quantity": -3. The server accepted it. The order total went negative, and the “refund” landed on a real card.
The bug was never in the screen. It was in the APIAPIThe way different software parts talk to each other, like an app asking a server for data.Open in glossary → behind it, and the UI was simply hiding it. This lesson teaches you to test that layer directly, with Postman, the most popular tool for the job.
Why it matters
Almost every modern app is two parts: a front end (the website or the mobile app you see) and a back end (servers and databases). They talk to each other through APIs. The same API often serves the website, the iOS app, the Android app and partner companies.
That means:
- One API bug breaks every platform at once.
- The UI can hide bugs. A form may block bad input, but the API might still accept it from anyone who sends a request directly.
- API tests are fast. You can check 20 cases in the time it takes to click through one checkout.
- Bugs are easier to pin down. If the API returns the wrong data, it’s a back-end bug. If the API is right but the screen is wrong, it’s a front-end bug. That makes your bug reportsBug reportA clear written description of a bug so developers can reproduce and fix it.Open in glossary → much sharper.
What is an API?
An API (Application Programming Interface) is a set of rules that lets one program ask another program for data or actions.
The classic analogy is a restaurant:
You (the client)
The app or website. It knows what it wants, but can’t go into the kitchen.
The waiter (the API)
Takes your order in an agreed format (the menu), carries it to the kitchen and brings back the result.
The kitchen (the server)
Does the real work: checks stock, saves orders, reads the database.
The menu is the API documentation: it lists what you can order and how. As an API tester you skip the dining room (the UI) and talk to the waiter directly: you order normal dishes, strange dishes, dishes that aren’t on the menu, and you check that the waiter always responds correctly.
REST basics
Most web and mobile APIs today are REST APIs. They use plain HTTP, the same protocol your browser uses, and usually send data as JSONJSONA simple text format for data, used by most APIs, like {"name": "Anna", "age": 31}.Open in glossary →. Every call is a request from the client and a response from the server.
1 The request (you → server)
Request line
what and where
Headers
extra info, e.g. who you are
Content-Type: application/json
Body
the data you send (JSON)
2 The response (server → you)
Status
did it work?
Headers
info about the response
Location: /v1/cart/items/1
Body
the data you get back
Endpoints and URLs
An endpointEndpointA specific API address the app calls, like GET /api/products or POST /api/login.Open in glossary → is one address of the API that does one job. A URL has several parts:
| Part | Example | Meaning |
|---|---|---|
| Base URL | https://api.shop.test/v1 | Where the API lives (often different per environment) |
| Path | /products | Which resource you want |
| Path parameter | /products/42 | Which one item (here product 42) |
| Query parametersQuery parameterData in a URL after the ?, like ?q=mug&sort=price, often used to keep search and filter state.Open in glossary → | ?category=shoes&limit=10 | Filters and options, after a ?, joined with & |
Methods
The HTTP methodHTTP methodThe type of request: GET reads data, POST creates, PUT/PATCH updates, DELETE removes.Open in glossary → says what you want to do with the resource:
| Method | Does | Example | Has a body? | Idempotent? |
|---|---|---|---|---|
| GET | Read | GET /products/42 | No | Yes |
| POST | Create / trigger an action | POST /cart/items | Yes | No |
| PUT | Replace / set completely | PUT /cart/items/42 with {"quantity": 3} | Yes | Yes |
| PATCH | Change some fields | PATCH /profile with {"name": "Ana"} | Yes | Not necessarily |
| DELETE | Remove | DELETE /cart/items/42 | Usually no | Yes |
Status codes
Every response starts with an HTTP status codeHTTP status codeA three-digit number in every server response: 200 means OK, 404 not found, 500 server error.Open in glossary →. The first digit tells you the family:
| Code | Name | When you’ll see it |
|---|---|---|
| 2xx | Success | |
| 200 | OK | A read or update worked |
| 201 | Created | Something new was created (POST) |
| 204 | No Content | Worked, nothing to send back (often DELETE) |
| 3xx | Redirection | |
| 301 / 302 | Moved Permanently / Found | Go to another URL |
| 304 | Not Modified | Use your cached copy |
| 4xx | Client error (the request was wrong) | |
| 400 | Bad Request | Invalid data, missing field, broken JSON |
| 401 | Unauthorized | Who are you? No token, or an invalid / expired one |
| 403 | Forbidden | I know who you are, but you’re not allowed |
| 404 | Not Found | The resource doesn’t exist |
| 405 | Method Not Allowed | E.g. DELETE on an endpoint that only supports GET |
| 409 | Conflict | Clashes with current state, e.g. creating a duplicate |
| 422 | Unprocessable Content | The JSON is valid but the values break a rule (some APIs use 400 for this) |
| 429 | Too Many Requests | Rate limit hit |
| 5xx | Server error (the server failed) | |
| 500 | Internal Server Error | Unhandled crash on the server: almost always a bug |
| 502 / 503 / 504 | Bad Gateway / Service Unavailable / Gateway Timeout | Server down, overloaded or too slow |
Headers
Headers are key–value pairs with extra information about the request or response:
Content-Type: application/json— “the body I’m sending is JSON”.Authorization: Bearer eyJhbGci...— “here’s my token, this is who I am”.Accept: application/json— “please answer in JSON”.- Response headers like
Cache-Control,Location(where a new resource lives) orX-Request-Id(great to paste into bug reports so developers can find the server log).
JSON bodies
POST, PUT and PATCH usually send a body in JSON: keys in double quotes, values as strings, numbers, true/false, null, arrays [ ] or objects { }.
{
"productId": 1,
"quantity": 2,
"giftWrap": false,
"note": "Leave at the door"
}
Notice that 1 (a number) and "1" (a string) are different types. Many API bugs are type bugs.
Authentication: tokens and API keys
Most APIs need to know who is calling:
- Bearer token. You log in once (e.g.
POST /login), the server returns a token, and you send it on every request in the headerAuthorization: Bearer <token>. Tokens expire. A common format is a JWT (a long string starting witheyJ). - API key. A fixed secret string for an app or partner, sent in a header like
x-api-key: ...or sometimes as a query parameter.
The two error codes to remember: 401 = missing or invalid credentials; 403 = valid credentials, but not allowed (for example a customer trying an admin endpoint).
Why test the API directly?
| Reason | Example |
|---|---|
| Faster feedback | Check 15 invalid inputs in two minutes, no clicking through screens |
| Finds bugs the UI hides | The UI greys out ”–” at 1, but the API accepts -5 |
| Validation bypass | Front-end checks can be skipped by anyone. The server must validate everything again. |
| Earlier testing | The API is often ready days before the screens are |
| Clear blame | Wrong data in the API response → back-end bug. Correct response but wrong screen → front-end bug. |
| Security checks | Call another user’s resources with your token to test access rules (see Security Basics) |
Try it: a mini API client
Here’s a simplified Postman-like client connected to a simulated Shop API that runs in your browser. Nothing is sent over the internet. Try these:
- Send the first request. “List products” is preloaded. Click Send and look at the status, time and JSON body.
- Change a query parameter. In Params, change
limitto2, then to0. Which status do you get for0, and does the message help? - Use the environment. Click the eye icon to see
{{baseUrl}}and{{token}}. Switch the environment to No environment and send again. Then switch back. - Check 401 vs 403. Load View cart, untick the
Authorizationheader and send (401). Then load Delete product (admin) and change{{adminToken}}to{{token}}(403). - Break the body. Load Add to cart, change
"quantity": 2to"quantity": "two", then remove the closing}. - Run tests. Open Tests, tick a few checks, send, and open Test Results. Then click curl to see the same request as a terminal command.
GET https://api.shop.test/v1/products?category=accessories&limit=10
Query parameters are added after a ? in the URL. Edit here or in the URL bar.
Click Send to get a response.
History
Requests you send appear here.
Postman essentials
Postman is an app (desktop and web, with a free plan) for building, sending, saving and automating API requests. Here’s what you’ll use every day.
- Build a request. Click New › HTTP (or the + tab). Choose the method from the dropdown, paste the URL and click Send. The response appears below with status, time, size, body (Pretty / Raw / Preview) and headers.
- Params. Query parameters typed in the URL appear in the Params tab, and vice versa. Path variables written as
:idin the URL (e.g./products/:id) also get a value field there. - Headers. Add headers in the Headers tab. For tokens, the Authorization tab is easier: choose Bearer Token and paste
{{token}}. Postman then adds the header for you. - Body. In Body, choose raw and JSON. Postman highlights invalid JSON and sets
Content-Type: application/jsonautomatically. - Environments and variables. Create an environment (e.g. Staging) with variables like
baseUrlandtoken, select it in the top-right dropdown, and write{{baseUrl}}/products. Switching to a Dev environment then retargets every request at once. Unresolved variables show in red. - Collections. Save requests into a collection (a folder for one API, e.g. Shop API) with sub-folders like Products and Cart. Collections can be shared with your team and run all at once with the Collection Runner.
- Save examples. After a response, click Save Response › Save as example. Examples document what a correct (or error) response looks like and can power mock servers.
- Scripts and tests. In the Scripts tab (called Tests in older versions), the Post-response script runs after every response. Add checks with
pm.testandpm.expect. The Snippets list on the side writes common ones for you.
A few tests worth knowing, in Postman’s JavaScript syntax:
// Status code
pm.test("Status code is 200", () => {
pm.response.to.have.status(200);
});
// Response time
pm.test("Response time is below 500 ms", () => {
pm.expect(pm.response.responseTime).to.be.below(500);
});
// Body content and types
pm.test("Product has the right shape", () => {
const json = pm.response.json();
pm.expect(json.id).to.eql(1);
pm.expect(json.name).to.be.a("string");
pm.expect(json.price).to.be.a("number");
});
// Header
pm.test("Content-Type is JSON", () => {
pm.expect(pm.response.headers.get("Content-Type")).to.include("application/json");
});
// Save a value for the next request (e.g. after POST /login)
pm.environment.set("token", pm.response.json().token);
What to test
Positive (happy path): each endpoint with valid data returns the documented status (200, 201, 204) and body · created items can be read back with GET · updates really change the data · lists support their filters, sorting and paginationPaginationSplitting a long list into pages that load one at a time.Open in glossary →.
Negative: input validation. input validationInput validationChecking that what the user typed is acceptable before the app uses it.Open in glossary → on the server is the heart of API testing:
- Missing required fields → 400 with a message naming the field.
- Wrong types:
"quantity": "two","price": "19.99",null, an array instead of an object. - Boundary values (boundary value analysis): if quantity is 1–10, try 0, 1, 10, 11, and also
-1and1.5. - Invalid JSON (a missing bracket) → 400, not 500.
- Extra or unknown fields like
"isAdmin": trueare ignored or rejected, never applied.
Negative: auth and existence.
- No token or an invalid / expired token → 401.
- Valid token but no permission → 403. Another user’s resource → 403 or 404.
- An ID that doesn’t exist → 404, not 200 with an empty body.
- Creating a duplicate (same email, same coupon code) → 409, not 500.
- Wrong method on an endpoint → 405.
Behavior:
- Idempotency. Send the same PUT or DELETE twice; the result must be the same as sending it once.
- Response schema and data. Field names, types and formats match the docs (dates, currencies, IDs). No missing fields, no extra sensitive fields like passwords or other users’ emails.
- Response time. Agree on a target (e.g. under 500 ms in the test environment) and watch for slow endpoints.
- Error messages are clear, consistent and don’t leak stack traces or SQL (see error messageError messageText that tells the user something went wrong and, ideally, how to fix it.Open in glossary →).
Common API bugs
| # | Bug | What you see | Typical severity |
|---|---|---|---|
| 1 | Missing server validation | quantity: -5 or 0 is accepted, totals go negative | High |
| 2 | Wrong status code | 200 with null or {} instead of 404; 200 with {"error": ...} | Medium |
| 3 | 500 instead of 4xx | A duplicate, missing field or bad JSON crashes the server | Medium–High |
| 4 | Missing auth check | An endpoint works without a token, or for the wrong user | Critical |
| 5 | Type / schema mismatch | "price": "19.99" (string) where the docs say number; renamed fields | Medium |
| 6 | Not idempotent | Repeating PUT adds the item again; a retried payment charges twice | High |
| 7 | Over-sharing | The response includes password hashes, internal IDs or other users’ data | High–Critical |
| 8 | Leaky errors | Stack traces, SQL queries or server paths in error messages | Medium |
| 9 | Inconsistent errors | Each endpoint returns errors in a different format | Low |
| 10 | Slow endpoints | A list call takes 4 seconds once there are 1,000 items | Medium |
Find the bug
Here’s the same client connected to a buggy build of the Shop API. The API docs at the bottom say what the API promises. Compare the real responses with the docs, and use the Tests tab and History to help.
🐞 Bug hunt: Shop API test build
This demo has 6 hidden bugs. Explore it like a tester, then check your findings.
GET https://api.shop.test/v1/products?category=accessories&limit=10
Query parameters are added after a ? in the URL. Edit here or in the URL bar.
Click Send to get a response.
History
Requests you send appear here.
- GET/productsPublic
List products. Query: category (string), limit (integer 1–50). Returns { data: Product[], total }. Product = { id: integer, name: string, price: number, category: string, inStock: boolean }.
200 · 400 bad limit
- GET/products/{id}Public
One product.
200 Product · 404 if it does not exist
- DELETE/products/{id}Admin token
Delete a product.
204 · 401 no/invalid token · 403 not an admin · 404
- GET/cartToken
The current user’s cart: { items, total: number }.
200 · 401
- POST/cart/itemsToken
Add a product. Body { productId: integer, quantity: integer 1–10 }. Adds to any quantity already in the cart.
201 · 400 invalid body · 401 · 404 unknown product · 409 out of stock
- PUT/cart/items/{productId}Token
Set the quantity of a cart item. Body { quantity: integer 1–10 }. Idempotent: sending it twice gives the same result.
200 · 400 · 401 · 404
- DELETE/cart/items/{productId}Token
Remove an item from the cart.
204 · 401 · 404 not in cart
- POST/couponsAdmin token
Create a coupon. Body { code: string, percent: integer 1–90 }. Codes are unique.
201 · 400 · 401 · 403 · 409 code already exists
- 1
Hint: The docs say quantity must be 1–10. Try values outside that range in Add to cart.
- 2
Hint: Ask for a product ID that is not in the list.
- 3
Hint: Add an item, then untick the Authorization header and remove it.
- 4
Hint: Send Create coupon (admin) twice with the same code.
- 5
Hint: Compare the type of price with the docs, or run the price test.
- 6
Hint: Send Update cart item several times and look at the quantity.
Reading API docs: Swagger and OpenAPI
Before testing, read the docs. Most teams describe their API in an OpenAPI file (formerly called Swagger), a YAML or JSON file that lists every endpoint, parameter, request body, response and status code. Tools like Swagger UI or Redoc turn it into a readable web page, often at a URL like /docs or /swagger.
As a tester, the docs are your expected results:
- Each documented field, type and status code is something to verify.
- Anything missing from the docs (what happens on a duplicate? what is the max length?) is a question for the team, and often a bug waiting to happen.
- Postman can import an OpenAPI file (Import › file or URL) and generate a whole collection for you.
Alternatives to Postman
| Tool | What it is | Good for |
|---|---|---|
| Insomnia | Desktop API client, similar to Postman | A cleaner, lighter UI; REST, GraphQL and gRPC |
| Bruno | Open-source desktop client that stores collections as plain files | Teams that keep API tests in Git next to the code |
| curl | Command-line tool available on macOS, Linux and Windows | Quick checks, sharing an exact request with a developer |
| Browser DevTools | Network tab › right-click a request › Copy as cURL / fetch | Grabbing the real request the app sent, then replaying it in Postman (Import › Raw text) |
curl -X POST 'https://api.shop.test/v1/cart/items' \
-H 'Authorization: Bearer <your-test-token>' \
-H 'Content-Type: application/json' \
-d '{"productId": 1, "quantity": 2}'
Example test cases
TC-API-001Get an existing productpositivehigh
Steps
- Send GET {{baseUrl}}/products/1
Expected result
200 OK. Body has id 1, name (string), price (number), category (string), inStock (boolean). Response time under 500 ms.
TC-API-002Get a product that does not existnegativemedium
Steps
- Send GET {{baseUrl}}/products/999
Expected result
404 Not Found with an error message. No 200, no null body.
TC-API-003Add to cart rejects quantities outside 1–10negativehigh
Preconditions
- Valid customer token in {{token}}
Steps
- Send POST {{baseUrl}}/cart/items with each quantity from the test data
- Check status and body of each response
Test data
quantity: 0, -1, 11, 1.5, "two", missing
Expected result
Each request returns 400 with a message naming the quantity rule. The cart does not change.
TC-API-004Cart endpoints require a valid tokennegativehigh
Steps
- Send GET /cart without the Authorization header
- Send it with "Bearer invalid-token"
- Repeat for POST /cart/items, PUT and DELETE /cart/items/1
Expected result
Every request returns 401 and makes no change.
TC-API-005Customer token cannot use admin endpointsnegativehigh
Steps
- Send DELETE {{baseUrl}}/products/5 with the customer token
Expected result
403 Forbidden. The product still exists (GET /products/5 returns 200).
TC-API-006Duplicate coupon code returns 409negativemedium
Preconditions
- Admin token in {{adminToken}}
Steps
- Send POST /coupons with code SPRING10
- Send the same request again
Expected result
First: 201 Created. Second: 409 Conflict with a clear message. Never 500.
TC-API-007PUT on a cart item is idempotentedgehigh
Steps
- Send PUT /cart/items/1 with {"quantity": 3}
- Send the exact same request two more times
- Send GET /cart
Expected result
Every PUT returns 200 with quantity 3. GET /cart shows quantity 3 for product 1.
Tip: click a test case to expand it.
Checklist
API testing checklist
0 of 14 checked
Check your understanding
Quick quiz
0/6 answered · 0 correct
1.You send a request with a valid customer token to an admin-only endpoint. What status code should you get?
2.Which method should be idempotent?
3.Creating a user with an email that already exists returns 500. Is that a bug?
4.Which Postman script checks that the response status is 200?
5.Why use {{baseUrl}} instead of typing https://api.staging.example.com in every request?
6.The UI blocks quantities below 1. Why test quantity -5 on the API anyway?
Key takeaways
- An API is how apps talk to servers. Testing it directly is fast, finds bugs the UI hides, and shows whether a bug is front end or back end.
- Know the basics: methods (GET, POST, PUT, PATCH, DELETE), status codes (2xx success, 4xx client error, 5xx server error), headers, JSON bodies and tokens.
- Remember 401 vs 403, 404 for missing items, 409 for duplicates, and that a 500 caused by user input is a bug.
- In Postman, use environments and variables, organize requests in collections, and automate checks with
pm.testandpm.expect. - Always test validation, auth, idempotency, the response schema against the docs, response time and error messages.
Go deeper: API Testing in Depth covers JWTs and expired tokens, authorization tests, negative requests, response checks beyond the status code, and a FoodDash API sandbox with 8 bugs to find.
Finished this page?Nice work, this page is complete!
Mark it as done to track your progress. It's saved in this browser, no account needed.
Its review cards are now in Bites ⚡