Sends HTTP requests to REST and GraphQL API endpoints, validates response codes, headers, and payloads, measures response times against SLA thresholds, generates structured test suites, and produces test reports covering functional, security, and performance scenarios. Use when the user asks about testing APIs, validating endpoints, checking HTTP status codes or response bodies, load testing, stress testing, debugging REST or GraphQL services, testing authentication flows, checking rate limiting, detecting SQL injection or OWASP API vulnerabilities, or integrating API tests into CI/CD pipelines.
93
92%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
Applies to REST, GraphQL, and microservice APIs across functional validation, performance testing, security assessment, and CI/CD integration.
Build automated test suites covering all three layers. See EXAMPLES.md for the full reference test suite.
Functional + Security test pattern (Playwright/fetch):
import { test, expect } from '@playwright/test';
import { performance } from 'perf_hooks';
const BASE_URL = process.env.API_BASE_URL;
let authToken: string;
test.beforeAll(async () => {
const res = await fetch(`${BASE_URL}/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: 'test@example.com', password: 'secure_password' })
});
authToken = (await res.json()).token;
});
// --- Functional ---
test('POST /users — valid data returns 201 with safe payload', async () => {
const res = await fetch(`${BASE_URL}/users`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${authToken}` },
body: JSON.stringify({ name: 'Test User', email: 'new@example.com', role: 'user' })
});
expect(res.status).toBe(201);
const user = await res.json();
expect(user.email).toBe('new@example.com');
expect(user.password).toBeUndefined(); // passwords must never be returned
});
test('POST /users — invalid input returns 400 with error details', async () => {
const res = await fetch(`${BASE_URL}/users`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${authToken}` },
body: JSON.stringify({ name: '', email: 'invalid-email', role: 'bad_role' })
});
expect(res.status).toBe(400);
expect((await res.json()).errors).toBeDefined();
});
// --- Security ---
test('GET /users — unauthenticated returns 401', async () => {
expect((await fetch(`${BASE_URL}/users`)).status).toBe(401);
});
test('GET /users — SQL injection in query param does not cause 500', async () => {
const res = await fetch(`${BASE_URL}/users?search=${encodeURIComponent("'; DROP TABLE users; --")}`, {
headers: { 'Authorization': `Bearer ${authToken}` }
});
expect(res.status).not.toBe(500);
});
test('GET /users — rate limiting triggers 429 after burst', async () => {
const responses = await Promise.all(
Array(100).fill(null).map(() =>
fetch(`${BASE_URL}/users`, { headers: { 'Authorization': `Bearer ${authToken}` } })
)
);
expect(responses.some(r => r.status === 429)).toBe(true);
});
// --- Performance ---
test('GET /users — P95 response time under 200ms', async () => {
const start = performance.now();
const res = await fetch(`${BASE_URL}/users`, { headers: { 'Authorization': `Bearer ${authToken}` } });
expect(res.status).toBe(200);
expect(performance.now() - start).toBeLessThan(200);
});k6 load test pattern (save as load-test.js, run with k6 run load-test.js):
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '1m', target: 50 }, // ramp to normal load
{ duration: '3m', target: 500 }, // 10× normal load
{ duration: '1m', target: 0 }, // ramp down
],
thresholds: {
http_req_duration: ['p(95)<200'], // P95 under 200ms
http_req_failed: ['rate<0.001'], // error rate under 0.1%
},
};
export default function () {
const res = http.get(`${__ENV.API_BASE_URL}/users`, {
headers: { Authorization: `Bearer ${__ENV.AUTH_TOKEN}` },
});
check(res, { 'status 200': r => r.status === 200 });
sleep(1);
}Execute in this order with explicit gates — do not proceed if a gate fails:
zap-cli quick-scan --self-contained --start-options '-config api.disablekey=true' $API_BASE_URL) → triage findings → fix critical/high issues → re-scan before continuingUse TEMPLATES.md for the full test report template. Minimum required fields per report:
# [API Name] Test Report — [Date]
## Coverage
- Functional: [N endpoints tested / N total] — [pass/fail counts]
- Security: [OWASP Top 10 items tested] — [critical/high/medium findings]
- Performance: [P95 response time] | [max throughput rps] | [error rate under load]
## Findings
| Severity | Issue | Endpoint | Recommended Fix |
|----------|-------|----------|-----------------|
| Critical | ... | ... | ... |
## Quality Gate Status
| Gate | Status | Notes |
|-------------|-------------|-------|
| Functional | PASS / FAIL | ... |
| Security | PASS / FAIL | ... |
| Performance | PASS / FAIL | ... |
## Release Recommendation
**Go / No-Go** — [Reason with supporting data]For every API under test, verify:
See ADVANCED.md for:
010799b
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.