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

> Unlock Xray-core built-in API for real-time traffic stats & user management without server restarts. Monitor and control your proxy efficiently. Learn how now.

- Repository: [Project X Community, Not Porn-jet X Hub/Xray-core](https://github.com/XTLS/Xray-core)
- Tags: how-to-guide
- Published: 2026-04-21

---

**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

```json
{
  "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)](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)](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)](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

```bash

# 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)](https://github.com/XTLS/Xray-core/blob/main/main/commands/all/api/stats_sys.go)**.

### Query Traffic Counters

```bash

# 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)](https://github.com/XTLS/Xray-core/blob/main/main/commands/all/api/stats_query.go)**.

### Monitor Online Users

```bash

# 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/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/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

```bash

# 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/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`](https://github.com/XTLS/Xray-core/blob/main/users.json):

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

```

### Remove Users from an Inbound

```bash

# 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/inbound_user_remove.go)](https://github.com/XTLS/Xray-core/blob/main/main/commands/all/api/inbound_user_remove.go)**.

### Query Inbound Users

```bash

# 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/inbound_user.go)](https://github.com/XTLS/Xray-core/blob/main/main/commands/all/api/inbound_user.go)**.

### Count Inbound Users

```bash

# 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/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

```go
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

```go
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`](https://github.com/XTLS/Xray-core/blob/main/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)](https://github.com/XTLS/Xray-core/blob/main/app/stats/command/command.pb.go) |
| HandlerService | [`app/proxyman/command/command.proto`](https://github.com/XTLS/Xray-core/blob/main/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)](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>>>`.