Manual API Testing for QA Testers Who Actually Test APIs

API testing shouldn’t require you to become a developer overnight. You just need to understand what you’re hitting, what you expect back, and how to document when things break.

This guide covers manual API testing using Postman and basic tools that work everywhere. No automation frameworks, no CI/CD pipelines, just you, your API, and testing that actually matters.

If you’re using the QA Journey Playground API for practice, this is your starting point.



Why Manual API Testing Still Matters

Let’s be real: automation is great until you need to debug a weird edge case at 3 PM on a Friday.

Manual API testing is still essential for:

  • Exploratory testing finding issues automation won’t catch
  • Verifying bug fixes quickly without updating test scripts
  • Understanding how the API actually behaves before automating anything
  • Documenting API behavior for developers who “forgot” to write docs

You can’t automate what you don’t understand. Manual testing comes first.

What You’re Actually Testing

Before smashing the “Send” button in Postman, understand what you’re validating:

1. Status Codes

Understanding HTTP status codes is critical. Here are the ones you’ll encounter most:

  • 200 OK: Success—data is returned as expected
  • 201 Created: Resource was created successfully
  • 400 Bad Request: You sent garbage data
  • 401 Unauthorized: No valid token/credentials
  • 403 Forbidden: You’re authenticated but don’t have permission
  • 404 Not Found: Endpoint or resource doesn’t exist
  • 405 Method Not Allowed: Using POST when only GET is allowed
  • 429 Too Many Requests: Rate limited—you’re hitting the API too fast
  • 500 Internal Server Error: Backend is broken (not your fault)
  • 502 Bad Gateway: Server acting as proxy got invalid response
  • 503 Service Unavailable: Server is down or overloaded

Full reference: MDN HTTP Status Codes

QA-Proven Hack: Bookmark that MDN page. When you get a weird status code like 418 (I’m a teapot), you’ll want the official definition.

2. Response Structure Does the JSON match what the API promised? Are required fields present? Is the data type correct?

3. Response Time Is the endpoint fast enough? If it takes 10 seconds to return user data, that’s a problem.

4. Error Messages When things fail, do the error messages actually help? “Error” is not helpful. “Email already exists” is.

5. Edge Cases What happens when you send:

  • Empty strings
  • Null values
  • Extremely long inputs
  • Special characters
  • Non-existent IDs

This is where bugs hide.





The QA Journey Playground API

I built a mock API for testing practice. It’s live at playground.qajourney.net/api and includes realistic endpoints testers actually encounter.

Available Endpoints

Users Module

GET    /api/users                     → List all users
GET    /api/users?id=1                → Get single user
POST   /api/users/create.php          → Create user
PUT    /api/users/update.php?id=1     → Update user
DELETE /api/users/delete.php?id=1    → Delete user

Products Module

GET  /api/products           → List all products
GET  /api/products?id=1      → Get single product
POST /api/products/create.php → Create product

Authentication Module

POST /api/auth/login.php    → Login (returns token)
POST /api/auth/logout.php   → Logout
GET  /api/auth/verify.php   → Verify token (requires Authorization header)

Test Scenarios Module (for practicing QA skills)

GET /api/test/slow-endpoint.php     → 3-second delay
GET /api/test/random-failure.php    → 50% chance of 500 error
GET /api/test/rate-limited.php      → Limited to 5 requests/minute

These endpoints simulate real-world API behavior. Use them to practice before hitting production APIs.

Manual API Testing with Postman

Postman is free, works on Windows/macOS/Linux, and doesn’t require a subscription to be useful.

Download: https://www.postman.com/downloads/

Writing Test Cases with Gherkin

Before jumping into Postman, write your test cases in plain English using Gherkin syntax. This helps you think through what you’re testing before you start clicking buttons.

Gherkin Format:

Feature: [What you're testing]
  Scenario: [Specific test case]
    Given [Initial state]
    When [Action taken]
    Then [Expected result]

Example Test Case:

Feature: User Management API
  Scenario: Successfully retrieve a user by ID
    Given the API is available at https://playground.qajourney.net/api
    And user with ID 1 exists in the database
    When I send a GET request to /api/users?id=1
    Then the response status code should be 200
    And the response should contain user data with id, name, and email
    And the response time should be less than 2000ms

Why Gherkin?

  • Forces you to think through the test before executing
  • Creates documentation developers can actually read
  • Translates directly into automated tests later (when you’re ready)
  • Stakeholders understand “Given/When/Then” without technical knowledge

QA-Proven Hack: I keep a test-cases.md file with all my Gherkin scenarios. When automating later, I just copy-paste the scenarios into my test framework.

Understanding Assertions: Chai/Mocha Basics

Postman’s test scripts use Chai assertions under the hood. Understanding Chai syntax now means you’re already halfway to automation.

Common Chai Assertions:

// Status code checks
expect(response.status).to.equal(200);
expect(response.status).to.be.oneOf([200, 201]);

// Property checks
expect(data).to.have.property('id');
expect(data).to.have.all.keys('id', 'name', 'email');

// Type checks
expect(data.id).to.be.a('number');
expect(data.name).to.be.a('string');

// Value checks
expect(data.name).to.equal('John Doe');
expect(data.email).to.include('@');

// Array checks
expect(data.users).to.be.an('array');
expect(data.users).to.have.lengthOf(5);

Postman wraps these in pm.test() blocks, but the assertion syntax is identical to Mocha/Chai test suites.

Why This Matters: When you automate with Mocha + Chai later, you’re using the exact same assertions. Learning them in Postman means zero learning curve for automation.

Setting Up Your Workspace

Step 1: Create a Collection

  1. Open Postman
  2. Click “New” → “Collection”
  3. Name it “QA Journey API Tests”

Step 2: Set Base URL Variable

  1. Click on your collection → “Variables” tab
  2. Add a variable:
    • Variable: base_url
    • Initial Value: https://playground.qajourney.net/api
  3. Save

Now use {{base_url}}/users instead of typing the full URL every time.

QA-Proven Hack: Use variables for everything that changes URLs, tokens, user IDs. When the API moves to staging or production, you just update one variable instead of 50 requests.





Testing Real Scenarios (Happy Path)

Test 1: Get All Users

Gherkin Test Case:

Feature: User Management API
  Scenario: Successfully retrieve all users
    Given the API is available
    When I send a GET request to /api/users
    Then the response status code should be 200
    And the response should contain a users array
    And the response time should be less than 2000ms

Postman Setup:

Method: GET
URL: {{base_url}}/users

What to Check:

  • Status code is 200
  • Response contains a users array
  • Response time is under 2 seconds

Postman Tests Tab (Chai Assertions):

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
    // Chai equivalent: expect(response.status).to.equal(200);
});

pm.test("Response has users array", function () {
    const jsonData = pm.response.json();
    pm.expect(jsonData).to.have.property('users');
    // Chai: expect(jsonData).to.have.property('users');
    
    pm.expect(jsonData.users).to.be.an('array');
    // Chai: expect(jsonData.users).to.be.an('array');
});

pm.test("Response time is acceptable", function () {
    pm.expect(pm.response.responseTime).to.be.below(2000);
    // Chai: expect(response.responseTime).to.be.below(2000);
});

Hit “Send”. If all three tests pass, the endpoint works as expected.

QA-Proven Hack: The comments show equivalent Chai syntax. When you automate with Mocha/Chai, you’re literally copying these assertions into your test files.

Test 2: Get Single User

Gherkin Test Case:

Feature: User Management API
  Scenario: Successfully retrieve a single user by ID
    Given the API is available
    And user with ID 1 exists
    When I send a GET request to /api/users?id=1
    Then the response status code should be 200
    And the response should contain id, name, and email fields
    And all fields should have correct data types

Postman Setup:

Method: GET
URL: {{base_url}}/users?id=1

Postman Tests Tab (Chai Assertions):

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("User has required fields", function () {
    const user = pm.response.json();
    
    // Check all required properties exist
    pm.expect(user).to.have.all.keys('id', 'name', 'email');
    // Chai: expect(user).to.have.all.keys('id', 'name', 'email');
});

pm.test("Data types are correct", function () {
    const user = pm.response.json();
    
    pm.expect(user.id).to.be.a('number');
    pm.expect(user.name).to.be.a('string');
    pm.expect(user.email).to.be.a('string');
    
    // Additional validation
    pm.expect(user.email).to.include('@');
    // Chai: expect(user.email).to.include('@');
});

Test 3: Create User

Gherkin Test Case:

Feature: User Management API
  Scenario: Successfully create a new user
    Given the API is available
    When I send a POST request to /api/users/create.php
    And the request body contains valid user data
      | name     | email            | password      |
      | Test User| [email protected] | SecurePass123 |
    Then the response status code should be 201
    And the response should contain the created user's ID
    And the response should contain a success message

Postman Setup:

Method: POST
URL: {{base_url}}/users/create.php
Headers:
  Content-Type: application/json
Body (raw JSON):
{
  "name": "Test User",
  "email": "[email protected]",
  "password": "SecurePass123"
}

Postman Tests Tab (Chai Assertions):

pm.test("Status code is 201 (Created)", function () {
    pm.response.to.have.status(201);
    // Note: 201 means resource created, not 200
});

pm.test("User created successfully", function () {
    const response = pm.response.json();
    
    pm.expect(response).to.have.property('id');
    pm.expect(response.id).to.be.a('number');
    
    pm.expect(response.message).to.include('created');
    // Chai: expect(response.message).to.include('created');
});

// Save the user ID for later tests (dependency management)
if (pm.response.code === 201) {
    const userId = pm.response.json().id;
    pm.environment.set("created_user_id", userId);
    console.log("Created user ID:", userId);
}

QA-Proven Hack: Always save IDs from creation responses. You’ll need them for update/delete tests without manually copying values.

Test 4: Update User

Gherkin Test Case:

Feature: User Management API
  Scenario: Successfully update an existing user
    Given the API is available
    And a user exists with ID {{created_user_id}}
    When I send a PUT request to /api/users/update.php?id={{created_user_id}}
    And the request body contains updated user data
    Then the response status code should be 200
    And the response should confirm the update

Postman Setup:

Method: PUT
URL: {{base_url}}/users/update.php?id={{created_user_id}}
Body (raw JSON):
{
  "name": "Updated Name",
  "email": "[email protected]"
}

Postman Tests Tab:

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Update confirmed", function () {
    const response = pm.response.json();
    pm.expect(response.message).to.include('updated');
});

Test 5: Delete User

Gherkin Test Case:

Feature: User Management API
  Scenario: Successfully delete a user
    Given the API is available
    And a user exists with ID {{created_user_id}}
    When I send a DELETE request to /api/users/delete.php?id={{created_user_id}}
    Then the response status code should be 200
    And the response should confirm deletion
    
  Scenario: Verify user no longer exists after deletion
    Given a user was deleted with ID {{created_user_id}}
    When I send a GET request to /api/users?id={{created_user_id}}
    Then the response status code should be 404

Postman Setup:

Method: DELETE
URL: {{base_url}}/users/delete.php?id={{created_user_id}}

Postman Tests Tab:

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Deletion confirmed", function () {
    const response = pm.response.json();
    pm.expect(response.message).to.include('deleted');
});

Follow-up Check (Create separate request): After deleting, try to GET the same user. You should get a 404.

Test 6: Login & Token Verification

Gherkin Test Case:

Feature: Authentication API
  Scenario: Successfully login and receive authentication token
    Given the API is available
    When I send a POST request to /api/auth/login.php
    And the request body contains valid credentials
      | email               | password    |
      | [email protected] | password123 |
    Then the response status code should be 200
    And the response should contain a token
    And the token should be a non-empty string
    
  Scenario: Successfully verify authentication token
    Given I have a valid authentication token
    When I send a GET request to /api/auth/verify.php
    And the Authorization header contains the token
    Then the response status code should be 200
    And the response should confirm the token is valid

Step 1: Login

Method: POST
URL: {{base_url}}/auth/login.php
Body (raw JSON):
{
  "email": "[email protected]",
  "password": "password123"
}

Postman Tests Tab (Chai Assertions):

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Token is returned", function () {
    const response = pm.response.json();
    
    pm.expect(response).to.have.property('token');
    pm.expect(response.token).to.be.a('string');
    pm.expect(response.token).to.not.be.empty;
    // Chai: expect(response.token).to.not.be.empty;
    
    // Save token for authenticated requests
    pm.environment.set("auth_token", response.token);
    console.log("Auth token saved:", response.token.substring(0, 20) + "...");
});

Step 2: Verify Token

Method: GET
URL: {{base_url}}/auth/verify.php
Headers:
  Authorization: Bearer {{auth_token}}

Postman Tests Tab:

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Token is valid", function () {
    const response = pm.response.json();
    pm.expect(response.valid).to.be.true;
    // Chai: expect(response.valid).to.be.true;
});

This simulates how real authentication flows work. Login once, use the token for subsequent requests.

Testing Real Scenarios (Sad Path)

Happy paths are easy. Sad paths expose the real bugs.

Test 7: Get Non-Existent User

Gherkin Test Case:

Feature: User Management API
  Scenario: Attempt to retrieve a non-existent user
    Given the API is available
    When I send a GET request to /api/users?id=999999
    Then the response status code should be 404
    And the response should contain an error message
    And the error message should be descriptive

Postman Setup:

Method: GET
URL: {{base_url}}/users?id=999999

Postman Tests Tab (Chai Assertions):

pm.test("Status code is 404 (Not Found)", function () {
    pm.response.to.have.status(404);
    // NOT 200 with empty data - that's a bug
});

pm.test("Error message is helpful", function () {
    const response = pm.response.json();
    
    pm.expect(response).to.have.property('error');
    // Chai: expect(response).to.have.property('error');
    
    pm.expect(response.error).to.not.equal('Error');
    // Generic messages are useless - should be specific
    // Good: "User with ID 999999 not found"
    // Bad: "Error"
});

Why This Matters: Returning 200 with empty data is a common mistake. The frontend will break trying to render non-existent users.

Test 8: Create User with Invalid Data

Gherkin Test Case:

Feature: User Management API
  Scenario: Attempt to create user with invalid email format
    Given the API is available
    When I send a POST request to /api/users/create.php
    And the request body contains invalid data
      | name | email        |
      |      | not-an-email |
    Then the response status code should be 400
    And the response should specify which fields are invalid
    And no user should be created in the database

Postman Setup:

Method: POST
URL: {{base_url}}/users/create.php
Body (raw JSON):
{
  "name": "",
  "email": "not-an-email"
}

Postman Tests Tab (Chai Assertions):

pm.test("Status code is 400 (Bad Request)", function () {
    pm.response.to.have.status(400);
});

pm.test("Validation errors are specific", function () {
    const response = pm.response.json();
    
    pm.expect(response.error).to.exist;
    // Chai: expect(response.error).to.exist;
    
    // Good error: "Email format is invalid, Name is required"
    // Bad error: "Validation failed"
    pm.expect(response.error).to.include('email').or.include('Email');
});

QA-Proven Hack: Test with intentionally broken data. Empty strings, wrong formats, missing required fields. If the API doesn’t reject garbage, the backend has no validation.

Test 9: Login with Wrong Credentials

Gherkin Test Case:

Feature: Authentication API
  Scenario: Attempt to login with invalid credentials
    Given the API is available
    When I send a POST request to /api/auth/login.php
    And the request body contains incorrect credentials
      | email             | password  |
      | [email protected] | wrongpass |
    Then the response status code should be 401
    And no authentication token should be returned
    And the error message should not reveal which field is incorrect

Postman Setup:

Method: POST
URL: {{base_url}}/auth/login.php
Body (raw JSON):
{
  "email": "[email protected]",
  "password": "wrongpass"
}

Postman Tests Tab (Chai Assertions):

pm.test("Status code is 401 (Unauthorized)", function () {
    pm.response.to.have.status(401);
});

pm.test("No token returned", function () {
    const response = pm.response.json();
    pm.expect(response).to.not.have.property('token');
    // Chai: expect(response).to.not.have.property('token');
});

pm.test("Error message doesn't reveal details", function () {
    const response = pm.response.json();
    
    // Good: "Invalid credentials"
    // Bad: "Email not found" (helps attackers enumerate accounts)
    pm.expect(response.error).to.include('Invalid');
    pm.expect(response.error).to.not.include('Email not found');
    pm.expect(response.error).to.not.include('Wrong password');
});

Good error: “Invalid credentials”
Bad error: “Email not found” (helps attackers enumerate accounts)

Test 10: Access Protected Endpoint Without Token

Gherkin Test Case:

Feature: Authentication API
  Scenario: Attempt to access protected endpoint without authentication
    Given the API is available
    When I send a GET request to /api/auth/verify.php
    And no Authorization header is provided
    Then the response status code should be 401
    And the response should indicate authentication is required

Postman Setup:

Method: GET
URL: {{base_url}}/auth/verify.php
Headers: (Remove Authorization header)

Postman Tests Tab:

pm.test("Status code is 401", function () {
    pm.response.to.have.status(401);
});

pm.test("Error indicates auth required", function () {
    const response = pm.response.json();
    pm.expect(response.error).to.include('auth').or.include('token');
});

Test 11: Rate Limiting

Gherkin Test Case:

Feature: API Rate Limiting
  Scenario: Verify rate limiting is enforced
    Given the API is available
    When I send 6 consecutive GET requests to /api/test/rate-limited.php
    Then the first 5 requests should return status code 200
    And the 6th request should return status code 429
    And the error message should explain the rate limit

Postman Setup:

Method: GET
URL: {{base_url}}/test/rate-limited.php

What to Do: Hit “Send” 6 times quickly (or use Postman Runner with 6 iterations).

Postman Tests Tab (Chai Assertions):

pm.test("Rate limit enforced", function () {
    const statusCode = pm.response.code;
    
    if (statusCode === 429) {
        // Rate limited
        pm.expect(pm.response.json().error).to.include('Rate limit');
        // Chai: expect(response.error).to.include('Rate limit');
    } else {
        // Should be 200
        pm.expect(statusCode).to.equal(200);
    }
});

QA-Proven Hack: If the API doesn’t have rate limiting, mention it in your test report. It’s a security and performance issue.

Test 12: Slow Endpoint (Performance)

Gherkin Test Case:

Feature: API Performance Testing
  Scenario: Verify slow endpoint handles delays properly
    Given the API is available
    When I send a GET request to /api/test/slow-endpoint.php
    Then the response status code should be 200
    And the response time should be approximately 3 seconds
    And the response should contain data despite the delay

Postman Setup:

Method: GET
URL: {{base_url}}/test/slow-endpoint.php

Postman Tests Tab (Chai Assertions):

pm.test("Endpoint responds despite delay", function () {
    pm.response.to.have.status(200);
});

pm.test("Response time reflects intentional delay", function () {
    const responseTime = pm.response.responseTime;
    
    pm.expect(responseTime).to.be.above(2900);
    pm.expect(responseTime).to.be.below(3500);
    // Chai: expect(responseTime).to.be.within(2900, 3500);
});

pm.test("Response contains data", function () {
    const response = pm.response.json();
    pm.expect(response).to.not.be.empty;
});

Why This Matters: Some endpoints are slow by design (reports, exports). Test that timeouts are configured properly.

Test 13: Random Failures (Flakiness)

Gherkin Test Case:

Feature: API Reliability Testing
  Scenario: Verify handling of intermittent failures
    Given the API is available
    When I send multiple GET requests to /api/test/random-failure.php
    Then some requests should return status code 200
    And some requests should return status code 500
    And failures should occur randomly

Postman Setup:

Method: GET
URL: {{base_url}}/test/random-failure.php

What to Do: Hit “Send” 10 times (use Postman Runner).

Postman Tests Tab:

pm.test("Endpoint behavior is documented", function () {
    const statusCode = pm.response.code;
    
    // Accept both success and failure
    pm.expect(statusCode).to.be.oneOf([200, 500]);
    // Chai: expect(statusCode).to.be.oneOf([200, 500]);
    
    console.log("Status:", statusCode);
});

Why This Matters: Real APIs have intermittent failures. Test how your frontend handles unpredictable errors.





How to Document API Bugs

When an API breaks, don’t just say “it doesn’t work.” Document it properly.

Bug Report Template

**Bug:** [Describe what's broken]

**Endpoint:** [Full URL including query params]

**Method:** [GET/POST/PUT/DELETE]

**Request:**
Headers:
  Content-Type: application/json
  Authorization: Bearer [token]

Body:
{
  "field": "value"
}

**Expected Response:**
Status Code: 200
Body:
{
  "expected": "data"
}

**Actual Response:**
Status Code: 500
Body:
{
  "error": "Internal Server Error"
}

**Environment:**
- API Base URL: https://playground.qajourney.net/api
- Tested on: 2025-01-15 14:30 UTC
- Postman Version: 10.x

**Steps to Reproduce:**
1. Login to get auth token
2. Send POST to /api/users/create.php with [specific payload]
3. Observe 500 error

**Attachments:**
[Screenshot of Postman request/response]

Copy this template into GitHub Issues, Jira, Taiga, or wherever your team tracks bugs.

QA-Proven Hack: I keep this template in a Google Doc. Copy-paste for every API bug. Consistent reports = faster fixes.

Common API Testing Mistakes

Mistake 1: Only Testing Happy Paths

If you’re not testing with invalid data, you’re not really testing.

Mistake 2: Ignoring Status Codes

200 doesn’t always mean success. Check the response body.

Mistake 3: Not Testing Without Authentication

Try accessing protected endpoints without tokens. If they work, that’s a security issue.

Mistake 4: Skipping Edge Cases

Test with:

  • Empty strings
  • Null values
  • Extremely long inputs (1000+ characters)
  • Special characters (&, <, >, “, ‘)
  • SQL injection attempts (' OR '1'='1)
  • XSS attempts (<script>alert('test')</script>)

Mistake 5: Not Documenting API Behavior

If the API doesn’t have docs, YOU are the docs. Document expected behavior so developers know what to fix.

Quick Reference: Testing Checklist

For every endpoint, verify:

Basic Functionality:

  • [ ] Correct status code (200, 201, 400, 401, 404, etc.)
  • [ ] Response structure matches expectations
  • [ ] Required fields are present
  • [ ] Data types are correct

Error Handling:

  • [ ] Invalid data returns 400
  • [ ] Missing auth returns 401
  • [ ] Non-existent resources return 404
  • [ ] Rate limiting returns 429
  • [ ] Server errors return 500

Performance:

  • [ ] Response time is acceptable (< 2s for most endpoints)
  • [ ] Slow endpoints have proper timeouts
  • [ ] Large datasets don’t crash the API

Security:

  • [ ] Protected endpoints require authentication
  • [ ] Tokens expire properly
  • [ ] Error messages don’t leak sensitive data
  • [ ] SQL injection doesn’t work
  • [ ] XSS attempts are sanitized

Edge Cases:

  • [ ] Empty/null values are handled
  • [ ] Special characters don’t break the API
  • [ ] Extremely long inputs are rejected
  • [ ] Duplicate submissions are prevented

Tools Beyond Postman

Postman is great, but sometimes you need lighter tools.

curl (Command Line)

Works on Windows (PowerShell/Git Bash), macOS, Linux—no installation on macOS/Linux.

Basic GET:

curl https://playground.qajourney.net/api/users

GET with query parameter:

curl "https://playground.qajourney.net/api/users?id=1"

POST with JSON:

curl -X POST https://playground.qajourney.net/api/users/create.php \
  -H "Content-Type: application/json" \
  -d '{"name":"Test","email":"[email protected]","password":"pass123"}'

With authentication:

curl https://playground.qajourney.net/api/auth/verify.php \
  -H "Authorization: Bearer YOUR_TOKEN"

Get just the status code:

curl -s -o /dev/null -w "%{http_code}" https://playground.qajourney.net/api/users

QA-Proven Hack: Save common curl commands in a text file. Faster than opening Postman when you need to test one endpoint quickly.

Browser DevTools (Network Tab)

If the API is called from a web page:

  1. Open DevTools (F12)
  2. Go to Network tab
  3. Interact with the page
  4. Click on API requests to see:
    • Request headers
    • Request payload
    • Response headers
    • Response body
    • Response time

Why This Matters: Sometimes the API works fine in Postman but breaks in the browser. DevTools shows you exactly what the browser is sending.

The Takeaway

Manual API testing isn’t about memorizing HTTP methods. It’s about understanding what the API promises, verifying it delivers, and documenting when it doesn’t.

Start with:

  1. Test happy paths (does it work as expected?)
  2. Test sad paths (does it fail gracefully?)
  3. Test edge cases (does it handle garbage data?)
  4. Document everything (so developers can actually fix bugs)

You don’t need automation frameworks to test APIs. You need curiosity, attention to detail, and a structured approach.

The QA Journey Playground API is live for practice: playground.qajourney.net/api

Start testing. Break things. Document what breaks.

Related guides:

Jaren Cudilla
Jaren Cudilla
QA Overlord

Leads QA teams through real-world testing, from Postman APIs to Gherkin scripts, without the fluff.
Turns theoretical “API testing” concepts into systems that actually catch bugs.
Has built and trained teams that survived release day chaos, and still finds issues seasoned automation misses.

Leave a Comment