How to Use Xray-core Built-in API for Runtime Traffic Statistics and User Management

The Xray-core built-in API is a gRPC-based interface that enables runtime traffic monitoring, user management, and system statistics without restarting the proxy server.

This comprehensive guide walks you through enabling the Xray-core built-in API, using the bundled CLI tools for traffic statistics and user operations, and building custom Go clients. All information is derived directly from the XTLS/Xray-core source code.


Enabling the Xray-core Built-in API

The built-in API is disabled by default. To activate it, add an api configuration section to your Xray JSON or YAML config file.

Minimal API Configuration

{
  "api": {
    "tag": "api",
    "listen": "127.0.0.1:8080",
    "services": [
      "StatsService",
      "HandlerService"
    ]
  }
}

Configuration Parameters

Field Type Default Description
tag string "api" Tag used to identify API traffic in routing rules
listen string "127.0.0.1:8080" gRPC listener address (use 0.0.0.0 for remote access)
services array all List of service names to register; omit to enable all

Available API Services

The following services can be enabled in the services array:

  • StatsService — Query traffic counters, system stats, and online users
  • HandlerService — Manage inbounds, outbounds, and users
  • ReflectionService — gRPC reflection for tools like grpcurl
  • LogService — Change log levels at runtime
  • ObservatoryService — Probe remote nodes
  • RouterService — Inspect routing configuration

Source Code Reference

The API configuration parsing is implemented in [infra/conf/api.go](https://github.com/XTLS/Xray-core/blob/main/infra/conf/api.go). The APIConfig struct converts to a commander.Config defined in [app/commander/config.pb.go](https://github.com/XTLS/Xray-core/blob/main/app/commander/config.pb.go).


Using the Bundled CLI for Traffic Statistics

Xray-core includes ready-to-use CLI commands under main/commands/all/api/. These wrap the gRPC API calls and output formatted JSON.

Common CLI Flags

All API commands support these flags defined in [main/commands/all/api/shared.go](https://github.com/XTLS/Xray-core/blob/main/main/commands/all/api/shared.go):

Flag Shorthand Default Description
--server -s 127.0.0.1:8080 API server address
--timeout -t 3s RPC timeout
--json (none) Output raw JSON instead of formatted tables

System Statistics


# Get CPU, memory, and uptime statistics

xray api statssys

This command calls StatsService.GetSysStats implemented in [main/commands/all/api/stats_sys.go](https://github.com/XTLS/Xray-core/blob/main/main/commands/all/api/stats_sys.go).

Query Traffic Counters


# List all counters containing "uplink"

xray api statsquery -pattern "uplink"

# Query all counters and reset them atomically

xray api statsquery -pattern "" -reset

# Query counters for a specific user

xray api statsquery -pattern "user@example.com"

Traffic counter names follow the pattern inbound>>>TAG>>>traffic>>>uplink or outbound>>>TAG>>>traffic>>>downlink. The implementation is in [main/commands/all/api/stats_query.go](https://github.com/XTLS/Xray-core/blob/main/main/commands/all/api/stats_query.go).

Monitor Online Users


# List all inbound tags with active connections

xray api statsonline

# Show IPs of a specific online user with traffic details

xray api statsonlineiplist -email user@example.com -all -include-traffic

These commands identify active connections without requiring traffic counters to be enabled. Source: [stats_online.go](https://github.com/XTLS/Xray-core/blob/main/main/commands/all/api/stats_online.go) and [stats_online_ip_list.go](https://github.com/XTLS/Xray-core/blob/main/main/commands/all/api/stats_online_ip_list.go).


Using the Bundled CLI for Runtime User Management

The HandlerService enables adding, removing, and querying users without restarting Xray.

Add Users to an Inbound


# Add users from JSON files to their respective inbounds

xray api adi users.json

The adi (add inbound users) command accepts one or more JSON files. Each file contains an array of user objects with tag specifying the target inbound. The implementation in [inbound_user_add.go](https://github.com/XTLS/Xray-core/blob/main/main/commands/all/api/inbound_user_add.go) uses the extractInboundUsers function to parse VMess, VLESS, Trojan, and Shadowsocks user formats.

Example users.json:

[
  {
    "tag": "vless-in",
    "users": [
      {
        "email": "newuser@example.com",
        "id": "a1b2c3d4-1111-2222-3333-444455556666",
        "level": 0
      }
    ]
  }
]

Remove Users from an Inbound


# Remove specific users by email from a tagged inbound

xray api rmu -tag=vless-in olduser@example.com another@example.com

The rmu (remove inbound users) command calls HandlerService.AlterInbound with RemoveUserOperation. Source: [inbound_user_remove.go](https://github.com/XTLS/Xray-core/blob/main/main/commands/all/api/inbound_user_remove.go).

Query Inbound Users


# List all users attached to an inbound

xray api inbounduser -tag=vless-in

# Query a specific user by email

xray api inbounduser -tag=vless-in -email=user@example.com

Source: [inbound_user.go](https://github.com/XTLS/Xray-core/blob/main/main/commands/all/api/inbound_user.go).

Count Inbound Users


# Get the total number of users for an inbound

xray api inboundusercount -tag=vless-in

Source: [inbound_user_count.go](https://github.com/XTLS/Xray-core/blob/main/main/commands/all/api/inbound_user_count.go).


Building Custom Go Clients for the Xray-core Built-in API

For integration into custom tooling, import the generated protobuf packages and use the gRPC stubs directly.

Required Imports

import (
	statspb "github.com/xtls/xray-core/app/stats/command"
	handlerpb "github.com/xtls/xray-core/app/proxyman/command"
	"github.com/xtls/xray-core/common/protocol"
)

Complete Example: Query Stats and Add Users

package main

import (
	"context"
	"log"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"

	statspb "github.com/xtls/xray-core/app/stats/command"
	handlerpb "github.com/xtls/xray-core/app/proxyman/command"
	"github.com/xtls/xray-core/common/protocol"
)

func main() {
	// -------------------------------------------------
	// 1️⃣ Connect to the Xray API (default 127.0.0.1:8080)
	// -------------------------------------------------
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	conn, err := grpc.DialContext(
		ctx,
		"127.0.0.1:8080",
		grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithBlock(),
	)
	if err != nil {
		log.Fatalf("dial error: %v", err)
	}
	defer conn.Close()

	// -------------------------------------------------
	// 2️⃣ Query all stats that start with "counter_"
	// -------------------------------------------------
	statsClient := statspb.NewStatsServiceClient(conn)
	resp, err := statsClient.QueryStats(ctx, &statspb.QueryStatsRequest{
		Pattern: "counter_",
		Reset_:  false,
	})
	if err != nil {
		log.Fatalf("query stats error: %v", err)
	}
	log.Printf("Stats: %+v", resp)

	// -------------------------------------------------
	// 3️⃣ Add a new VMess user to inbound tag "vless-in"
	// -------------------------------------------------
	handlerClient := handlerpb.NewHandlerServiceClient(conn)
	newUser := &protocol.User{
		Email: "new@example.com",
		Level: 0,
		// the UUID string is parsed internally by VMess
		Id: &protocol.User_Uuid{Uuid: "a1b2c3d4-1111-2222-3333-444455556666"},
	}
	_, err = handlerClient.AlterInbound(ctx, &handlerpb.AlterInboundRequest{
		Tag: "vless-in",
		Operation: &handlerpb.AlterInboundRequest_AddUser{
			AddUser: &handlerpb.AddUserOperation{User: newUser},
		},
	})
	if err != nil {
		log.Fatalf("add user error: %v", err)
	}
	log.Println("User added successfully")
}

Protocol Buffer Definitions

Service Proto File Generated Go File
StatsService app/stats/command/command.proto [app/stats/command/command.pb.go](https://github.com/XTLS/Xray-core/blob/main/app/stats/command/command.pb.go)
HandlerService app/proxyman/command/command.proto [app/proxyman/command/command.pb.go](https://github.com/XTLS/Xray-core/blob/main/app/proxyman/command/command.pb.go)

Security Considerations for the Xray-core Built-in API

The API grants full control over proxy configuration and user data. Follow these guidelines:

  • Bind to localhost by default — Use 127.0.0.1:8080 unless remote access is required
  • Use TLS for remote access — Configure gRPC TLS credentials when exposing beyond localhost
  • Restrict service exposure — Enable only the services you need in the services array
  • Monitor access logs — The API tag appears in access logs like any other inbound

Summary

The Xray-core built-in API provides a production-ready gRPC interface for runtime traffic statistics and user management without proxy restarts.

  • Enable the API by adding an api section to your config with desired services
  • Use bundled CLI commands under xray api for quick operational tasks
  • Query traffic statistics via StatsService with patterns like counter_ or specific user emails
  • Manage users dynamically via HandlerService to add, remove, and query VMess, VLESS, Trojan, and Shadowsocks users
  • Build custom integrations using the generated protobuf stubs in app/stats/command and app/proxyman/command

Frequently Asked Questions

How do I enable traffic statistics in Xray-core?

Traffic statistics require two configuration steps: enable the StatsService in the api section, and add a stats object to your root configuration. Without the stats object, counters exist but remain at zero. The CLI xray api statsquery will return empty results if statistics aren't properly enabled.

What is the difference between statsonline and statsonlineiplist?

xray api statsonline returns inbound tags that have active connections, useful for quick health checks. xray api statsonlineiplist provides granular data including specific IP addresses per user email, with optional traffic counters. Use statsonline for monitoring dashboards and statsonlineiplist for detailed user session tracking.

Can I add users without restarting Xray-core?

Yes, this is a primary benefit of the built-in API. Use xray api adi with a JSON file containing user definitions, or call HandlerService.AlterInbound directly with AddUserOperation. The change takes effect immediately for new connections without dropping existing ones. Removed users via xray api rmu are disconnected at the protocol level.

How do I query statistics for a specific user?

Use xray api statsquery with a pattern matching the user's email. Xray-core creates counter names following the pattern inbound>>>TAG>>>traffic>>>uplink and user>>>EMAIL>>>traffic>>>uplink. For user-specific queries, use patterns like user>>>user@example.com or broader patterns like >>>user@example.com>>>.

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 →