ReqRes Blog

API Testing in CI/CD - Skip the Mock Server, Use a Real API

Feb 6, 2026 3 min read
ci-cd automation github-actions qa api-testing devops
Next step

Want to try this in 2 minutes? Start a project or open the Notes example.

Adding API tests to CI shouldn't mean maintaining mock servers, Docker containers, or fragile test fixtures. ReqRes is a free hosted API that's always available, returns stable payloads, and supports auth - so your CI tests run reliably without infrastructure overhead. Here's how to wire it up.

Why do CI API tests need a stable external API?

Common approaches create friction:

  • Local mock servers add Docker/startup complexity, slow down pipelines, and diverge from real API behaviour
  • Shared staging environments are flaky, slow, and block other teams
  • Inline mocking (Jest mocks, page.route) only tests your mocks, not your actual integration

What you want: an API that's always up, returns predictable data, supports auth, and doesn't require any setup in CI.

ReqRes gives you exactly that - a real REST API you can hit from any CI runner without provisioning anything.

How do I test APIs in GitHub Actions?

Here are three examples using common tools.

With cURL (simplest)

name: API Smoke Tests
on: [push]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Test API is reachable
        run: |
          STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
            -H "x-api-key: ${{ secrets.REQRES_API_KEY }}" \
            https://reqres.in/api/users?page=2)
          if [ "$STATUS" != "200" ]; then exit 1; fi

      - name: Test user creation
        run: |
          RESPONSE=$(curl -s -X POST \
            -H "x-api-key: ${{ secrets.REQRES_API_KEY }}" \
            -H "Content-Type: application/json" \
            -d '{"name":"CI Test","job":"Pipeline"}' \
            https://reqres.in/api/users)
          echo "$RESPONSE" | jq -e '.id' > /dev/null

With Newman (Postman CLI)

- name: Run Postman collection
  run: |
    npm install -g newman
    newman run tests/api-collection.json \
      --env-var "api_key=${{ secrets.REQRES_API_KEY }}" \
      --env-var "base_url=https://reqres.in"

With Playwright

- name: Run Playwright API tests
  run: npx playwright test tests/api/
  env:
    REQRES_API_KEY: ${{ secrets.REQRES_API_KEY }}

Your Playwright test file:

import { test, expect } from '@playwright/test'

test('GET /api/users returns 200', async ({ request }) => {
    const response = await request.get('https://reqres.in/api/users?page=2', {
        headers: {
            'x-api-key': process.env.REQRES_API_KEY!,
        },
    })
    expect(response.status()).toBe(200)
    const data = await response.json()
    expect(data.data).toBeInstanceOf(Array)
})

test('POST /api/users returns 201', async ({ request }) => {
    const response = await request.post('https://reqres.in/api/users', {
        headers: {
            'x-api-key': process.env.REQRES_API_KEY!,
            'Content-Type': 'application/json',
        },
        data: { name: 'CI Test', job: 'Pipeline' },
    })
    expect(response.status()).toBe(201)
    const body = await response.json()
    expect(body.id).toBeDefined()
})

How do I test APIs in GitLab CI?

The same patterns work in .gitlab-ci.yml:

With cURL

stages:
  - test

api-smoke-test:
  stage: test
  image: alpine:latest
  before_script:
    - apk add --no-cache curl jq
  script:
    - |
      STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
        -H "x-api-key: $REQRES_API_KEY" \
        https://reqres.in/api/users?page=2)
      if [ "$STATUS" != "200" ]; then exit 1; fi
    - |
      RESPONSE=$(curl -s -X POST \
        -H "x-api-key: $REQRES_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{"name":"CI Test","job":"Pipeline"}' \
        https://reqres.in/api/users)
      echo "$RESPONSE" | jq -e '.id' > /dev/null

With Newman

api-collection-test:
  stage: test
  image: node:18
  script:
    - npm install -g newman
    - newman run tests/api-collection.json
        --env-var "api_key=$REQRES_API_KEY"
        --env-var "base_url=https://reqres.in"

With Playwright

playwright-api-test:
  stage: test
  image: mcr.microsoft.com/playwright:v1.40.0-jammy
  script:
    - npm ci
    - npx playwright test tests/api/
  variables:
    REQRES_API_KEY: $REQRES_API_KEY

How do I handle API keys securely in CI?

Store your ReqRes API key as a CI secret - never commit it to code.

GitHub Actions:
Settings → Secrets and variables → Actions → New repository secret → REQRES_API_KEY

GitLab CI:
Settings → CI/CD → Variables → Add variable → REQRES_API_KEY (masked)

Jenkins:
Manage Jenkins → Credentials → Add Credentials → Secret text → REQRES_API_KEY

Reference in your pipeline config via environment variables:

# GitHub Actions
env:
  REQRES_API_KEY: ${{ secrets.REQRES_API_KEY }}

# GitLab CI
variables:
  REQRES_API_KEY: $REQRES_API_KEY

Get your API key at app.reqres.in/api-keys.

What makes a good CI API test suite?

Keep it practical:

Test the happy path first:

  • GET returns 200 with expected data shape
  • POST returns 201 with an ID
  • Auth endpoints return tokens

Test auth failures:

  • Missing x-api-key returns 401
  • Invalid key returns 403

Test edge cases:

  • Invalid payloads return 400
  • Missing resources return 404

Keep tests fast:

  • 10-20 targeted assertions beat 200 slow integration tests
  • Focus on critical paths, not exhaustive coverage

Debug with logs:

  • ReqRes logs every request with headers and response times
  • When tests fail, check logs to see exactly what was received

For detailed endpoint information, see the API documentation.

What about rate limits in CI?

Practical guidance:

  • ReqRes free tier handles CI test suites comfortably for most teams
  • Parallel pipelines across many repos may need higher limits - consider upgrading
  • Add retry logic with exponential backoff (3 retries, starting at 1 second)
  • Don't over-test - run smoke tests on push, full suite on PR/merge

Example retry wrapper for cURL:

retry_curl() {
  local max_attempts=3
  local delay=1
  local attempt=1

  while [ $attempt -le $max_attempts ]; do
    if curl "$@"; then
      return 0
    fi
    echo "Attempt $attempt failed. Retrying in ${delay}s..."
    sleep $delay
    delay=$((delay * 2))
    attempt=$((attempt + 1))
  done
  return 1
}

Get started

Get a free API key and add API tests to your pipeline in under 10 minutes. No mock servers, no Docker, no flaky staging environments.

Comparing tools for your pipeline? See the best mock API tools for testing, by use case, including which work offline in CI.

Ready to ship? Continue in the app.