Postman API Testing
A practical reference for the Postman features you'll use across collections, environments, scripting, and CLI runs. All scripting examples use the modern pm.* API.
Collections & Requests
Creating requests
Pick the HTTP method and URL, then optionally configure headers, body, and auth.
GET https://api.example.com/users
POST https://api.example.com/users
PUT https://api.example.com/users/42
PATCH https://api.example.com/users/42
DELETE https://api.example.com/users/42Request body types
In the Body tab:
- raw + JSON / XML / Text / HTML / JavaScript
- form-data for file uploads (
multipart/form-data) - x-www-form-urlencoded for HTML form submissions
- binary for raw file payloads
- GraphQL with separate Query and Variables panes
{
"name": "Ada Lovelace",
"email": "ada@example.com",
"roles": ["admin", "editor"]
}Headers and authentication
Add headers under the Headers tab. Common entries:
Content-Type: application/json
Accept: application/json
Authorization: Bearer {{token}}
X-Request-ID: {{$guid}}The Authorization tab attaches credentials at request time without polluting the headers list — pick Bearer Token, Basic, OAuth 2.0, API Key, etc.
Organizing requests
My API Collection
├── Auth
│ ├── POST Login
│ └── POST Refresh token
├── Users
│ ├── GET List users
│ ├── GET User by ID
│ ├── POST Create user
│ └── DELETE User
└── Orders
└── ...
Folders inherit auth and pre-request scripts from their parent — define shared logic at the highest level that makes sense.
Variables & Environments
Variable scopes
| Scope | Set via | Lifetime |
|---|---|---|
| Global | pm.globals.set() | Across all collections + workspaces |
| Collection | pm.collectionVariables.set() | Within one collection |
| Environment | pm.environment.set() | Within selected environment |
| Data | CSV/JSON in Collection Runner | One iteration |
| Local | pm.variables.set() | Single request |
Resolution order (highest precedence first): Local → Data → Environment → Collection → Global.
Setting variables
pm.globals.set("api_version", "v2");
pm.collectionVariables.set("base_url", "https://api.example.com");
pm.environment.set("auth_token", responseToken);
pm.environment.unset("temp_value");
const value = pm.environment.get("auth_token");Using variables
In URLs, headers, and bodies use double curly braces:
{{base_url}}/users/{{user_id}}
Authorization: Bearer {{auth_token}}
{
"request_id": "{{$guid}}",
"timestamp": "{{$isoTimestamp}}"
}Dynamic variables
Built-in generators — refresh on every request:
{{$guid}} UUID v4
{{$timestamp}} Unix timestamp
{{$isoTimestamp}} ISO 8601 timestamp
{{$randomFirstName}} random first name
{{$randomLastName}}
{{$randomEmail}}
{{$randomPhoneNumber}}
{{$randomInt}} int 0–1000
{{$randomUrl}}
{{$randomIP}}
Pre-request Scripts
Sending a chained request
pm.sendRequest({
url: pm.collectionVariables.get("base_url") + "/auth/token",
method: "POST",
header: { "Content-Type": "application/json" },
body: {
mode: "raw",
raw: JSON.stringify({ client_id: "abc", client_secret: "xyz" })
}
}, (err, res) => {
if (err) return console.error(err);
pm.environment.set("auth_token", res.json().access_token);
});Generating signed timestamps and HMACs
const timestamp = Math.floor(Date.now() / 1000).toString();
const secret = pm.environment.get("api_secret");
const payload = pm.request.method + pm.request.url.toString() + timestamp;
const signature = CryptoJS.HmacSHA256(payload, secret).toString(CryptoJS.enc.Hex);
pm.request.headers.add({ key: "X-Timestamp", value: timestamp });
pm.request.headers.add({ key: "X-Signature", value: signature });Conditional skipping
if (!pm.environment.get("auth_token")) {
console.log("Skipping — no token set");
postman.setNextRequest(null);
}Test Scripts (pm.test)
Basic structure
pm.test("Response is 200", () => {
pm.response.to.have.status(200);
});
pm.test("Response time is under 500ms", () => {
pm.expect(pm.response.responseTime).to.be.below(500);
});Status codes
pm.response.to.have.status(200);
pm.response.to.be.success; // 2xx
pm.response.to.be.clientError; // 4xx
pm.response.to.be.serverError; // 5xx
pm.response.to.have.status("OK");Headers
pm.response.to.have.header("Content-Type");
pm.response.to.have.header("Content-Type", "application/json; charset=utf-8");
pm.expect(pm.response.headers.get("X-Rate-Limit")).to.exist;JSON body assertions
const data = pm.response.json();
pm.test("User has expected shape", () => {
pm.expect(data).to.have.property("id");
pm.expect(data.name).to.eql("Ada Lovelace");
pm.expect(data.roles).to.include("admin");
pm.expect(data.email).to.match(/@example\.com$/);
});
pm.test("Items array is non-empty", () => {
pm.expect(data.items).to.be.an("array").that.is.not.empty;
pm.expect(data.items).to.have.lengthOf.at.least(1);
});JSON schema validation
const schema = {
type: "object",
required: ["id", "name", "email"],
properties: {
id: { type: "integer" },
name: { type: "string" },
email: { type: "string", format: "email" }
}
};
pm.test("Response matches schema", () => {
pm.response.to.have.jsonSchema(schema);
});Storing values for the next request
const data = pm.response.json();
pm.collectionVariables.set("user_id", data.id);
pm.environment.set("auth_token", data.token);Failing on a condition
pm.test("Inventory is in stock", () => {
const stock = pm.response.json().stock;
if (stock <= 0) {
throw new Error(`Item out of stock: stock=${stock}`);
}
});Authentication
Postman attaches credentials from the Authorization tab automatically. Use it instead of hand-rolling headers — it surfaces in tests and Newman runs.
Bearer Token
Type: Bearer Token
Token: {{auth_token}}
Sends Authorization: Bearer {{auth_token}}.
Basic Auth
Type: Basic Auth
Username: {{username}}
Password: {{password}}
Sends Authorization: Basic <base64(user:pass)>.
OAuth 2.0
Configure under Authorization → OAuth 2.0:
- Grant type: Authorization Code / Client Credentials / Password / etc.
- Auth URL, Access Token URL
- Client ID, Client Secret
- Scope
Click Get New Access Token → token is saved and reused.
API Key
Type: API Key
Key: X-API-Key
Value: {{api_key}}
Add to: Header (or Query Params)
Digest Auth
Type: Digest Auth
Username: {{user}}
Password: {{pass}}
Realm, Nonce, Algorithm, qop (advanced)
Postman computes the digest hash automatically.
Newman CLI
Newman is the command-line runner for Postman collections.
npm install -g newman
npm install -g newman-reporter-htmlextra # nicer HTML reportRunning a collection
newman run collection.json
newman run https://api.getpostman.com/collections/<id>?apikey=...With environment
newman run collection.json -e environment.json
newman run collection.json -e env.json -g globals.jsonReporters
newman run collection.json --reporters cli,json,htmlextra
newman run collection.json \
--reporters htmlextra \
--reporter-htmlextra-export ./report.htmlData-driven runs (CSV / JSON)
newman run collection.json \
--iteration-data data.csv \
--iteration-count 10data.csv:
username,password
alice,secret1
bob,secret2
In the request use {{username}} and {{password}} — Newman injects one row per iteration.
Throttling and timeouts
newman run collection.json --delay-request 500 # 500ms between requests
newman run collection.json --timeout-request 30000
newman run collection.json --bail # stop on first failureCI/CD exit codes
Newman exits non-zero on test failures. In any CI:
newman run collection.json -e env.json \
--reporters cli,junit \
--reporter-junit-export results.xmlGitHub Actions example:
- name: API tests
run: |
newman run postman/collection.json \
-e postman/staging.json \
--reporters cli,junit \
--reporter-junit-export newman-results.xml
- uses: actions/upload-artifact@v4
with:
name: newman-results
path: newman-results.xmlCollection Runner
The Postman GUI Collection Runner mirrors Newman with a visual interface.
Data files
Pick a CSV or JSON file under Data. Variables bind by column name (CSV) or property name (JSON).
[
{ "username": "alice", "expected_status": 200 },
{ "username": "missing-user", "expected_status": 404 }
]Tests can read iteration variables directly:
pm.test(`Status matches expected for ${pm.iterationData.get("username")}`, () => {
pm.response.to.have.status(pm.iterationData.get("expected_status"));
});Workflow control
Branch through the run with setNextRequest:
if (pm.response.code === 401) {
postman.setNextRequest("Refresh token");
} else if (pm.response.code >= 500) {
postman.setNextRequest(null); // halt the run
} else {
// continue in collection order
}Run summary
After a run, expand each iteration to see request/response, console output, and assertion pass/fail. Export results as JSON for offline analysis or as a Postman Cloud share link.