How v2rayN Exposes APIs for External Control and Integration

v2rayN exposes two local HTTP APIs on dynamically allocated ports—one for V2Ray statistics via a dokodemo-door inbound and another for Clash/Sing-Box REST operations—allowing external tools to query metrics and manage proxies without SDK dependencies.

The open-source proxy client v2rayN (repository 2dust/v2rayN) does not rely on a custom binary protocol for external integration. Instead, it automatically spawns local HTTP listeners that expose standard REST endpoints and Prometheus-style metrics, making it trivial for scripts, third-party GUIs, or automation tools to control a running instance.

Architectural Overview of v2rayN APIs

v2rayN utilizes two distinct TCP ports allocated at runtime to separate core statistics from proxy management operations. Both listeners bind strictly to the loopback interface (127.0.0.1), ensuring that external network entities cannot access these control surfaces directly.

Port Allocation and Management

The AppManager singleton generates free ports during application startup and persists them for the session lifetime.

// v2rayN/ServiceLib/Manager/AppManager.cs
public int StatePort  => _statePort;   // V2Ray stats (e.g., 10085)
public int StatePort2 => _statePort2;  // Clash / Sing-Box API (e.g., 10086)

These values are referenced throughout the service layer to construct endpoint URLs dynamically.

V2Ray Statistics API

To expose V2Ray’s internal metrics, v2rayN injects a dokodemo-door inbound into the generated core configuration. This is implemented in V2rayStatisticService.cs.

// v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayStatisticService.cs
Inboundsettings4Ray apiInboundSettings = new();
apiInbound.tag = tag;
apiInbound.listen = Global.Loopback;            // 127.0.0.1
apiInbound.port = AppManager.Instance.StatePort;
apiInbound.protocol = Global.InboundAPIProtocol; // "dokodemo-door"
apiInboundSettings.address = Global.Loopback;
apiInbound.settings = apiInboundSettings;
_coreConfig.inbounds.Add(apiInbound);

Once the core starts, the endpoint http://127.0.0.1:{StatePort}/debug/vars returns Prometheus-style JSON containing uplink and downlink counters. The StatisticsXrayService consumes this data.

// v2rayN/ServiceLib/Services/Statistics/StatisticsXrayService.cs
private string Url => $"{Global.HttpProtocol}{Global.Loopback}:{AppManager.Instance.StatePort}/debug/vars";

var result = await HttpClientHelper.Instance.TryGetAsync(Url);

Clash and Sing-Box Integration API

The second port (StatePort2) hosts a REST-like interface compatible with the Clash external-controller specification, enabling proxy provider manipulation and real-time traffic monitoring.

ClashApiManager constructs the base URL as follows:

// v2rayN/ServiceLib/Manager/ClashApiManager.cs
private string GetApiUrl()
    => $"{Global.HttpProtocol}{Global.Loopback}:{AppManager.Instance.StatePort2}";

Supported operations include:

  • GET /proxies – Retrieve the full proxy list and latency tests.
  • PATCH /proxies/{name} – Switch the active proxy for a selector.
  • GET /configs – Export the current running configuration.
  • PUT /configs?force=true – Hot-reload a new YAML configuration.
  • GET /connections – Inspect active connections.
  • DELETE /connections/{id} – Terminate a specific connection.

For Sing-Box cores, StatisticsSingboxService opens a WebSocket connection to the same port to stream traffic statistics:

// Conceptual usage within StatisticsSingboxService
var wsUrl = $"ws://{Global.Loopback}:{AppManager.Instance.StatePort2}/traffic";

The CoreConfigClashService ensures the generated Clash configuration contains the external-controller directive pointing to 127.0.0.1:{StatePort2}, binding the core’s native API to the port managed by v2rayN.

Practical Code Examples for External Integration

Because v2rayN uses standard HTTP, any language with an HTTP client can control the application. Below are minimal C# demonstrations.

Querying V2Ray Statistics

using System.Net.Http;
using System.Text.Json;

// Obtain the actual port from v2rayN logs or settings
int statePort = 10085; 
string url = $"http://127.0.0.1:{statePort}/debug/vars";

using var http = new HttpClient();
var json = await http.GetStringAsync(url);
var stats = JsonSerializer.Deserialize<JsonElement>(json);

Console.WriteLine($"Uplink: {stats.GetProperty("uplink").GetInt64()} bytes");
Console.WriteLine($"Downlink: {stats.GetProperty("downlink").GetInt64()} bytes");

Managing Clash Proxies via REST

using System.Net.Http;
using System.Text;
using System.Text.Json;

int apiPort = 10086; // StatePort2
string baseUrl = $"http://127.0.0.1:{apiPort}";

using var client = new HttpClient();

// List all proxies
var proxies = await client.GetStringAsync($"{baseUrl}/proxies");
Console.WriteLine(proxies);

// Switch active proxy to "🇯🇵 Japan"
var payload = new { type = "select", name = "🇯🇵 Japan" };
var content = new StringContent(JsonSerializer.Serialize(payload), 
                                Encoding.UTF8, "application/json");
await client.PatchAsync($"{baseUrl}/proxies", content);

Pushing Configuration Updates

string newConfig = await File.ReadAllTextAsync("custom-config.yaml");
var yamlContent = new StringContent(newConfig, Encoding.UTF8, "application/x-yaml");

// force=true hot-reloads without restarting the core
await client.PutAsync($"{baseUrl}/configs?force=true", yamlContent);

Key Source Files and Implementation Details

File Role Location
Global.cs Defines constants (Loopback, HttpProtocol, InboundAPIProtocol) v2rayN/ServiceLib/Global.cs
AppManager.cs Allocates and exposes StatePort and StatePort2 v2rayN/ServiceLib/Manager/AppManager.cs
V2rayStatisticService.cs Injects the dokodemo-door inbound for V2Ray metrics v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayStatisticService.cs
StatisticsXrayService.cs Polls /debug/vars and parses Prometheus-style JSON v2rayN/ServiceLib/Services/Statistics/StatisticsXrayService.cs
ClashApiManager.cs Implements REST client for Clash external-controller endpoints v2rayN/ServiceLib/Manager/ClashApiManager.cs
StatisticsSingboxService.cs Consumes WebSocket traffic stream on StatePort2 v2rayN/ServiceLib/Services/Statistics/StatisticsSingboxService.cs
CoreConfigClashService.cs Writes external-controller directive into generated Clash YAML v2rayN/ServiceLib/Services/CoreConfig/CoreConfigClashService.cs
HttpClientHelper.cs Internal wrapper for HTTP requests (redirects, TLS, timeouts) v2rayN/ServiceLib/Helper/HttpClientHelper.cs

Summary

  • v2rayN exposes APIs for external control and integration through two local HTTP listeners bound to 127.0.0.1, eliminating the need for custom SDKs or binary protocols.
  • The V2Ray statistics API runs on a dynamically allocated port (StatePort) via a dokodemo-door inbound, exposing Prometheus-style metrics at /debug/vars.
  • The Clash/Sing-Box integration API operates on a second port (StatePort2), implementing the standard Clash external-controller REST specification for proxy management and configuration hot-reloading.
  • Sing-Box specific traffic statistics are streamed via WebSocket on the same StatePort2, providing real-time byte counters.
  • All endpoints are private by default (localhost-only), making them safe for local automation while preventing remote exposure.

Frequently Asked Questions

How do I discover the current API port for a running v2rayN instance?

v2rayN assigns random free ports at startup to avoid conflicts. You can find the active ports by checking the application logs or by inspecting the guiNConfig.json settings file in the v2rayN directory. The values are stored as statePort (for V2Ray statistics) and statePort2 (for Clash/Sing-Box API).

Is the v2rayN API accessible from remote machines?

No. By design, both the V2Ray statistics endpoint and the Clash integration API bind exclusively to 127.0.0.1 (localhost). This is hardcoded in Global.Loopback and enforced during inbound configuration. If you need remote management, you must set up a reverse proxy or tunnel, as v2rayN does not expose these APIs on external interfaces for security reasons.

Can I use the Clash API to switch proxies without restarting the core?

Yes. The Clash integration API supports hot-reloading via the PATCH /proxies endpoint. When you send a PATCH request to switch the active proxy, v2rayN communicates with the running Clash core through the external-controller interface on StatePort2. This operation does not require restarting the v2rayN application or the underlying core process.

What is the difference between StatePort and StatePort2?

StatePort (accessed via AppManager.Instance.StatePort) is dedicated to the V2Ray/Xray core statistics API. It serves Prometheus-style metrics through a dokodemo-door inbound at /debug/vars. StatePort2 (accessed via AppManager.Instance.StatePort2) is reserved for Clash and Sing-Box integration, exposing REST endpoints for proxy management and WebSocket streams for real-time traffic data.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →