Blog
4 min read

How to Test an API: Tools and a Simple Checklist for Beginners

Testing an API means sending requests and checking the responses — by hand with curl, Postman, Bruno or your editor, and automatically with tests that run on every change. What to check (status codes, body, auth, bad input), example tests, and a checklist.

An API is a set of URLs your app (or other apps) can call. Testing it means sending requests and checking that the responses are what they should be — including when something goes wrong. (What is an API?)

There are two kinds of testing, and you want both.

1. Manual testing: poke it and see

Good for exploring, debugging, and checking a new endpoint works at all.

curl (terminal)

curl -i http://localhost:3000/api/products
curl -i -X POST http://localhost:3000/api/products \
  -H "Content-Type: application/json" \
  -d '{"name": "Lamp", "price": 39}'

-i shows the status code and headers as well as the body. (What is curl?)

A graphical API client

  • Postman — the best-known; collections, environments, sharing.
  • Bruno — open source, stores requests as files in your repo, no account needed.
  • Insomnia, Hoppscotch (in the browser).
  • REST Client extension for VS Code — write requests in a .http file:
POST http://localhost:3000/api/products
Content-Type: application/json

{ "name": "Lamp", "price": 39 }

Your browser

GET requests can be opened directly in a tab, and DevTools' Network tab shows every request your frontend makes. (Browser developer tools)

What to check

For each endpoint, test more than the happy path:

Check Example
Status code 200/201 on success, not 200 with an error message inside (status codes)
Response body Right fields, right types, nothing sensitive (no password hashes!)
Bad input Missing fields, wrong types, huge values → 400 with a clear message
No login Protected endpoint without a token → 401
Someone else's data User A requests User B's order → 403 or 404 (IDOR)
Not found /api/products/999999 → 404
Duplicates Submitting the same thing twice behaves sensibly
Speed Responds in a reasonable time with realistic data

The "someone else's data" check is the one most often skipped — and the one that matters most for security. (401 vs 403)

2. Automated testing: let the computer check every time

Manual tests catch problems once. Automated tests catch them every time you change something — including when an AI tool changes something. (Unit vs integration vs E2E tests)

An example with Vitest and Supertest for an Express API:

import request from 'supertest'
import { app } from '../src/app'

describe('POST /api/products', () => {
  it('creates a product', async () => {
    const res = await request(app)
      .post('/api/products')
      .set('Authorization', `Bearer ${adminToken}`)
      .send({ name: 'Lamp', price: 39 })
    expect(res.status).toBe(201)
    expect(res.body.name).toBe('Lamp')
  })

  it('rejects a missing price', async () => {
    const res = await request(app)
      .post('/api/products')
      .set('Authorization', `Bearer ${adminToken}`)
      .send({ name: 'Lamp' })
    expect(res.status).toBe(400)
  })

  it('requires login', async () => {
    const res = await request(app).post('/api/products').send({ name: 'Lamp', price: 39 })
    expect(res.status).toBe(401)
  })
})

Run them on every push with CI. (Set up CI with GitHub Actions)

AI coding tools are good at writing these — ask for tests that cover bad input and authorisation, not just the happy path, and check they fail when they should. (Getting AI to write tests that catch bugs)

Testing against a real database

Tests are most trustworthy when they use a real (test) database rather than mocks — that's where many bugs live. Use a separate test database that's reset between runs, never your production one.

Load testing

Once it works, check how it behaves under many requests at once. (Load testing your app)

A beginner's checklist

  1. Every endpoint returns the right status codes.
  2. Bad input gets a 400, not a crash (500).
  3. Protected endpoints return 401 without login.
  4. Users can't read or change other users' data.
  5. Responses don't leak sensitive fields.
  6. There's at least one automated test per endpoint, running in CI.

EasySpawn runs your API with a separate development database next to production, so you and Claude Code can test freely without touching real data. See how it works or join the waitlist.

Related: What Is curl? · How to Test Your App Before Launch · Designing a REST API · Unit vs Integration vs E2E Tests

Keep reading