home / writing / api-testing-with-postman

API Testing with Postman

Postman turns ad hoc API calls into repeatable, shareable tests. This guide covers requests, collections, variables, assertions, auth and running everything automatically from the command line or CI.

HTTP requests in brief

Postman is a client for building, sending and testing API requests. Every call is an HTTP exchange: the client sends a request, the server returns a response. A request has these parts:

  • Method such as GET, POST, PUT, PATCH or DELETE. GET reads data and can be repeated safely; POST creates data and normally carries a body.
  • URL, which may include query parameters (?page=2&limit=10, added with the Params tab, where unticking a row disables it) and path variables (/users/:id).
  • Headers, for example Content-Type: application/json to declare the body format, or Accept for the response format.
  • Body: raw JSON, form data, URL-encoded fields or binary.

The response gives a status code, headers, body and timing. Remember the groups: 2xx success, 3xx redirect, 4xx client error, 5xx server error.

A handy shortcut: copy a request from your browser's network tab as cURL, then use Import and paste it to get an editable Postman request.

Collections and organising requests

A collection groups related requests, optionally in folders, such as one folder per resource (users, orders, payments). Collections can carry their own authorization, variables, pre-request scripts and tests, which every request inside inherits unless it overrides them. A well-structured collection doubles as living documentation and as a test suite.

Add descriptions and saved example responses to requests. Examples document the expected shape and can power mock servers.

Variables and environments

Variables avoid hard-coded values. Reference them with double braces, for example {{baseUrl}}/users/{{userId}}. They exist at several scopes, resolved from narrowest to widest:

ScopeUse for
LocalTemporary values in a single run
DataValues from a CSV or JSON file in a run
EnvironmentPer-target settings (dev, staging, prod)
CollectionValues shared by one collection
GlobalValues shared across the workspace

An environment is a named set of variables. Define baseUrl in each one, then switch the active environment to run the same collection against different servers.

Each variable has an initial value and a current value. The initial value is what gets shared when you sync or export; the current value stays local to you. Keep real credentials only in current values (or mark them as secret type) so they are never shared accidentally, and put placeholders in the initial value.

Scripts can read and write variables, which is how you chain requests:

// in the post-response script of a login request
const body = pm.response.json();
pm.environment.set("accessToken", body.token);

Authentication

The Authorization tab supports the common schemes, and setting it once at collection or folder level avoids repetition:

  • API key, sent as a header or query parameter.
  • Bearer token, sent as Authorization: Bearer <token>.
  • Basic auth, a base64 encoded username and password; use only over HTTPS.
  • OAuth 2.0, where Postman can run the flow and store the resulting token.

Store tokens in variables such as {{accessToken}} and refer to them in the auth settings. A pre-request script can fetch a fresh token when the old one has expired.

Writing tests and assertions

Post-response scripts (the Tests tab in older Postman versions) run after the response arrives. Each pm.test takes a name and a function containing assertions; a failing assertion marks that test as failed without stopping the others.

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

pm.test("Responds quickly", function () {
  pm.expect(pm.response.responseTime).to.be.below(800);
});

pm.test("Has expected JSON shape", function () {
  const data = pm.response.json();
  pm.expect(data).to.have.property("id");
  pm.expect(data.items).to.be.an("array").that.is.not.empty;
});

pm.test("Content-Type is JSON", function () {
  pm.response.to.have.header("Content-Type");
  pm.expect(pm.response.headers.get("Content-Type")).to.include("application/json");
});

Good tests check the status code, the headers, key fields of the body and response time, and also the failure paths: invalid input should return the right 4xx code, a missing token should return 401. Pre-request scripts run before sending, and are the place for generating timestamps, random data or signatures.

Automated runs

  • Collection Runner: executes a collection (or folder) in order, with an environment, iteration count and optional data file. Use pm.execution.setNextRequest sparingly to change order.
  • Monitors: hosted scheduled runs that check an API at intervals. Remember they run from Postman's infrastructure, so they can only reach publicly accessible endpoints.
  • Newman: the command-line runner, installed with npm or run from a container, built for CI.
npm install -g newman
newman run my-collection.json \
  -e staging.environment.json \
  --reporters cli,junit \
  --reporter-junit-export results.xml

Newman exits non-zero when assertions fail, so any CI system (Jenkins, GitHub Actions, GitLab CI) can gate a pipeline on it. The JUnit report plugs into the CI test-result views. Export the collection and environment files into the repository, with secrets supplied at runtime through --env-var or CI secrets rather than files.

Collaboration and good habits

  • Workspaces (personal, team, public) hold collections and environments. Share through team workspaces so everyone uses the same source of truth.
  • Use forks and pull requests for collections, comments for review, and version history to see who changed what.
  • Publish documentation generated from your collection for consumers of the API.
  • Never put production secrets in shared initial values, exported files, or public workspaces. If a token leaks, revoke it.
  • Keep names consistent, group by resource, and give each request at least a status-code test.
  • Run the same collection locally, in CI and on a monitor to catch environment-specific problems early.

Treat API tests as code: keep them in version control, review changes, run them on every merge, and fail the build when they fail.