πΈοΈ Capture API β Network Traffic Recording β
Capture HTTP requests, WebSocket frames, cookies, and storage in real time. Perfect for debugging, API reverse engineering, and understanding how web applications work.
β οΈ Version Requirement: Binary v0.1.14+ | Library v0.0.20+
Overview β
Network capture runs in the background and records everything automatically. Start capture, navigate, stop capture, and retrieve the data.
| Capture Type | Method | What It Records |
|---|---|---|
| Requests | capture.requests() | HTTP method, URL, status, headers, bodies |
| WebSocket | capture.ws() | Frames, direction (sent/received), data |
| Cookies | capture.cookies() | Name, value, domain, path, flags |
| Storage | capture.storage() | localStorage and sessionStorage writes |
Basic Usage β
import piggy from "nothing-browser";
await piggy.launch({ mode: "tab", binary: "headless" });
await piggy.register("site", "https://example.com");
// Start capturing
await piggy.site.capture.start();
// Navigate and interact
await piggy.site.navigate();
await piggy.site.click("#load-data");
// Stop capturing
await piggy.site.capture.stop();
// Get captured data
const requests = await piggy.site.capture.requests();
const websockets = await piggy.site.capture.ws();
const cookies = await piggy.site.capture.cookies();
const storage = await piggy.site.capture.storage();
console.log(`Captured ${requests.length} requests`);
console.log(`Captured ${websockets.length} WebSocket frames`);
console.log(`Captured ${cookies.length} cookies`);Capture Lifecycle β
capture.start() β
Starts recording network traffic. Buffers are cleared automatically.
await piggy.site.capture.start();capture.stop() β
Stops recording. Data remains available for retrieval.
await piggy.site.capture.stop();capture.clear() β
Clears all captured data from memory.
await piggy.site.capture.clear();HTTP Requests β
capture.requests() β
Returns all captured HTTP requests with full details.
const requests = await piggy.site.capture.requests();
for (const req of requests) {
console.log(`${req.method} ${req.url} β ${req.status}`);
console.log(" Request headers:", req.reqHeaders);
console.log(" Response headers:", req.resHeaders);
if (req.reqBody) console.log(" Request body:", req.reqBody);
if (req.resBody) console.log(" Response body:", req.resBody);
}CapturedRequest Object β
interface CapturedRequest {
method: string; // GET, POST, PUT, DELETE
url: string; // Full request URL
status: string; // HTTP status code (e.g., "200")
type: string; // Resource type (document, xhr, fetch, etc.)
mime: string; // Content type (text/html, application/json)
reqHeaders: string; // Request headers as string
reqBody: string; // Request body (if any)
resHeaders: string; // Response headers as string
resBody: string; // Response body (if any)
size: number; // Response size in bytes
timestamp: string; // ISO timestamp
}WebSocket Frames β
capture.ws() β
Returns all captured WebSocket frames.
const frames = await piggy.site.capture.ws();
for (const frame of frames) {
console.log(`${frame.direction} ${frame.direction === "sent" ? "β" : "β"} ${frame.data}`);
console.log(` URL: ${frame.url}`);
console.log(` Binary: ${frame.binary}`);
}WebSocketFrame Object β
interface WebSocketFrame {
connectionId: string; // Unique connection identifier
url: string; // WebSocket endpoint URL
direction: "sent" | "received"; // Which direction
data: string; // Frame payload (text or base64)
binary: boolean; // Whether data is binary
timestamp: string; // ISO timestamp
}Cookies β
capture.cookies() β
Returns all cookies captured during the session.
const cookies = await piggy.site.capture.cookies();
for (const cookie of cookies) {
console.log(`${cookie.name}=${cookie.value}`);
console.log(` Domain: ${cookie.domain}, Path: ${cookie.path}`);
console.log(` HttpOnly: ${cookie.httpOnly}, Secure: ${cookie.secure}`);
console.log(` Expires: ${cookie.expires || "Session"}`);
}CapturedCookie Object β
interface CapturedCookie {
name: string;
value: string;
domain: string;
path: string;
httpOnly: boolean;
secure: boolean;
expires: string; // ISO timestamp or empty string for session
}Storage β
capture.storage() β
Returns localStorage and sessionStorage entries as a flat array.
const storage = await piggy.site.capture.storage();
for (const entry of storage) {
console.log(`${entry.key} = ${entry.value}`);
}
// Filter localStorage entries
const localStorageEntries = storage.filter(e => e.key.startsWith("localStorage:"));StorageEntry Object β
interface StorageEntry {
key: string; // Storage key (prefixed with localStorage: or sessionStorage:)
value: string; // Storage value
}Key format: localStorage:https://example.com:keyName or sessionStorage:https://example.com:keyName
Real-World Examples β
Example 1: Capture API Authentication Flow β
await piggy.site.capture.start();
// Login
await piggy.site.navigate("https://example.com/login");
await piggy.site.type("#email", "user@example.com");
await piggy.site.type("#password", "password123");
await piggy.site.click("#login-btn");
await piggy.site.waitForNavigation();
// Navigate to dashboard
await piggy.site.navigate("https://example.com/dashboard");
await piggy.site.capture.stop();
// Find the login request
const requests = await piggy.site.capture.requests();
const loginRequest = requests.find(r => r.url.includes("/api/login") && r.method === "POST");
if (loginRequest?.resBody) {
const response = JSON.parse(loginRequest.resBody);
console.log("Auth token:", response.token);
console.log("User ID:", response.userId);
}
// Find all API calls after login
const apiCalls = requests.filter(r =>
r.url.includes("/api/") && r.type === "fetch"
);
console.log(`Found ${apiCalls.length} API calls`);Example 2: Monitor WebSocket Messages β
await piggy.site.capture.start();
await piggy.site.navigate("https://tradingview.com");
await piggy.site.wait(10000); // Let WebSocket collect data
await piggy.site.capture.stop();
const frames = await piggy.site.capture.ws();
// Filter trade messages
const trades = frames.filter(f =>
f.type === "text" && f.data.includes("trade")
);
for (const trade of trades) {
console.log(`${trade.direction}: ${trade.data}`);
}Example 3: Debug Request-Response Mismatch β
await piggy.site.capture.start();
await piggy.site.click("#submit-form");
await piggy.site.waitForResponse("*/api/submit*");
await piggy.site.capture.stop();
const requests = await piggy.site.capture.requests();
const submitRequest = requests.find(r => r.url.includes("/api/submit"));
if (submitRequest) {
console.log("Sent:", submitRequest.reqBody);
console.log("Received:", submitRequest.resBody);
console.log("Status:", submitRequest.status);
if (submitRequest.status !== "200") {
console.error("Request failed!");
console.log("Request headers:", submitRequest.reqHeaders);
console.log("Response headers:", submitRequest.resHeaders);
}
}Example 4: Extract Session Setup β
async function captureSessionSetup(site: any, loginUrl: string, credentials: any) {
await site.capture.start();
await site.navigate(loginUrl);
await site.type("#username", credentials.username);
await site.type("#password", credentials.password);
await site.click("#login-btn");
await site.waitForNavigation();
await site.navigate("https://example.com/dashboard");
await site.wait.selector({ selector: ".dashboard", state: "visible" });
await site.capture.stop();
const session = {
requests: await site.capture.requests(),
cookies: await site.capture.cookies(),
storage: await site.capture.storage()
};
return session;
}
const session = await captureSessionSetup(piggy.site, "https://example.com/login", {
username: "user@example.com",
password: "password123"
});
console.log(`Captured ${session.requests.length} requests`);
console.log(`Saved ${session.cookies.length} cookies`);Example 5: Export to HAR Format β
async function exportToHAR(site: any): Promise<object> {
const requests = await site.capture.requests();
return {
log: {
version: "1.2",
creator: { name: "Piggy", version: "1.0" },
entries: requests.map(req => ({
request: {
method: req.method,
url: req.url,
headers: parseHeaders(req.reqHeaders),
bodySize: req.reqBody?.length || 0
},
response: {
status: parseInt(req.status),
headers: parseHeaders(req.resHeaders),
bodySize: req.resBody?.length || 0,
content: { text: req.resBody }
},
timings: { wait: 0 },
time: 0,
startedDateTime: req.timestamp
}))
}
};
}
function parseHeaders(headerString: string): Array<{ name: string; value: string }> {
const lines = headerString.split("\n");
const headers = [];
for (const line of lines) {
const colonIndex = line.indexOf(":");
if (colonIndex > 0) {
headers.push({
name: line.substring(0, colonIndex).trim(),
value: line.substring(colonIndex + 1).trim()
});
}
}
return headers;
}
const har = await exportToHAR(piggy.site);
console.log(JSON.stringify(har, null, 2));Example 6: Capture and Save to File β
import { writeFileSync } from "fs";
await piggy.site.capture.start();
await piggy.site.navigate("https://example.com/api/data");
await piggy.site.capture.stop();
const requests = await piggy.site.capture.requests();
const apiCalls = requests.filter(r => r.url.includes("/api/"));
writeFileSync("./api-calls.json", JSON.stringify(apiCalls, null, 2));
console.log(`Saved ${apiCalls.length} API calls to api-calls.json`);Performance Considerations β
// β Bad: Capturing for too long
await piggy.site.capture.start();
await piggy.site.navigate("https://example.com");
await piggy.site.wait(60000); // 1 minute β memory grows
await piggy.site.capture.stop();
// β
Good: Capture only what you need
await piggy.site.capture.start();
await piggy.site.navigate("https://example.com");
await piggy.site.click("#target-action");
await piggy.site.waitForResponse("*/api/target*");
await piggy.site.capture.stop(); // Stop immediately after
// Clear between operations to free memory
await piggy.site.capture.start();
await piggy.site.navigate("https://example.com/page1");
await piggy.site.capture.stop();
const data1 = await piggy.site.capture.requests();
await piggy.site.capture.clear(); // Free memory
await piggy.site.capture.start();
await piggy.site.navigate("https://example.com/page2");
await piggy.site.capture.stop();
const data2 = await piggy.site.capture.requests();Complete Example: API Reverse Engineering β
import piggy, { usePiggy } from "nothing-browser";
import { writeFileSync } from "fs";
await piggy.launch({ mode: "tab", binary: "headless" });
await piggy.register("app", "https://example.com");
const { app } = usePiggy<"app">();
// Start capture before navigation
await app.capture.start();
// Navigate and trigger API calls
await app.navigate();
await app.click("#load-products");
await app.wait.selector({ selector: ".product-list", state: "visible" });
await app.click("#load-more");
await app.wait.selector({ selector: ".product-card:last-child", state: "attached" });
// Stop capture
await app.capture.stop();
// Analyze captured requests
const requests = await app.capture.requests();
// Find all XHR/fetch requests
const apiCalls = requests.filter(r => r.type === "fetch" || r.type === "xhr");
console.log(`Found ${apiCalls.length} API calls`);
for (const call of apiCalls) {
console.log(`\n${call.method} ${call.url}`);
console.log(` Status: ${call.status}`);
console.log(` Request: ${call.reqBody}`);
console.log(` Response: ${call.resBody?.slice(0, 200)}...`);
}
// Save to file for analysis
writeFileSync("./captured-api.json", JSON.stringify(apiCalls, null, 2));
// Check for WebSocket connections
const wsFrames = await app.capture.ws();
if (wsFrames.length > 0) {
console.log(`\nWebSocket frames: ${wsFrames.length}`);
writeFileSync("./websocket-frames.json", JSON.stringify(wsFrames, null, 2));
}
await piggy.close();API Reference β
| Method | Parameters | Returns | Description |
|---|---|---|---|
capture.start() | β | Promise<void> | Start recording |
capture.stop() | β | Promise<void> | Stop recording |
capture.clear() | β | Promise<void> | Clear captured data |
capture.requests() | β | Promise<CapturedRequest[]> | Get HTTP requests |
capture.ws() | β | Promise<WebSocketFrame[]> | Get WebSocket frames |
capture.cookies() | β | Promise<CapturedCookie[]> | Get captured cookies |
capture.storage() | β | Promise<StorageEntry[]> | Get storage entries |
Type Definitions β
interface CapturedRequest {
method: string;
url: string;
status: string;
type: string;
mime: string;
reqHeaders: string;
reqBody: string;
resHeaders: string;
resBody: string;
size: number;
timestamp: string;
}
interface WebSocketFrame {
connectionId: string;
url: string;
direction: "sent" | "received";
data: string;
binary: boolean;
timestamp: string;
}
interface CapturedCookie {
name: string;
value: string;
domain: string;
path: string;
httpOnly: boolean;
secure: boolean;
expires: string;
}
interface StorageEntry {
key: string;
value: string;
}Next Steps β
- Intercept API β Block, redirect, mock requests
- Cookies API β Manage cookies directly
- Proxy API β Route traffic through proxies
Nothing Ecosystem Β· Ernest Tech House Β· Kenya Β· 2026