Agent API

The running agent serves an API next to its web inspector, at http://127.0.0.1:4040/api (4041 and up when several agents run). Its calls and JSON follow ngrok’s agent API, so the usual one-liner for grabbing the public URL keeps working:

$ curl -s localhost:4040/api/tunnels | jq -r '.tunnels[0].public_url'

Calls

CallWhat it does
GET /api/tunnelsRunning tunnels: name, public_url, config.addr, connection counts.
GET /api/tunnels/:nameOne tunnel.
POST /api/tunnelsStart a tunnel: name, addr, url (or domain), traffic_policy, pooling_enabled, host_header.
DELETE /api/tunnels/:nameStop it.
GET /api/requests/httpCaptured requests, newest first; ?limit=, ?tunnel_name=. Bodies come base64 in raw.
GET /api/requests/http/:idOne request and its response.
POST /api/requests/httpReplay a request: {"id": "12"}.
DELETE /api/requests/httpClear the captured requests.

Starting a tunnel from a script

shell
curl -s localhost:4040/api/tunnels \
  -H 'Content-Type: application/json' \
  -d '{"name": "api", "addr": "9000", "url": "https://api.example.internal"}'

It answers with the tunnel, its public_url included, once the session is up.

Questions people actually ask

Will my scripts written for ngrok’s local API work?

Yes for the common calls: /api/tunnels to read the public URL, starting and stopping tunnels, and /api/requests/http to list, read and replay captured requests answer in ngrok’s JSON shapes on the same port, 4040.

Can a web page I visit call this API?

No. It answers only on localhost, refuses other Host headers (DNS rebinding), and accepts changes only as JSON or DELETE requests, which a cross-site page can’t send without a CORS preflight the agent never grants.