Skip to main content
May 10, 2026api performance testing checklist

By Performate

API Performance Testing Checklist: 12 Things Teams Forget

Twelve concrete checks before and during API load tests—environment realism, auth, data safety, metrics, and gates—grounded in k6 and release practice.

Your staging run hit 500 concurrent users and checkout stayed green—so the release shipped. Two days later production p99 doubled because the test never exercised OAuth token refresh, shared sandbox rows collided under write load, and nobody tagged requests by route. Serious API load testing is less about "how many VUs" and more about whether the question, environment, and metrics match production risk.

This checklist captures twelve checks teams forget before they scale traffic—especially when stakeholders will read the outcome. Pair it with stress vs load vs spike for the right traffic shape and k6 thresholds examples when you define pass/fail.

Why checklists fail without executable structure

Most performance postmortems trace back to the same gaps:

  • Unnamed decisions: runs happen because "we should load test," not because a release gate needs a specific answer.
  • Environment lies: staging without realistic data volume, cache warmth, or network path produces charts nobody should trust.
  • Untagged metrics: aggregate http_req_duration hides which endpoint regressed.
  • Missing abort ownership: saturation spills outside the sandbox and surprises security or platform on-call.

A checklist only helps when it maps to k6 scenarios, thresholds, and archived parameters—not slide decks. The sections below turn each item into something you can verify in script and summary output.

Practical k6 implementation: checklist encoded in options

The example script embeds checklist items as tags, thresholds, and setup hooks so pass/fail is objective—not a meeting opinion.

Example script (illustrative—not a production-ready test). Adapt URLs, auth, datasets, and thresholds to your environment.

What this example demonstrates:

  • Decision tag: decision:release-gate-checkout documents why the run exists.
  • Route-scoped thresholds: checkout gets tighter SLOs than a health probe.
  • Setup isolation: setup() fetches a dedicated test tenant token instead of reusing shared credentials.
  • Smoke-first duration: two-minute ramp before the main stage—item 6 on the checklist.
import http from 'k6/http';
import { check, sleep } from 'k6';

const BASE = __ENV.API_BASE || 'https://staging.example.com';
const DECISION = __ENV.TEST_DECISION || 'release-gate-checkout';

export function setup() {
  // Checklist #3: freeze credentials — dedicated tenant per run
  const authRes = http.post(`${BASE}/oauth/token`, {
    grant_type: 'client_credentials',
    client_id: __ENV.CLIENT_ID,
    client_secret: __ENV.CLIENT_SECRET,
    scope: 'checkout:write',
  });
  check(authRes, { 'token issued': (r) => r.status === 200 });
  return { token: authRes.json('access_token') };
}

export const options = {
  tags: { decision: DECISION, env: __ENV.ENV_NAME || 'staging' },
  scenarios: {
    smoke_auth: {
      executor: 'constant-vus',
      vus: 5,
      duration: '2m',
      tags: { phase: 'smoke', route: 'checkout' },
      exec: 'checkout',
    },
    load_checkout: {
      executor: 'ramping-vus',
      startVUs: 0,
      stages: [
        { duration: '3m', target: 40 },
        { duration: '10m', target: 40 },
        { duration: '2m', target: 0 },
      ],
      tags: { phase: 'load', route: 'checkout' },
      exec: 'checkout',
      startTime: '2m',
    },
  },
  thresholds: {
    'http_req_duration{route:checkout}': ['p(95)<800', 'p(99)<1200'],
    'http_req_failed{route:checkout}': ['rate<0.01'],
    'checks{phase:smoke}': ['rate>0.99'],
  },
};

export function checkout(data) {
  const idempotencyKey = `load-${__VU}-${__ITER}-${Date.now()}`;
  const body = JSON.stringify({ sku: 'SKU-100', qty: 1 });

  const res = http.post(`${BASE}/v2/checkout`, body, {
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${data.token}`,
      'Idempotency-Key': idempotencyKey,
    },
    tags: { route: 'checkout', decision: DECISION },
  });

  check(res, {
    'checkout 2xx': (r) => r.status >= 200 && r.status < 300,
    'no timeout': (r) => r.status !== 0,
  });
  sleep(0.5);
}

Pro tip (example command): archive parameters with the run for checklist item 11.

TEST_DECISION=release-gate-checkout ENV_NAME=staging-v4 k6 run checkout-checklist.js --summary-export=run-evidence.json

The 12 checks: decision table

#CheckPass signalCommon miss
1Name the decision the run supportsOne-sentence gate documented in tags or README"See how it handles load"
2Match environment fidelity to the questionMissing fidelity listed in run notesTreating staging charts as prod forecasts
3Freeze URLs, tenants, credentialsEnv vars recorded; no mystery sandboxesRotating keys mid-run
4Isolate or reset test dataIdempotency keys or dedicated prefixesShared rows → flaky "regressions"
5Tag requests for post-run diagnosisPer-route http_req_duration in summaryOne aggregate latency line
6Smoke before marathonShort scenario passes checks firstDebugging script bugs at scale
7Tie thresholds to product riskTighter SLO on checkout vs adminVanity hero numbers
8Separate functional from load proofContract tests in CI; load answers concurrencyGreen functional = ship
9Watch errors by typeTimeouts vs 5xx vs business soft-fails splitSingle http_req_failed blob
10Correlate server-side when allowedAPM window matches test durationOptimizing scripts not systems
11Capture scenario params with reportVU model, duration, git SHA archivedChart with no context
12Assign abort ownerNamed contact + comms path to on-callSurprise saturation

Use strict thresholds on revenue paths (items 7–9). Use lighter gates on internal admin unless the decision explicitly covers those routes.

Use smoke-first staging (items 5–6) before any marathon soak or stress campaign (common mistakes).

Use correlation (item 10) with API bottleneck analysis when APM exists for the same window.

Pre-run checklist (copy before you scale)

Before you start the main scenario:

  • Decision sentence written: "After this test we will approve/reject ______ because we measured ______."
  • Environment gaps documented (data volume, cache warmth, network path).
  • Base URL, OAuth flow, and tenant per VU recorded in env file—not chat.
  • Test data isolation plan confirmed with platform (prefixes, resets, idempotency).
  • Tags defined for every route under test (route:checkout, etc.).
  • Smoke scenario scheduled with strict check thresholds.
  • k6 thresholds encode product risk, not best-day weekend numbers.
  • Abort owner named with authority to stop run and notify on-call.
  • Server-side dashboards open for the same time window (if policy allows).
  • Export path chosen for summary JSON + git/build identifiers.

Google's SRE material frames aligned incentives between SLIs and real user pain—your environment contract and checklist should reflect that (alerting on SLOs).

How Performate helps you execute this checklist

Boilerplate kills compliance with items 1–12. Below is a concrete workflow example for the checkout release gate this article discusses.

Example: run the checklist on a Postman collection without losing tags or evidence

  1. Import the collection that product already trusts for functional tests. Problem solved: checklist item 3—URLs and auth shapes stay aligned with daily QA.
  2. Add tags per request in the scenario panel: route:checkout, decision:release-gate-checkout. Problem solved: item 5—summaries slice latency by endpoint automatically.
  3. Configure smoke then load as two scenarios—2 minutes at 5 VUs, then ramp to 40. Problem solved: item 6 without hand-editing executor blocks.
  4. Set thresholds in the UI matching negotiated SLO (p95, error rate). Problem solved: item 7—objective pass/fail visible before export.
  5. Run and open the integrated report; split errors by type (timeouts vs 5xx). Problem solved: items 9 and 11 in one view.
  6. Export k6 script + summary for CI and attach git SHA from your release branch. Problem solved: evidence survives the meeting; reruns compare apples to apples.

Starting from Postman? Follow Postman to k6 step-by-step first so imports do not skip critical variables.

That workflow maps to the cta in this post: execute the checklist with repeatable scenarios, reports, and exports in one desktop workflow.

Closing takeaway

A load test without a decision, tags, and archived parameters is a demo—not evidence. Walk the twelve checks before you scale; encode the important ones in k6 options so pass/fail survives the release meeting.

Run smoke on your next candidate build this week—and confirm checkout tags, thresholds, and abort owner are named before anyone asks for "just five more minutes at 2× traffic."

Try Performate free | Book a demo | k6 scenarios documentation

Ready to optimize your API performance?

Download Performate to execute this checklist with repeatable scenarios, reports, and exports.

← Back to all posts