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.
Comparing tools for your pipeline? See the best mock API tools for testing, by use case, including which work offline in CI.