What Is API Testing?
API testing is the process of sending requests to an API endpoint and verifying that the response is correct -- the right status code, the right data structure, the right values, and the right performance. Unlike end-to-end UI testing, API testing skips the browser entirely and talks directly to your application's business logic layer.
This matters because APIs are the contract between your backend and everything that consumes it. A single API serves your web app, your mobile app, your partner integrations, and your internal microservices. A bug at the API level cascades everywhere.
According to the Postman 2024 State of the API Report, 75% of developers report that API-first development has increased their team's productivity. But only 36% have comprehensive API test coverage. That gap is where production incidents live.
You can start testing APIs in under 30 seconds with the QTool API Tester -- enter a URL, pick your method, add headers if needed, and hit send. No account, no download, no setup.
Types of API Testing
API testing is not a single activity. It encompasses several categories, each catching a different class of bugs.
Functional testing
The foundation. Functional tests verify that each endpoint returns the correct response for both valid and invalid inputs. For a GET /users/:id endpoint, functional tests would check:
- Valid ID returns 200 with the user object.
- Non-existent ID returns 404 with an error message.
- Invalid ID format (e.g.,
abcinstead of a number) returns 400. - Unauthorized request returns 401.
- Response includes all documented fields and correct data types.
Integration testing
Integration tests verify that your API works correctly with external dependencies -- databases, third-party APIs, message queues, and caches. A functional test might mock the database; an integration test uses a real (or realistic) database to catch issues like constraint violations, missing indexes, and connection pool exhaustion.
Load testing
Load tests measure how your API performs under stress. How many concurrent requests can it handle before response times degrade? At what point does it start returning 503 errors? Tools like k6, Artillery, and Locust simulate hundreds or thousands of concurrent users hitting your endpoints.
Security testing
Security tests probe your API for vulnerabilities: SQL injection, broken authentication, excessive data exposure, rate limiting bypass, and CORS misconfiguration. The OWASP API Security Top 10 is the standard checklist. Use the HTTP Status Codes reference to understand what your API should return for each security scenario.
Contract testing
Contract tests verify that the API response structure matches the documented schema. If your API documentation says a field is a string, contract tests fail when it returns a number. Tools like Pact and Schemathesis automate this.
Start with functional tests (they catch the most bugs per hour invested), then add integration tests for critical paths, then security tests, then load tests. Contract tests are especially valuable when multiple teams consume the same API.
Best Practices for API Testing
These practices apply regardless of which testing tool or framework you use.
- Test both happy and unhappy paths. Every endpoint needs tests for valid inputs (200s) and invalid inputs (400s, 401s, 404s, 422s). Most production bugs are in the unhappy paths because developers only tested the success case.
- Validate the response structure, not just the status code. A 200 status code means nothing if the response body is missing required fields or has incorrect data types. Parse the JSON and assert on specific fields.
- Use realistic test data. Test with names that have special characters, emails with plus signs, passwords at the maximum length, dates in different time zones. Edge cases in data cause more bugs than edge cases in logic.
- Test idempotency. Sending the same PUT or DELETE request twice should produce the same result. A non-idempotent DELETE that returns 200 on the first call and 500 on the second is a bug.
- Test response times. Set a maximum acceptable response time (e.g., 200ms for reads, 500ms for writes) and fail the test if the API exceeds it. Slow responses degrade user experience and can cascade into timeouts in downstream services.
- Isolate test data. Tests should create their own data, run assertions, and clean up after themselves. Never depend on data from previous test runs or shared staging environments.
- Version your API tests alongside your API code. When you change an endpoint, the test for that endpoint should change in the same commit. This prevents test drift.
Your First API Test
Let us walk through testing a real API endpoint using curl, which is pre-installed on macOS, Linux, and Windows 10+.
1. GET request
# Fetch a list of users from a public API
curl -s https://jsonplaceholder.typicode.com/users | head -20
# With headers displayed
curl -i https://jsonplaceholder.typicode.com/users/1
The -i flag shows response headers, which include the status code, content type, and caching information. The -s flag suppresses the progress bar for cleaner output.
2. POST request with JSON body
# Create a new resource
curl -X POST https://jsonplaceholder.typicode.com/posts \
-H "Content-Type: application/json" \
-d '{
"title": "API Testing Guide",
"body": "Testing APIs is essential for reliable software.",
"userId": 1
}'
3. Authenticated request
# Bearer token authentication
curl -H "Authorization: Bearer YOUR_TOKEN_HERE" \
https://api.example.com/me
# API key in header
curl -H "X-API-Key: YOUR_KEY_HERE" \
https://api.example.com/data
If you prefer a visual interface over the command line, the QTool API Tester lets you build these same requests with a form -- pick the method, enter the URL, add headers and body, and see the response formatted automatically.
To convert curl commands into code for your programming language, use the cURL to Code converter. It generates equivalent code in JavaScript, Python, Go, PHP, and more.
HTTP Status Codes Reference
Knowing which status code to expect is half of API testing. Here is a reference for the codes you will encounter most often.
| Code | Name | When to use |
|---|---|---|
200 | OK | Successful GET, PUT, or PATCH request |
201 | Created | Successful POST that created a new resource |
204 | No Content | Successful DELETE with no response body |
301 | Moved Permanently | Resource URL has changed permanently |
304 | Not Modified | Cached version is still valid (conditional GET) |
400 | Bad Request | Malformed request syntax or invalid parameters |
401 | Unauthorized | Missing or invalid authentication credentials |
403 | Forbidden | Authenticated but lacks permission for the resource |
404 | Not Found | Resource does not exist at the given URL |
409 | Conflict | Request conflicts with current state (e.g., duplicate) |
422 | Unprocessable Entity | Valid syntax but semantic errors in the data |
429 | Too Many Requests | Rate limit exceeded, retry after delay |
500 | Internal Server Error | Unhandled exception on the server |
502 | Bad Gateway | Upstream server returned an invalid response |
503 | Service Unavailable | Server is overloaded or under maintenance |
For the complete list with detailed explanations, see the QTool HTTP Status Codes reference.
Testing Authentication
Authentication testing is where security meets functionality. Every API that requires credentials should be tested against these scenarios.
# Test 1: Valid token returns 200
curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer VALID_TOKEN" \
https://api.example.com/me
# Expected: 200
# Test 2: Missing token returns 401
curl -s -o /dev/null -w "%{http_code}" \
https://api.example.com/me
# Expected: 401
# Test 3: Expired token returns 401
curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer EXPIRED_TOKEN" \
https://api.example.com/me
# Expected: 401
# Test 4: Valid token, wrong permissions returns 403
curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer USER_TOKEN" \
https://api.example.com/admin/users
# Expected: 403
The -o /dev/null flag discards the response body, and -w "%{http_code}" prints only the status code. This pattern is useful for quickly scripting authentication test suites.
If your API uses JWT tokens, validate the token structure and claims with the JWT Decoder. Paste a token to see its header, payload, and expiration time without making any API calls.
Always test that: tokens cannot be used after logout, tokens with tampered signatures are rejected, sensitive data is not included in the token payload, and rate limiting applies to authentication endpoints (to prevent brute-force attacks).
Testing Error Handling
A well-designed API returns consistent, informative error responses. Test that your API follows a standard error format.
// Good error response (consistent, informative)
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Email address is required",
"field": "email",
"status": 422
}
}
// Bad error response (inconsistent, unhelpful)
{
"error": true,
"msg": "Something went wrong"
}
Use the JSON Formatter to quickly inspect and prettify API error responses. For validating that responses match your expected schema, the JSON Validator checks structural correctness instantly.
What to test in error handling
- Missing required fields should return 422 with the field name in the error.
- Invalid data types (string where number expected) should return 400 or 422.
- Exceeding length limits should return 422 with the constraint that was violated.
- Internal errors should return 500 without exposing stack traces, database queries, or internal file paths.
- Rate limiting should return 429 with a
Retry-Afterheader.
Test APIs in Your Browser
Send GET, POST, PUT, and DELETE requests with custom headers and body. See formatted responses instantly. No signup, no download.
Open API TesterBest Free API Testing Tools
| Tool | Type | Best For | Limitations |
|---|---|---|---|
| QTool API Tester | Browser | Quick manual testing, no setup | Manual only, no scripting |
| curl | CLI | Scripting, CI/CD, universal availability | Steep learning curve for complex requests |
| HTTPie | CLI | Readable output, intuitive syntax | Requires installation (pip or brew) |
| Hoppscotch | Web / Desktop | Postman alternative, collections, environments | Some features require account |
| Bruno | Desktop | Git-friendly, offline-first, open-source | Newer tool, smaller ecosystem |
| REST Client (VS Code) | Extension | Testing inside your editor, .http files | VS Code only |
For testing webhooks and callback URLs, the Webhook Tester gives you a temporary URL that captures incoming requests -- useful for debugging OAuth callbacks, payment notifications, and event-driven integrations.
When you need to encode or decode data for API headers and payloads, the Base64 Encoder/Decoder handles conversion instantly.
Automating API Tests
Manual testing is a starting point. For production APIs, you need automated tests that run on every commit.
JavaScript (Jest + fetch)
// tests/api/users.test.js
describe('GET /api/users/:id', () => {
test('returns user for valid ID', async () => {
const res = await fetch('http://localhost:3000/api/users/1');
const data = await res.json();
expect(res.status).toBe(200);
expect(data).toHaveProperty('id', 1);
expect(data).toHaveProperty('name');
expect(data).toHaveProperty('email');
expect(typeof data.name).toBe('string');
});
test('returns 404 for non-existent ID', async () => {
const res = await fetch('http://localhost:3000/api/users/99999');
expect(res.status).toBe(404);
});
test('returns 400 for invalid ID format', async () => {
const res = await fetch('http://localhost:3000/api/users/abc');
expect(res.status).toBe(400);
});
});
Python (pytest + requests)
# tests/test_users_api.py
import requests
BASE_URL = "http://localhost:8000/api"
def test_get_user_valid():
response = requests.get(f"{BASE_URL}/users/1")
assert response.status_code == 200
data = response.json()
assert "id" in data
assert "name" in data
assert "email" in data
def test_get_user_not_found():
response = requests.get(f"{BASE_URL}/users/99999")
assert response.status_code == 404
def test_create_user():
payload = {"name": "Test User", "email": "test@example.com"}
response = requests.post(f"{BASE_URL}/users", json=payload)
assert response.status_code == 201
data = response.json()
assert data["name"] == "Test User"
CI/CD integration
# .github/workflows/api-tests.yml
name: API Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Start API server
run: npm start &
- name: Wait for server
run: sleep 5
- name: Run API tests
run: npm test -- --testPathPattern=api
Run API tests in parallel to keep CI pipelines fast. Group tests by endpoint, not by test type. A single endpoint's functional, auth, and validation tests should run together so you can immediately see if a change broke that endpoint.
Frequently Asked Questions
What is API testing and why is it important?
API testing is the process of verifying that an Application Programming Interface works correctly, reliably, and securely. Unlike UI testing, API testing operates at the business logic layer -- it sends requests directly to endpoints and validates the responses. API testing is important because APIs are the backbone of modern applications. A bug in an API can affect every client that consumes it: web apps, mobile apps, third-party integrations, and internal microservices. Catching API issues before deployment prevents cascading failures across your entire system.
What are the main types of API testing?
The main types of API testing are: (1) Functional testing -- verifies that each endpoint returns the correct response for valid and invalid inputs. (2) Integration testing -- confirms that APIs work correctly with other services, databases, and external dependencies. (3) Load testing -- measures how the API performs under high traffic and concurrent requests. (4) Security testing -- checks for vulnerabilities like injection attacks, broken authentication, and data exposure. (5) Contract testing -- ensures the API response structure matches the documented schema. Most teams start with functional testing and expand to other types as their API matures.
What is the best free API testing tool for developers?
The best free API testing tools in 2026 include: QTool API Tester (browser-based, no signup, supports GET/POST/PUT/DELETE with headers and body), curl (command-line, pre-installed on most systems, the universal baseline), HTTPie (developer-friendly CLI with readable output), Hoppscotch (open-source Postman alternative with a clean UI), and Bruno (offline-first, Git-friendly API client). For quick one-off tests, browser-based tools like QTool are the fastest since they require no installation. For automated test suites, consider combining curl or HTTPie with a test runner like Jest or pytest.
What does a 429 Too Many Requests HTTP status code mean?
HTTP 429 Too Many Requests means the client has sent too many requests in a given time period and has been rate-limited by the server. The response usually includes a Retry-After header indicating how many seconds to wait before retrying. To handle 429 errors, implement exponential backoff in your client code: wait 1 second after the first 429, then 2 seconds, then 4 seconds, doubling each time up to a maximum delay. Most APIs document their rate limits in their documentation -- check the X-RateLimit-Limit and X-RateLimit-Remaining response headers.
How do I test API authentication and authorization?
To test API authentication, verify these scenarios: (1) Valid credentials return a 200 response with the expected data. (2) Invalid or missing credentials return 401 Unauthorized. (3) Expired tokens return 401 with a clear error message. (4) Valid credentials but insufficient permissions return 403 Forbidden. (5) Tokens cannot be reused after logout or revocation. For JWT-based APIs, test that expired tokens are rejected, that tokens with tampered signatures fail validation, and that the token payload contains only necessary claims. Use tools like the QTool API Tester to send requests with different Authorization headers and verify each response.
What is the difference between a 401 and 403 HTTP status code?
401 Unauthorized means the request lacks valid authentication credentials -- the server does not know who you are. The client should authenticate (provide a valid API key, token, or login) and retry. 403 Forbidden means the server knows who you are (authentication succeeded) but you do not have permission to access the requested resource. Re-authenticating will not help; you need different permissions or a different role. In practice: 401 = "who are you?", 403 = "I know who you are, but you cannot do that."
Explore QTool for free
Browse 269 indexed tool pages with no QTool account required, and inspect the source on GitHub.
View on IT-Tools →