REST API Testing Guide for Beginners

DevToolkit Team · · 15 min read

Whether you're a frontend developer integrating a backend, a QA engineer writing test suites, or a backend developer debugging your own endpoints, knowing how to test REST APIs is a fundamental skill. This guide takes you from zero to confidently testing any API.

We'll cover HTTP fundamentals, the most common testing patterns, authentication, error handling, and hands-on examples using curl, Postman, and browser-based tools. By the end, you'll be able to test any REST API you encounter.

What Is a REST API?

REST (Representational State Transfer) is an architectural style for building web APIs. A REST API exposes resources (data entities like users, orders, products) at URLs and uses standard HTTP methods to manipulate them.

The key principles:

HTTP Methods: The Big Five

Every REST API test starts with choosing the right HTTP method.

GET — Read Data

Retrieves a resource without modifying it. GET requests should be idempotent — calling them multiple times produces the same result.

GET /api/users          → List all users
GET /api/users/123      → Get user with ID 123
GET /api/users?role=admin&limit=10  → Filtered list

When testing GET requests, check:

POST — Create Data

Creates a new resource. The request body contains the data for the new resource.

POST /api/users
Content-Type: application/json

{
  "name": "Alice",
  "email": "[email protected]",
  "role": "user"
}

When testing POST requests, check:

PUT — Replace Data

Replaces an entire resource. If the resource doesn't exist, some APIs create it (upsert behavior).

PUT /api/users/123
Content-Type: application/json

{
  "name": "Alice Updated",
  "email": "[email protected]",
  "role": "admin"
}

PUT should be idempotent — sending the same PUT request twice should have the same effect as sending it once.

PATCH — Partial Update

Updates specific fields without replacing the entire resource.

PATCH /api/users/123
Content-Type: application/json

{
  "role": "admin"
}

Only the role field changes; name and email stay the same. PATCH is more bandwidth-efficient than PUT for small updates.

DELETE — Remove Data

DELETE /api/users/123

When testing DELETE, verify:

HTTP Status Codes You Must Know

CodeMeaningWhen You'll See It
200OKSuccessful GET, PUT, PATCH, DELETE
201CreatedSuccessful POST that creates a resource
204No ContentSuccessful DELETE with no response body
301/302RedirectResource moved; follow the Location header
400Bad RequestMalformed JSON, missing required fields, invalid values
401UnauthorizedMissing or invalid authentication
403ForbiddenAuthenticated but not authorized for this action
404Not FoundResource doesn't exist
409ConflictDuplicate creation, version conflict
422Unprocessable EntityValid JSON but business rule violation
429Too Many RequestsRate limited; check Retry-After header
500Internal Server ErrorServer bug — not your fault (usually)
502/503Bad Gateway / UnavailableServer is down or overloaded

Essential Headers

HTTP headers carry metadata about the request and response. Here are the ones you'll use in every API test:

Request Headers

Content-Type: application/json          # "I'm sending JSON"
Accept: application/json                 # "I want JSON back"
Authorization: Bearer eyJhbGciOiJI...   # Auth token
X-Request-ID: 550e8400-e29b-41d4       # Trace ID for debugging
If-None-Match: "etag-value"             # Conditional GET (caching)

Response Headers

Content-Type: application/json          # Response format
Location: /api/users/456                # URL of created resource
X-RateLimit-Limit: 100                  # Max requests per window
X-RateLimit-Remaining: 42              # Requests left
Retry-After: 30                         # Seconds to wait (429)
ETag: "33a64df5"                        # Resource version for caching

Testing with curl

curl is the universal API testing tool — available on every OS, scriptable, and precise. Here are the patterns you'll use daily.

GET Request

# Simple GET
curl https://api.example.com/users

# With headers and pretty-print
curl -s https://api.example.com/users \
  -H "Accept: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" | jq .

POST Request

# Create a user
curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -d '{"name": "Alice", "email": "[email protected]"}'

# From a file
curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -d @user.json

PUT / PATCH / DELETE

# Full update
curl -X PUT https://api.example.com/users/123 \
  -H "Content-Type: application/json" \
  -d '{"name": "Alice Updated", "email": "[email protected]", "role": "admin"}'

# Partial update
curl -X PATCH https://api.example.com/users/123 \
  -H "Content-Type: application/json" \
  -d '{"role": "admin"}'

# Delete
curl -X DELETE https://api.example.com/users/123 \
  -H "Authorization: Bearer YOUR_TOKEN"

Useful curl Flags

-v            # Verbose: show request/response headers
-s            # Silent: no progress bar
-o /dev/null  # Discard body (useful with -w)
-w "%{http_code}\n"  # Print just the status code
-L            # Follow redirects
-k            # Skip TLS verification (dev only!)
--max-time 10 # Timeout after 10 seconds

API Authentication Patterns

Most APIs require authentication. Here are the four most common patterns and how to test each.

1. API Key (Header or Query)

# In header (preferred)
curl -H "X-API-Key: your-api-key" https://api.example.com/data

# In query string (less secure)
curl "https://api.example.com/data?api_key=your-api-key"

2. Bearer Token (OAuth2 / JWT)

# Get token
curl -X POST https://auth.example.com/token \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_ID" \
  -d "client_secret=YOUR_SECRET"

# Use token
curl -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
  https://api.example.com/protected

3. Basic Auth

# curl handles encoding automatically
curl -u "username:password" https://api.example.com/data

# Equivalent header
curl -H "Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=" \
  https://api.example.com/data

4. Cookie-Based (Session)

# Login and save cookies
curl -c cookies.txt -X POST https://app.example.com/login \
  -d '{"email": "[email protected]", "password": "secret"}'

# Use saved cookies
curl -b cookies.txt https://app.example.com/dashboard

Testing Error Cases

Good API testing isn't just about the happy path. Here's a checklist of error cases every API should handle:

Validation Errors (400)

# Missing required field
curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -d '{"name": "Alice"}'
# Expected: 400 with message about missing email

# Wrong data type
curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -d '{"name": "Alice", "email": "[email protected]", "age": "not-a-number"}'
# Expected: 400 with type validation error

# Empty body
curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -d '{}'
# Expected: 400 with list of missing fields

Authentication Errors (401/403)

# No auth header
curl https://api.example.com/protected
# Expected: 401

# Expired token
curl -H "Authorization: Bearer expired-token" \
  https://api.example.com/protected
# Expected: 401

# Valid token, wrong permissions
curl -H "Authorization: Bearer user-token" \
  -X DELETE https://api.example.com/admin/users/123
# Expected: 403

Not Found (404)

# Non-existent resource
curl https://api.example.com/users/99999
# Expected: 404 with meaningful message

# Wrong URL
curl https://api.example.com/userz
# Expected: 404

Rate Limiting (429)

# Rapid-fire requests
for i in $(seq 1 100); do
  curl -s -o /dev/null -w "%{http_code}\n" \
    https://api.example.com/data
done
# Expected: 429 after exceeding limit, with Retry-After header

Writing Automated API Tests

Once you're comfortable testing manually, automate your tests. Here's a simple example in JavaScript using fetch and a test runner:

// api.test.js (using Node.js built-in test runner)
import { describe, it } from 'node:test';
import assert from 'node:assert';

const BASE_URL = 'https://api.example.com';

describe('Users API', () => {
  it('GET /users returns 200 and array', async () => {
    const res = await fetch(`${BASE_URL}/users`);
    assert.strictEqual(res.status, 200);

    const data = await res.json();
    assert.ok(Array.isArray(data));
    assert.ok(data.length > 0);
  });

  it('POST /users creates a user', async () => {
    const res = await fetch(`${BASE_URL}/users`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        name: 'Test User',
        email: `test-${Date.now()}@example.com`
      })
    });

    assert.strictEqual(res.status, 201);
    const user = await res.json();
    assert.ok(user.id);
    assert.strictEqual(user.name, 'Test User');
  });

  it('GET /users/999999 returns 404', async () => {
    const res = await fetch(`${BASE_URL}/users/999999`);
    assert.strictEqual(res.status, 404);
  });

  it('POST /users without email returns 400', async () => {
    const res = await fetch(`${BASE_URL}/users`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name: 'No Email' })
    });
    assert.strictEqual(res.status, 400);
  });
});

Python with pytest

import requests
import pytest

BASE_URL = "https://api.example.com"

def test_get_users():
    res = requests.get(f"{BASE_URL}/users")
    assert res.status_code == 200
    data = res.json()
    assert isinstance(data, list)
    assert len(data) > 0

def test_create_user():
    payload = {"name": "Test", "email": f"test-{id(object())}@example.com"}
    res = requests.post(f"{BASE_URL}/users", json=payload)
    assert res.status_code == 201
    assert "id" in res.json()

def test_not_found():
    res = requests.get(f"{BASE_URL}/users/999999")
    assert res.status_code == 404

def test_validation_error():
    res = requests.post(f"{BASE_URL}/users", json={"name": "No Email"})
    assert res.status_code == 400

API Testing Best Practices

  1. Test the contract, not the implementation. Your tests should verify the API's documented behavior — status codes, response shape, error messages. Don't test internal database state.
  2. Use unique test data. Generate unique emails, names, and IDs for each test run to avoid collisions. Timestamps or UUIDs work well.
  3. Clean up after yourself. If your test creates a user, delete it afterward. Use setup/teardown hooks or a dedicated test database.
  4. Test edge cases. Empty strings, very long strings, special characters, Unicode, null values, zero, negative numbers, boundary values (max int, empty arrays).
  5. Check response times. A test that passes in 30 seconds is a broken API. Set timeouts and assert on response time.
  6. Version your test collections. Keep API tests in version control alongside the code. They're documentation and regression safety nets.
  7. Test auth thoroughly. Missing tokens, expired tokens, wrong scopes, cross-tenant access — auth bugs are security bugs.

Test APIs Online — No Setup Required

Sometimes you need to quickly test an endpoint without setting up Postman or writing a script. DevToolkit's free API Tester lets you send any HTTP request right in your browser — GET, POST, PUT, PATCH, DELETE with custom headers, body, and authentication.

It shows the full response including status code, headers, timing, and a formatted JSON body. Perfect for quick debugging, exploring a new API, or sharing reproducible requests with your team. No signup, no install.

Common Mistakes When Testing APIs

Conclusion

API testing is a core skill that pays dividends in every project. Start with manual testing using curl or a browser-based tool to understand the API, then automate your tests for regression safety. Always test both the happy path and error cases.

Ready to test an API right now? Open DevToolkit's API Tester — send requests, inspect responses, and debug endpoints in seconds. Free, in your browser, no setup needed.

Enjoyed this article?

Get the free Developer Cheatsheet Pack + weekly tips on tools, workflows, and productivity.

Subscribe Free

Try These Tools

Related free tools mentioned in this article

Back to Blog