API Testing
Before automated tests, you need to poke your API and see what it really answers. Manual API testing with curl and Postman is fast, and the requests you save become living documentation.
On this page · 5 min · Advanced
On this page
- Learning objectives
- Explanation
- Syntax
- Code example
- Step by step
- Real-world use
- Common mistakes
- Best practices
- Remember this
- Practice exercise
- Mini quiz
- Related topics
- My notes
- Reading time
- 5 min
- Difficulty
- Advanced
- Updated
- Oct 28, 2026
Related tutorials
Learning objectives#
- Send GET, POST, PUT and DELETE requests with curl
- Organise requests in a Postman collection with variables
- Write simple Postman test scripts
- Cover happy paths, errors, auth and limits for every endpoint
Explanation#
curl ships with Windows 10+, macOS and Linux. -X sets the method, -H adds headers, -d sends a body, -i shows response headers.
Postman (or Insomnia, or Visual Studio’s .http files) saves requests in collections. Environment variables such as {{baseUrl}} and {{token}} let the same collection run against local, staging and production.
Postman test scripts run after each request: pm.test("status is 201", () => pm.response.to.have.status(201)). Run the whole collection with the Collection Runner or Newman in CI.
Swagger UI’s “Try it out” is a quick way to test while reading the docs.
For every endpoint, test: the happy path, invalid input (400), missing auth (401), wrong role (403), unknown id (404), conflicts (409) and limits (page size, file size).
Syntax#
// Postman → Tests tab for POST {{baseUrl}}/api/products
pm.test("returns 201", () => pm.response.to.have.status(201));
pm.test("has a Location header", () => pm.response.to.have.header("Location"));
pm.test("returns the new id", () => {
const body = pm.response.json();
pm.expect(body.Id).to.be.a("number");
pm.environment.set("productId", body.Id); // reuse in the next request
});
Code example: A curl session against the Shop API#
# 1. Sign in and get a token
curl -s -X POST https://localhost:44300/token -d "grant_type=password&username=asha&password=P%40ssw0rd!"
TOKEN="paste-the-access_token-here"
# 2. Happy path: read a product (with headers shown)
curl -i https://localhost:44300/api/products/1 -H "Authorization: Bearer $TOKEN"
# 3. Create a product
curl -i -X POST https://localhost:44300/api/products -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{"Name":"Bag","Price":650,"Stock":5,"Category":"Bags"}'
# 4. Invalid input
curl -i -X POST https://localhost:44300/api/products -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{"Name":"","Price":-1}'
# 5. No token
curl -i https://localhost:44300/api/products/1
# 6. Delete, then delete again
curl -i -X DELETE https://localhost:44300/api/products/2 -H "Authorization: Bearer $TOKEN"
curl -i -X DELETE https://localhost:44300/api/products/2 -H "Authorization: Bearer $TOKEN"
1 → {"access_token":"Ah3k...","token_type":"bearer","expires_in":3599}
2 → HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 {"Id":1,"Name":"Pen",...}
3 → HTTP/1.1 201 Created Location: https://localhost:44300/api/products/2
4 → HTTP/1.1 400 Bad Request {"Message":"The request is invalid.","ModelState":{...}}
5 → HTTP/1.1 401 Unauthorized
6 → HTTP/1.1 204 No Content, then HTTP/1.1 404 Not FoundThis example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.
Step-by-step explanation#
- The token request uses a form-encoded body, as the OWIN /token endpoint expects (%40 is @).
- Storing the token in a shell variable keeps later commands short.
- -i prints status line and headers, so you can check Location, Content-Type and caching headers.
- Requests 4 and 5 deliberately break the rules to check the error responses.
- Deleting twice confirms DELETE is idempotent: 204, then 404.
Real-world use case#
A team keeps a Postman collection in Git next to the API. Every pull request runs it with Newman against a test deployment, so a broken status code fails the build before code review.
Common mistakes#
Most production bugs are in error handling. Test 400, 401, 403, 404 and 409 on purpose.
Collections full of localhost URLs and expired tokens are useless to others. Use environment variables.
Exported environments can contain production passwords. Keep secrets in local, unshared variables.
Best practices#
- Save every request you send in a collection
- Use variables for base URL and token
- Add test scripts for status and key fields
- Run the collection in CI
Remember this#
curl -X METHOD -H "Header: value" -d body -i.
Happy path + 400 + 401 + 403 + 404 + 409.
Collections + variables + tests = reusable API checks.
Practice exercise#
Your task: Write curl commands that (1) update product 1’s name with PUT, (2) request page 2 with pageSize 5, and (3) check that pageSize=1000 is capped.
Need a hint?
Show solution
curl -i -X PUT https://localhost:44300/api/products/1 -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{"Name":"Gel Pen","Price":25,"Stock":40,"Category":"Stationery"}'
curl -s "https://localhost:44300/api/products?page=2&pageSize=5" -H "Authorization: Bearer $TOKEN"
curl -s "https://localhost:44300/api/products?page=1&pageSize=1000" -H "Authorization: Bearer $TOKEN"
# expect "PageSize":100 in the response
Mini quiz#
Which curl option shows response headers?
Want more? Practise all ASP.NET Web API MCQs · ASP.NET Web API interview questions
My notes#
Saved in this browser.