HTTP API
Complete HTTP client API for load testing
HTTP Methods
http.get(url, [options])Performs a GET request
http.post(url, body, [options])Performs a POST request
http.put(url, body, [options])Performs a PUT request
http.patch(url, body, [options])Performs a PATCH request
http.del(url, [options])Performs a DELETE request
http.head(url, [options])Performs a HEAD request
http.options(url, [options])Performs an OPTIONS request
http.request({method, url, body, headers, ...})Generic request builder
http.batch(requests, [onProgress])Execute multiple requests in parallel, optional progress callback
http.graphql(url, query, [variables], [options])Send GraphQL query (convenience wrapper for POST with JSON body)
Request Options
headers: Object - Custom HTTP headers
name: String - Custom metric tag for aggregating dynamic URLs
tags: Object - Custom tags for filtering metrics
timeout: String - Request timeout ("10s", "500ms"). Default: "60s"
retry: Number - Retry attempts on failure (default: 0)
retryDelay: Number - Initial retry delay in ms (default: 100, doubles each retry)
retryOn: Function - Custom retry predicate (res) => boolean
retryDelayFn: Function - Custom delay calculator (count) => ms
followRedirects: Boolean - Whether to follow redirects (default: true)
maxRedirects: Number - Maximum redirects to follow (default: 5)
# Retry with Exponential Backoff
const res = http.request({
method: 'GET',
url: 'https://api.example.com/flaky-endpoint',
retry: 3, // Retry up to 3 times
retryDelay: 200 // Start with 200ms, then 400ms, 800ms
});
// Custom retry conditions - retry on rate limit or server errors
const res = http.request({
method: 'GET',
url: 'https://api.example.com/data',
retry: 5,
retryOn: (r) => r.status === 429 || r.status >= 500,
retryDelayFn: (count) => count * 1000 // Linear: 1s, 2s, 3s...
});Response Object
status: Number - HTTP status code (200, 404, etc). 0 for network/timeout errors
statusText: String - HTTP status text ("OK", "Not Found", "Network Error")
body: String - Response body as string. null/empty when response_sink enabled
headers: Object - Response headers
proto: String - Protocol version: "h1" (HTTP/1.x), "h2" (HTTP/2), "h3" (HTTP/3)
cookies: Object - Cookies from Set-Cookie headers
timings: Object - Request timing breakdown (see below)
error: String - Error type: TIMEOUT, DNS, TLS, CONNECT, RESET, NETWORK
errorCode: String - Error code: ETIMEDOUT, ENOTFOUND, ECERT, ECONNREFUSED, ECONNRESET, EPIPE, ENETWORK
Response Helpers
res.json()Parse body as JSON and return object
res.html(selector)Query HTML with CSS selector (returns array of matches)
res.hasHeader(name, [value])Check if header exists (optionally matches value)
res.bodyContains(substring)Check if body contains string
res.bodyMatches(regex)Check if body matches regex pattern
res.isJson()Check if content-type is application/json
res.matchesSchema(schema)Validate JSON body matches type schema ({key: 'string'|'number'|'boolean'|'array'|'object'})
Timings Object
duration - Total request time (ms)
blocked - Time waiting for connection (ms)
connecting - TCP connection time (ms)
tls_handshaking - TLS handshake time (ms)
sending - Time sending request (ms)
waiting - Time waiting for response (ms)
receiving - Time receiving response (ms)
Utility Functions
http.url(baseUrl, params)Build URL with query parameters
http.formEncode(obj)Encode object as x-www-form-urlencoded
http.basicAuth(user, pass)Generate Basic auth header value
http.bearerToken(token)Generate Bearer auth header value
http.setDefaults(options)Set global defaults for all requests
http.file(path, [filename], [contentType])Create file for multipart upload
new FormData()Create multipart form data
# URL and Form Helpers
// Build URL with query parameters
const url = http.url('https://api.example.com/search', { q: 'test', page: 1 });
// Returns: https://api.example.com/search?q=test&page=1
// Form URL encoding
const body = http.formEncode({ username: 'user', password: 'pass' });
http.post('/login', body, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});Parallel Requests
const responses = http.batch([
{ method: 'GET', url: 'https://api.example.com/users' },
{ method: 'GET', url: 'https://api.example.com/posts' },
{ method: 'POST', url: 'https://api.example.com/log', body: JSON.stringify({ event: 'test' }) }
]);
// responses is array in same order
check(responses[0], { 'users loaded': (r) => r.status === 200 });
check(responses[1], { 'posts loaded': (r) => r.status === 200 });
// Track progress with callback
const results = http.batch(requests, (completed, total) => {
print(`Progress: ${completed}/${total}`);
});GraphQL
// Simple GraphQL query
const res = http.graphql('https://api.example.com/graphql', `
query GetUser($id: ID!) {
user(id: $id) { name email }
}
`, { id: '123' });
check(res, { 'has data': (r) => r.json().data !== undefined });
// With custom headers
const res2 = http.graphql('https://api.example.com/graphql',
'mutation { createUser(name: "Test") { id } }',
null,
{ headers: { 'Authorization': 'Bearer token' } }
);Cookie Handling
Cookies are automatically managed per worker. Cookies received via Set-Cookie headers are stored and sent on subsequent requests to matching domains.
# Automatic Cookie Management
// First request - server sets session cookie
http.post('https://api.example.com/login', { user: 'test', pass: 'secret' });
// Subsequent requests automatically include the session cookie
http.get('https://api.example.com/dashboard'); // Cookie header sent automaticallyCookie Jar API
http.cookieJar()Get the current worker's cookie jar
jar.set(url, name, value, [options])Set a cookie
jar.get(url, name)Get a cookie object
jar.delete(url, name)Delete a cookie
jar.clear()Clear all cookies
jar.cookiesForUrl(url)Get all cookies for a URL (array)
const jar = http.cookieJar();
// Set a cookie manually
jar.set('https://api.example.com', 'session', 'abc123', {
domain: 'api.example.com',
path: '/',
secure: true,
httpOnly: true,
});
// Get a specific cookie
const session = jar.get('https://api.example.com', 'session');
print(session.value); // "abc123"
// Read cookies from response
const res = http.post('https://api.example.com/login', credentials);
const sessionCookie = res.cookies['session_id'];
if (sessionCookie) {
print(`Session: ${sessionCookie.value}, expires: ${sessionCookie.expires}`);
}
// Get all cookies for a URL
const cookies = jar.cookiesForUrl('https://api.example.com');
// Returns array: [{name: 'session', value: 'abc123', ...}, ...]Cookie Properties
name - Cookie name
value - Cookie value
domain - Cookie domain
path - Cookie path
expires - Expiration timestamp (Unix epoch)
maxAge - Max age in seconds
secure - HTTPS only
httpOnly - HTTP only (no JS access)
sameSite - SameSite policy (Strict/Lax/None)
Checks & Assertions
Validate responses with the check() function. Failed checks are tracked in metrics.
const res = http.get('https://api.example.com/users');
check(res, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
'body contains users': (r) => r.bodyContains('users'),
'body matches pattern': (r) => r.bodyMatches(/user-\d+/),
'has content-type': (r) => r.hasHeader('content-type'),
'content-type is json': (r) => r.hasHeader('content-type', 'application/json'),
'response is valid json': (r) => r.isJson(),
});Custom Failure Messages
Check functions can return a string instead of a boolean to provide custom failure messages:
check(res, {
'status is 200': (r) => r.status === 200 || `Expected 200, got ${r.status}`,
'has valid data': (r) => {
const data = r.json();
if (!data.items) return 'Missing items array';
if (data.items.length === 0) return 'Items array is empty';
return true;
},
});
// Return values:
// - true = check passed
// - false = check failed (generic message)
// - string = check failed with custom messageRequest Grouping & URL Naming
Group related requests for better metrics organization.
Automatic URL Naming
Fusillade automatically normalizes URLs for metrics by replacing numeric and UUID segments with :id:
// These requests are automatically grouped under the same metric:
http.get('/users/123'); // Metric: GET /users/:id
http.get('/users/456'); // Metric: GET /users/:id
http.get('/orders/abc-def-123'); // Metric: GET /orders/:id (UUID detected)
// Override with explicit name if needed:
http.get('/users/123', { name: 'GetSpecificUser' });Manual Grouping
// Use name option to group dynamic URLs
http.get(`https://api.example.com/users/${userId}`, {
name: 'GET /users/:id'
});
// Use tags for filtering metrics
http.get('https://api.example.com/products', {
tags: { type: 'catalog', region: 'us-east' }
});Request/Response Hooks
// Before request hook
http.addHook('beforeRequest', (request) => {
request.headers['X-Request-ID'] = utils.uuid();
print(`Sending ${request.method} to ${request.url}`);
});
// After response hook
http.addHook('afterResponse', (response) => {
if (response.status >= 400) {
print(`Error: ${response.status} - ${response.body}`);
}
});
// Clear all hooks
http.clearHooks();File Upload
// Simple file upload
const file = http.file('./data/image.png', 'image.png', 'image/png');
http.post('https://api.example.com/upload', JSON.stringify({ file }));
// FormData for complex multipart
const form = new FormData();
form.append('file', http.file('./data/document.pdf'));
form.append('name', 'My Document');
form.append('tags', JSON.stringify(['important', 'work']));
http.post('https://api.example.com/documents', form.body(), {
headers: { 'Content-Type': form.contentType() }
});