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-keyreturns 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.