API testing practice lab
Learn API testing the only way that sticks โ by sending real requests. Fire off calls to safe public sandboxes right here, watch the status codes and responses, then work the graded challenges below. No setup, no keys.
Try it live
Uses dummyjson.com, jsonplaceholder and httpbin.org โ all safe to hammer.
Practice challenges
Work top to bottom. Each one loads into the runner โ try to predict the status code before you Send.
Beginner
- Beginner
Your first GET request
Fetch product #1 from DummyJSON and read its title and price.
Assert: Status is 200, and the body has a title and a price field.
- Beginner
Read a status code
Ask for a post that doesn't exist and see what the server says.
Assert: Status is 404 (Not Found) โ not every request succeeds, and that's the point.
- Beginner
Query parameters
Get only the posts written by userId 2.
Assert: Every item in the array has userId = 2.
Intermediate
- Intermediate
Create with POST
Send a new post with a JSON body and check what comes back.
Assert: Status is 201 (Created) and the response echoes your data with a new id.
- Intermediate
See exactly what you send
POST a small JSON body to httpbin and find it in the response.
Assert: The data you sent appears back under the response's json key, with your Content-Type header.
- Intermediate
Search & pagination
Search DummyJSON products for 'phone', 5 per page.
Assert: You get a total count, and the products array has at most 5 items (limit).
Advanced
- Advanced
Auth: get a token, then use it
First run 'Login โ get a token', copy the accessToken from the response, paste it into the runner's Bearer token box, then run 'Use the token'.
Assert: Login returns 200 with an accessToken; /auth/me returns your logged-in user.
- Advanced
Drill into nested JSON
Fetch cart #1 and reach the first product's title deep inside the response.
Assert: total is greater than 0, and products[0].title exists.
- Advanced
A full CRUD flow (in Postman)
On restful-booker, create a booking (POST), read it (GET), update it (PUT with an auth token), then delete it (DELETE). It has no CORS, so use Postman for this one.
Assert: Create returns a bookingid; the GET matches; after DELETE, the GET returns 404.
Do this one in Postman
Now do it in code
The exact same requests, in the three frameworks QA teams hire for. This is what the API kits drill โ same endpoints as the runner above, so you can compare click-by-click with line-by-line.
import { test, expect } from '@playwright/test';
test('get a product', async ({ request }) => {
const res = await request.get('https://dummyjson.com/products/1');
expect(res.status()).toBe(200);
const body = await res.json();
expect(body).toHaveProperty('title');
});test('create a post', async ({ request }) => {
const res = await request.post('https://jsonplaceholder.typicode.com/posts', {
data: { title: 'my first API test', body: 'learning by doing', userId: 1 },
});
expect(res.status()).toBe(201);
expect((await res.json()).id).toBeTruthy();
});test('filter by userId', async ({ request }) => {
const res = await request.get('https://jsonplaceholder.typicode.com/posts', {
params: { userId: 2 },
});
const posts = await res.json();
expect(posts.every((p) => p.userId === 2)).toBeTruthy();
});test('auth flow', async ({ request }) => {
const login = await request.post('https://dummyjson.com/auth/login', {
data: { username: 'emilys', password: 'emilyspass' },
});
const { accessToken } = await login.json();
const me = await request.get('https://dummyjson.com/auth/me', {
headers: { Authorization: `Bearer ${accessToken}` },
});
expect(me.status()).toBe(200);
});test('nested json', async ({ request }) => {
const res = await request.get('https://dummyjson.com/carts/1');
const cart = await res.json();
expect(cart.total).toBeGreaterThan(0);
expect(cart.products[0].title).toBeTruthy();
});Ready to go deeper?
The full path โ HTTP & Postman, RestAssured, schema validation, auth flows and CI โ is mapped in the roadmaps, with kits to practice against.