HTTP Capture Automation

HTTP Capture saves the requests that pass through Surge to disk as a capture session, including their headers and bodies. This page describes how to run a capture session and read its results entirely through external control: Surge CLI, the HTTP API, or its MCP endpoint. It suits scripts and AI agents that analyze traffic, for example to draft rewrite rules or blocking modules, and works the same for Surge Mac and for Surge iOS controlled from another device. iOS 6.0.0+ Mac 6.11.0+

The examples use surge-cli. Each step lists the HTTP API endpoint and MCP tool that do the same, and Command Reference at the end puts them side by side.

How HTTP Capture Works

Surge keeps two kinds of request records:

  • Recent requests are always kept in memory. The list is short and newer requests push older ones out, so dump recent only shows what happened just now. The in-memory capture filter decides which requests it keeps.
  • Capture sessions are written to disk while HTTP Capture is on. A session keeps every request it records, with request and response bodies, until it is deleted. The on-disk capture filter decides which requests it records.

Each time HTTP Capture is turned on, Surge starts a new session, named after its start time, such as 2026-11-03-102105. The session ends when HTTP Capture is turned off or when it reaches one of its limits, whichever comes first. The default limits are 3 minutes, 100 requests, and 50 MB, so set them before capturing anything longer (see Step 2). Surge sends a notification when a session ends on a limit.

A rule with the always-capture=<session-name> option records the requests it matches into the session with that name, whether HTTP Capture is on or not. See Rule Parameters. These sessions are listed and read in the same way.

Keep the following in mind:

  • Turning HTTP Capture on or off closes all existing connections, so that apps reconnect and their requests are recorded from the start.
  • HTTPS requests only have readable headers and bodies when Surge decrypts them. While a session is running, Surge decrypts the hostnames in the capture's MITM override list even if MITM is turned off. By default the list covers every hostname except Apple, iCloud, the App Store CDN, Facebook, Instagram, Twitter, Dropbox, and bare IP addresses. Decryption requires a CA certificate configured in the profile and trusted by the device. Apps that use certificate pinning fail while their traffic is decrypted.
  • HTTP Capture slows down request processing. Turn it off when you are done.
  • On Surge Mac, sessions are saved in the temporary directory and may be removed by macOS after some days. On Surge iOS, sessions older than 7 days are deleted if Delete Obsolete Sessions is turned on, except sessions you renamed.

Before You Start: Connecting to Surge

Target How to connect
Surge Mac, from the same Mac Run surge-cli directly. It finds the running instance on its own.
Surge Mac or Surge iOS, from another device Set external-controller-access on the target, then run surge-cli --remote <host:port>. Surge iOS only accepts connections from the local Wi-Fi network when the listen address is 0.0.0.0.
HTTP API Set http-api on the target and send the key in the X-Key header.
MCP Also set http-api-mcp = true and add the endpoint http://<host>:<port>/v1/mcp to your MCP client. See HTTP API.

For example, to control Surge iOS at 192.168.1.20 with external-controller-access = apassword@0.0.0.0:6170:

export SURGE_CLI_PASSWORD=apassword
surge-cli --remote 192.168.1.20:6170 capture-session list

All surge-cli commands below accept --remote. Add --raw to get JSON instead of the readable output, which is easier to process in scripts.

Step 1: Choose What to Record

This step is optional. The on-disk capture filter decides which requests a session records. Narrowing it keeps sessions small and leaves more room under the request count and size limits.

surge-cli capture-filter get on-disk
surge-cli capture-filter set on-disk http-only=on type=with-keywords keywords=example.com,api.example.net

capture-filter set <in-memory|on-disk> takes one or more options, applied in order. Nothing changes if one of them is invalid.

  • http-only=<on|off>: record only HTTP and HTTPS requests.
  • hide-crash-reporter=<on|off>, hide-apple=<on|off>, hide-udp=<on|off>: hide requests to crash reporters and analytics services, requests to Apple and iCloud, or UDP sessions.
  • type=<off|with-keywords|without-keywords|pattern>: with-keywords records only URLs containing one of the keywords, without-keywords only URLs containing none of them, and pattern records URLs that match a pattern. Patterns support the wildcard characters * and ?; prefix a pattern with - to exclude a hostname.
  • keywords=<a,b,...> replaces the keyword list; an empty value clears it.
  • add-keyword=, remove-keyword=, enable-keyword=, and disable-keyword= change a single entry. A disabled entry stays in the list but does not take effect. Use add-keyword= for an entry that contains a comma.

HTTP API: GET and POST /v1/capture/filters. MCP: get_capture_filters and set_capture_filter.

Step 2: Set the Session Limits

The session limits are part of Surge's runtime environment, so they are changed with set. They are kept until changed again, and are the same settings as the session options in the app.

surge-cli set ReplicaSessionParameters.timeLimit=1800 ReplicaSessionParameters.requestCountLimit=0 ReplicaSessionParameters.sizeLimit=524288000
Field Meaning Default
ReplicaSessionParameters.timeLimit Maximum duration, in seconds 180
ReplicaSessionParameters.requestCountLimit Maximum number of requests 100
ReplicaSessionParameters.sizeLimit Maximum size on disk, in bytes 52428800 (50 MB)
ReplicaSessionParameters.mitmOverride Decrypt the hostnames in the MITM override list while capturing: 1 or 0. When 0, only the hostnames of the [MITM] section are decrypted, and only while MITM is on. 1

A limit of 0 means no limit. The MITM override list itself can be edited in the app.

Set the limits before starting the session. Changing them while a session is running ends that session and starts a new one with the new limits.

HTTP API: there is no endpoint for the limits; set them with surge-cli, MCP, or the app. MCP: set_environment with {"values": {"ReplicaSessionParameters.timeLimit": 1800, "ReplicaSessionParameters.requestCountLimit": 0}}.

Before Surge iOS 6.0.0 and Surge Mac 6.11.0, set ReplicaSessionParameters.<field> had no effect until the capture options had been changed in the app at least once.

Step 3: Start Capturing

surge-cli feature set capture on

The limits and the switch can also be changed together, which starts the session with the new limits right away:

surge-cli set ReplicaSessionParameters.timeLimit=1800 Replica=1

HTTP API: POST /v1/features/capture with {"enabled": true}. MCP: set_feature with {"feature": "capture", "enabled": true}.

Now use the apps whose traffic you want to analyze. To confirm that requests are being recorded:

  • surge-cli capture-session list shows the new session first, marked as capturing ("active": true in JSON), with its request count.
  • surge-cli watch request prints each new request as it starts.

Step 4: Stop Capturing

surge-cli feature set capture off

HTTP API: POST /v1/features/capture with {"enabled": false}. MCP: set_feature with {"feature": "capture", "enabled": false}.

If the session already reached a limit, HTTP Capture is already off. surge-cli feature get capture shows the current state.

When a session ends on a limit, requests still in progress are saved as they finish. Turning HTTP Capture off closes all connections, which finishes them at once.

Step 5: Find the Session

surge-cli capture-session list
2026-11-03-102105
  152 requests
  Started: 2026-11-03 10:21:05 +08:00
  Ended: 2026-11-03 10:51:05 +08:00 (time-limit)
  Filter: with-keywords (example.com, api.example.net), HTTP only

Sessions are listed newest first. Add include-size to also measure each session on disk, which reads every file in it.

HTTP API: GET /v1/capture/sessions. MCP: list_capture_sessions.

Each session in the JSON output has these fields:

Field Meaning
name The session name, used to address the session in the next steps
active true while HTTP Capture is writing to the session
createdDate When the session folder was created, in seconds since 1970
requestCount Number of saved requests, including any still in progress
size Size on disk in bytes, only with include-size
source http-capture for sessions started by HTTP Capture
startDate, endDate When the session started and ended, in seconds since 1970
endReason stopped, replaced (the limits were changed during the session), time-limit, size-limit, or request-count-limit
limits {"time", "size", "requestCount"} as set in Step 2
filter The on-disk capture filter in effect when the session started, in the format of capture-filter get

source, startDate, endDate, endReason, limits, and filter are only present for sessions started by HTTP Capture in Surge iOS 6.0.0, Surge Mac 6.11.0, or later. endDate and endReason are missing when the Surge engine stopped during the session.

Step 6: Read the Requests

surge-cli capture-session dump 2026-11-03-102105
surge-cli capture-session dump 2026-11-03-102105 filter=api.example.com
surge-cli capture-session dump 2026-11-03-102105 offset=100 limit=100
Session 2026-11-03-102105: requests 1-100 of 152

#123 [2026-11-03 10:21:07 +08:00] GET https://api.example.com/v1/items (Up: 512 B, Down: 18.2 KB)
  key: 000123 - 10.21.07 - GET - https%3A%2F%2Fapi.example.com%2Fv1%2Fitems

Requests are listed in the order they were saved, 100 at a time by default and at most 500. filter keeps the requests whose URL, host, address, policy, rule, process, device, status, or method contains the text, ignoring case. The response reports total, and matched when a filter is given; continue with offset until all requests have been read.

With --raw, each request is a full record in the same format as dump recent, including URL, method, status, requestHeader, responseHeader, timing, traffic, policyName, and rule, plus key. The key identifies the request within the session and is needed to read its bodies. Use the key instead of id: request IDs restart with the Surge engine and can repeat within a session.

HTTP API: GET /v1/capture/sessions/requests?name=<session>&offset=0&limit=100. MCP: list_capture_session_requests, which returns summaries unless detail is true.

Step 7: Read the Bodies

surge-cli capture-session data 2026-11-03-102105 '000123 - 10.21.07 - GET - https%3A%2F%2Fapi.example.com%2Fv1%2Fitems' response

The last argument is request or response. Quote the session name and the key; keys contain spaces.

Each call returns up to 512 KB, or up to length=<bytes> (at most 1 MB), starting at offset=<bytes>. The output says how large the body is and, when more remains, which offset to continue from. With --raw, the body is returned in base64 together with its full length and whether it was truncated.

Bodies are saved as they were transferred. When the record's responseHeader (or requestHeader) has a Content-Encoding such as gzip or br, the body is compressed and the CLI reports it as binary. Decode it yourself:

surge-cli --raw capture-session data 2026-11-03-102105 '000123 - ...' response | jq -r .data | base64 -d | gunzip

An error is returned when no body was saved for the request, for example because it had none or because its HTTPS traffic was not decrypted.

HTTP API: GET /v1/capture/sessions/data?name=<session>&key=<key>&type=response&offset=0, with the name and key percent-encoded. MCP: get_capture_session_body.

Command Reference

Task surge-cli HTTP API MCP tool
Show or change the capture filter `capture-filter get\ set on-disk ...` GET/POST /v1/capture/filters get_capture_filters, set_capture_filter
Set the session limits set ReplicaSessionParameters.<field>=<value> — set_environment
Start or stop capturing `feature set capture on\ off` POST /v1/features/capture set_feature
List saved sessions capture-session list [include-size] GET /v1/capture/sessions list_capture_sessions
Read a session's requests capture-session dump <session> [offset=N] [limit=N] [filter=TEXT] GET /v1/capture/sessions/requests list_capture_session_requests
Read a request or response body `capture-session data <request\ response> [offset=N] [length=N]` GET /v1/capture/sessions/data get_capture_session_body
Read the requests of the last moments without a session dump recent GET /v1/requests/recent list_requests

In the apps, saved sessions can also be browsed, exported, and deleted on the Surge iOS Saved HTTP Capture Sessions page, and the Capture view of Surge Mac sets the session limits and opens the folder of saved sessions in Finder.

results matching ""

    No results matching ""