Skip to main content

QA Bash Learning

API Playground.

A real sample REST + GraphQL API to practice testing against — CRUD, a state machine, two auth styles, and endpoints built to fail on purpose.

Users

GET/api/tools/sample-api/users
Try in API Workbench

List all sample users.

Responses

[
  { "id": 1, "name": "Alice Johnson", "email": "alice@example.com", "role": "admin", "active": true, "age": 32 },
  { "id": 2, "name": "Bob Smith", "email": "bob@example.com", "role": "user", "active": false, "age": 28 }
]
curl https://www.qabash.com/api/tools/sample-api/users
GET/api/tools/sample-api/users/:id
Try in API Workbench

Get a single user by id.

Params

idpath · requiredNumeric user id (try 1, 2, 3, or a bad id for the 404 case)

Responses

{ "id": 1, "name": "Alice Johnson", "email": "alice@example.com", "role": "admin", "active": true, "age": 32 }
curl https://www.qabash.com/api/tools/sample-api/users/1
POST/api/tools/sample-api/users
Try in API Workbench

Create a user. Not persisted — echoes back what a real create would return, so every visitor gets a clean demo.

Request body

{ "name": "Dana Lee", "email": "dana@example.com" }

Responses

{ "id": 47, "name": "Dana Lee", "email": "dana@example.com", "role": "user", "active": true, "age": null }
curl -X POST https://www.qabash.com/api/tools/sample-api/users -H "Content-Type: application/json" -d '{"name":"Dana Lee","email":"dana@example.com"}'
PATCH/api/tools/sample-api/users/:id
Try in API Workbench

Update a user. Not persisted — merges your patch onto the seed record and echoes it back.

Params

idpath · requiredNumeric user id

Request body

{ "active": false }

Responses

{ "id": 1, "name": "Alice Johnson", "email": "alice@example.com", "role": "admin", "active": false, "age": 32 }
curl -X PATCH https://www.qabash.com/api/tools/sample-api/users/1 -H "Content-Type: application/json" -d '{"active":false}'
DELETE/api/tools/sample-api/users/:id
Try in API Workbench

Delete a user. Not persisted.

Params

idpath · requiredNumeric user id

Responses

{ "deleted": true, "id": 1 }
curl -X DELETE https://www.qabash.com/api/tools/sample-api/users/1

Test Cases

GET/api/tools/sample-api/testcases
Try in API Workbench

List sample test cases. Supports an optional ?status= filter.

Params

statusquerydraft | active | archived

Responses

[
  { "id": 1, "title": "Login with valid credentials", "status": "active", "priority": "P1", "owner": "Alice Johnson" },
  { "id": 2, "title": "Login with locked account", "status": "draft", "priority": "P2", "owner": "Bob Smith" }
]
curl "https://www.qabash.com/api/tools/sample-api/testcases?status=active"
PATCH/api/tools/sample-api/testcases/:id
Try in API Workbench

Update a test case. Status only moves forward: draft → active → archived. Requesting any other transition returns a 409 with the allowed next states — most practice APIs only demo happy-path CRUD; this one teaches conflict handling too.

Params

idpath · requiredNumeric test case id — try 3 (already archived) to trigger the 409

Request body

{ "status": "active" }

Responses

{ "id": 2, "title": "Login with locked account", "status": "active", "priority": "P2", "owner": "Bob Smith" }

Allowed transitions: draft → active, active → archived. archived has no further transitions.

curl -X PATCH https://www.qabash.com/api/tools/sample-api/testcases/2 -H "Content-Type: application/json" -d '{"status":"active"}'

Auth — Bearer Token

GET/api/tools/sample-api/secure
Try in API Workbench

Requires an exact bearer token. Missing or wrong token → 401.

Params

Authorizationheader · requiredBearer demo-token-123

Responses

{ "message": "Authenticated.", "scope": "sample-api:read" }
curl https://www.qabash.com/api/tools/sample-api/secure -H "Authorization: Bearer demo-token-123"

Auth — API Key

GET/api/tools/sample-api/auth/api-key
Try in API Workbench

Requires an x-api-key header. Deliberately distinguishes missing (401) from present-but-wrong (403) — a nuance most practice APIs collapse into one status code.

Params

x-api-keyheader · requireddemo-key-abc123

Responses

{ "message": "Authenticated.", "scope": "sample-api:read" }
curl https://www.qabash.com/api/tools/sample-api/auth/api-key -H "x-api-key: demo-key-abc123"

Resilience — Flaky

GET/api/tools/sample-api/flaky
Try in API Workbench

Fails with a 500 roughly 30% of the time, at random — for practicing retry logic.

Responses

{ "message": "Success." }
curl https://www.qabash.com/api/tools/sample-api/flaky

Resilience — Slow

GET/api/tools/sample-api/slow
Try in API Workbench

Responds after an artificial delay — for practicing timeout handling. Delay is clamped to 8000ms server-side no matter what you request.

Params

delayqueryMilliseconds to wait, 0-8000 (default 1000)

Responses

{ "message": "Responded after delay.", "delayMs": 3000 }
curl "https://www.qabash.com/api/tools/sample-api/slow?delay=3000"

Resilience — Rate Limited

GET/api/tools/sample-api/rate-limited
Try in API Workbench

Allows 5 requests per 10 seconds per IP, then 429s with a Retry-After header — for practicing backoff logic.

Responses

{ "message": "OK", "remaining": 3, "limit": 5, "windowMs": 10000 }

Try the live demo below — it fires 6 requests back to back so you can watch the 429 happen.

curl -i https://www.qabash.com/api/tools/sample-api/rate-limited

Live demo

Fires 6 requests back to back — watch request 6 get a 429.

GraphQL

POST/api/tools/sample-api/graphql
Try in API Workbench

A minimal GraphQL endpoint resolving "users" and "user(id: ...)" against the same seed data as the REST Users resource.

Request body

{ "query": "{ users { id name email } }" }

Responses

{ "data": { "users": [ { "id": 1, "name": "Alice Johnson", "email": "alice@example.com" } ] } }
curl -X POST https://www.qabash.com/api/tools/sample-api/graphql -H "Content-Type: application/json" -d '{"query":"{ users { id name email } }"}'

QA Bash's API Playground is a real, live sample REST + GraphQL backend for practicing API testing — full CRUD, a draft→active→archived state machine that 409s on invalid transitions, bearer-token and API-key auth (with the 401-vs-403 distinction most practice APIs skip), plus dedicated flaky, slow, and rate-limited endpoints for retry, timeout, and backoff logic. Free, no signup required.

Features

Built for testing, not just CRUD.

A real state machine

Test Cases move draft → active → archived. Request an invalid transition and get a 409 with the allowed next states — not just happy-path CRUD.

401 vs 403, done right

Bearer-token and API-key auth endpoints deliberately distinguish 'missing credential' (401) from 'wrong credential' (403) — a nuance most practice APIs collapse into one code.

A flaky endpoint

Fails ~30% of the time at random. Point your retry logic at it and watch it actually get exercised.

A slow endpoint

Configurable artificial delay for practicing timeout handling — clamped server-side so it can't be abused.

A rate-limited endpoint

5 requests per 10 seconds, then a real 429 with Retry-After. There's a live demo button that fires 6 requests so you can watch it happen.

REST + GraphQL

The same seed data is reachable via REST and a minimal GraphQL resolver, so you can practice both query styles against one dataset.

FAQ

Frequently asked questions.

Is this a real backend I can hit from my automated tests?

Yes — every endpoint here is a real, live HTTP endpoint. Point curl, Postman, RestAssured, requests, or QA Bash's own API Workbench at it directly.

Does anything I create actually get saved?

No. POST/PUT/PATCH/DELETE all echo back what a real mutation would return, but nothing persists — so your requests never collide with anyone else hitting the same shared demo.

Why does the Test Cases endpoint reject some PATCH requests?

Status only moves forward: draft → active → archived. Requesting any other transition (like archived → draft) returns a 409 Conflict with the allowed next states in the body — deliberately, to give you a real conflict-handling case to test against.

What's the difference between the two auth endpoints?

/secure requires a bearer token; /auth/api-key requires an x-api-key header. Both are here so you can practice the two most common auth shapes, and /auth/api-key specifically demonstrates 401 (missing) vs 403 (wrong) as separate cases.

How is the rate limit tracked?

By IP address, in-memory, on the server — 5 requests per rolling 10-second window. It resets whenever the app redeploys, which is expected for a practice endpoint.

Can I use these endpoints in a CI pipeline?

They're a shared public demo on ordinary hosting, not a guaranteed-uptime SLA service — fine for learning and one-off scripts, not recommended as a dependency in a production test suite.