REST API Reference
The API is available at http://127.0.0.1:8081/api when the proxy is running. All endpoints are served from the same port as the web UI.
GET /api/requests
Query captured requests. Returns paginated results.
Query parameters match the CLI requests command:
| Parameter | Description |
|---|---|
host | Filter by hostname (substring match) |
status | Filter by HTTP status code |
method | Filter by HTTP method |
content_type | Filter by content type |
client_protocol | Filter by client-hop wire protocol: http/1.1 or h2. 400 on an unrecognised value |
origin_protocol | Filter by origin-hop wire protocol: http/1.1 or h2. Same rules, combinable with client_protocol |
search | Search URLs (substring match) |
since | After this time (Unix ms or ISO date) |
until | Before this time (Unix ms or ISO date) |
limit | Maximum number of results (default: 100) |
offset | Pagination offset |
# All requests
curl http://127.0.0.1:8081/api/requests
# Filtered
curl "http://127.0.0.1:8081/api/requests?host=example.com&status=200&limit=50"
# The mixed-hops case: h2 client, HTTP/1.1 origin
curl "http://127.0.0.1:8081/api/requests?client_protocol=h2&origin_protocol=http%2F1.1"Response:
{
"data": [
{
"id": "...",
"timestamp": 1700000000000,
"method": "GET",
"client_protocol": "h2",
"origin_protocol": "http/1.1",
...
}
],
"total": 142,
"limit": 100,
"offset": 0
}GET /api/requests/:id
Get full details for a single request, including headers and base64-encoded bodies.
curl http://127.0.0.1:8081/api/requests/a1b2c3d4-e5f6-7890-abcd-ef1234567890GET /api/requests/:id/messages
Get captured WebSocket frames for a connection, paginated. :id is the id of the request whose kind is websocket (the recorded 101 handshake).
curl 'http://127.0.0.1:8081/api/requests/a1b2c3d4-e5f6-7890-abcd-ef1234567890/messages?limit=100&offset=0'Response:
{
"data": [
{ "direction": "sent", "opcode": "text", "payload": "...", "timestamp": 1700000000000 }
],
"total": 812,
"limit": 500,
"offset": 0
}limit defaults to 500, offset to 0; both must be non-negative integers. An unknown or not-yet-populated id returns an empty page (total: 0), not a 404. Payloads are base64-encoded unconditionally, regardless of opcode -- this is intentionally different from the CLI's --format json/--format agent output, which decodes text frames to UTF-8. Check which surface you're reading from before assuming either encoding.
DELETE /api/requests
Delete all captured traffic.
curl -X DELETE http://127.0.0.1:8081/api/requestsGET /api/status
Get proxy status.
curl http://127.0.0.1:8081/api/statusResponse:
{
"running": true,
"proxyPort": 8080,
"requestCount": 142,
"dbSizeBytes": 3358720
}GET /api/throttle
Get current bandwidth throttle settings and the available presets.
curl http://127.0.0.1:8081/api/throttleResponse:
{
"settings": { "enabled": true, "downKbps": 780, "upKbps": 330, "latencyMs": 100 },
"presets": { "56k": { "...": "..." } }
}PUT /api/throttle
Apply a preset, or set explicit down/up/latency values.
curl -X PUT http://127.0.0.1:8081/api/throttle -H "Content-Type: application/json" \
-d '{"preset":"3g"}'{"preset":"off"} disables throttling. A preset takes full precedence over any enabled/downKbps/upKbps/latencyMs fields sent alongside it -- it replaces the settings object rather than merging into it. Or send explicit fields (omitted fields fall back to the current setting):
curl -X PUT http://127.0.0.1:8081/api/throttle -H "Content-Type: application/json" \
-d '{"enabled":true,"downKbps":500,"upKbps":100,"latencyMs":200}'Settings are persisted to ~/.laurel-proxy/config.json before being applied to the live throttler. If the write fails, the endpoint returns 500 and the change is not applied. A successful request returns { "settings": { ... } }.
GET /api/ca.crt
Download the Laurel Proxy CA certificate. Useful for installing on mobile devices -- open this URL in the device's browser to trigger a certificate install prompt.
curl -O http://127.0.0.1:8081/api/ca.crtPOST /api/proxy/start
Start the proxy server.
curl -X POST http://127.0.0.1:8081/api/proxy/startPOST /api/proxy/stop
Stop the proxy server. The API remains available after stopping the proxy.
curl -X POST http://127.0.0.1:8081/api/proxy/stopGET /api/system-proxy
Report whether the macOS system proxy is currently pointed at Laurel Proxy. Returns a JSON object with a single boolean enabled field.
curl http://127.0.0.1:8081/api/system-proxyPOST /api/system-proxy/enable
Route all macOS HTTP and HTTPS traffic through Laurel Proxy. This is the programmatic equivalent of laurel-proxy proxy-on. macOS only.
curl -X POST http://127.0.0.1:8081/api/system-proxy/enablePOST /api/system-proxy/disable
Restore the previous system proxy settings, equivalent tolaurel-proxy proxy-off. macOS only.
curl -X POST http://127.0.0.1:8081/api/system-proxy/disablePOST /api/replay
Replay a previously captured request.
curl -X POST http://127.0.0.1:8081/api/replay \
-H "Content-Type: application/json" \
-d '{"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"}'POST /api/websocket/replay
Reopen a WebSocket connection and resend the client-sent data frames from a previously recorded connection, preserving the original inter-frame gaps. Only frames with opcode text or binary and direction: 'sent' are replayed -- control frames (ping/pong/close) are managed by the WebSocket client itself.
curl -X POST http://127.0.0.1:8081/api/websocket/replay -H "Content-Type: application/json" \
-d '{"requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"}'Prefer requestId over the alternative {url, frames} body shape: a recorded connection's base64-encoded frames routinely exceed Express's default 100 kb body-size limit, so posting them inline fails before reaching the replay logic. With requestId the server reads the frames from storage itself, keeping the HTTP request body tiny.
A replay refuses (400) if the connection has any truncated client-sent data frame recorded, since replaying a payload cut off at maxBodySize would send corrupted data to a real server. The response includes a stoppedBecause field (close, idle, timeout, or error) plus frameCount and sentAll -- stoppedBecause alone is not a success signal. idle fires whenever the server's first reply takes longer than 500ms, which is routine for real work happening server-side, and close can mean a clean end or a connection cut short after one frame of three. Check sentAll to know whether the replay actually finished what it was asked to send.
GET /api/events
Server-Sent Events stream for real-time updates.
curl -N http://127.0.0.1:8081/api/eventsEvents are named:
event: request-- new captured request (data is the request record as JSON)event: status-- proxy state change (data includes running state and proxy port)event: ws-message-- a new WebSocket frame, pushed immediately as it's captured (not batched likerequest), in the same base64-encoded shape asGET /api/requests/:id/messages
POST /api/shutdown
Shut down the entire process (proxy + API + web UI).
curl -X POST http://127.0.0.1:8081/api/shutdownRelated
- CLI Reference - CLI equivalent of every API endpoint
- Architecture - How the API fits into the system
- Web UI - Visual interface powered by this API
- HTTPS Interception - CA certificate details for the /api/ca.crt endpoint
- HTTP/2 Support - client_protocol/origin_protocol fields in full
- AI Agent Plugin - Programmatic access to traffic for AI agents
- Capture iOS Traffic - Mobile CA certificate installation via /api/ca.crt