Get one sub-reseller, action=get_user
Return the record of a single reseller in the classic Xtream Codes envelope. The compat layer translates this call to the native GET /panel-api/v1/resellers/{id} and wraps the result in the XC {"status": "STATUS_SUCCESS", "data": {...}} shape. The response carries the current billing snapshot in a nested billing object whose shape depends on the reseller's billing_mode (see below).
Only admin API keys can call this action. Reseller keys are rejected before the handler runs because they never receive resellers:read at issuance. From a reseller-side integration, call action=user_info instead to introspect the caller's own account.
Endpoint
GET https://<your-panel-domain>/panel-api/xc/{accesscode}/admin/index.php?action=get_user
The {accesscode} segment is decorative. Any non-empty value works. Security is enforced through the API key, not the path.
Authentication
Send the API key either as api_key=<your-api-key> in the query string or as Authorization: Bearer <your-api-key> in the header. Admin key only. See Xtream Codes compatibility for the full auth contract.
Required scope
resellers:read. Reseller keys never carry this scope, so a reseller-issued key always sees STATUS_NO_PERMISSIONS with insufficient_scope.
Query parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
id |
int | yes | Numeric reg-user ID of the reseller. |
Response
The data payload matches the native GET /resellers/{id} shape. The billing object differs between billing modes.
For a credits-mode reseller, credits holds the current balance and both max_users and active_users are null:
{
"status": "STATUS_SUCCESS",
"data": {
"id": 100260595,
"username": "reseller_bob",
"email": "bob@example.com",
"member_group_id": 4,
"member_group_name": "RESELLER",
"status": 1,
"billing": {
"mode": "credits",
"credits": 0.25,
"max_users": null,
"active_users": null,
"billing_expires": null
}
}
}
For a users-mode reseller, credits is null, max_users is the slot cap, active_users is the current usage (own active non-trial lines plus slots already reserved by sub-resellers), and billing_expires is the Unix timestamp in seconds when the plan lapses:
{
"status": "STATUS_SUCCESS",
"data": {
"id": 100295821,
"username": "reseller_dave",
"email": "dave@example.com",
"member_group_id": 4,
"member_group_name": "RESELLER",
"status": 1,
"billing": {
"mode": "users",
"credits": null,
"max_users": 250,
"active_users": 231,
"billing_expires": 1793520000
}
}
}
HTTP status is always 200. Your client must branch on body.status, not on the HTTP code.
Examples
cURL
curl "https://<your-panel-domain>/panel-api/xc/panel_api/admin/index.php?api_key=<your-api-key>&action=get_user&id=100260595"
PHP raw
$url = 'https://<your-panel-domain>/panel-api/xc/panel_api/admin/index.php'
. '?' . http_build_query([
'api_key' => '<your-api-key>',
'action' => 'get_user',
'id' => 100260595,
]);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);
if ($body['status'] !== 'STATUS_SUCCESS') {
throw new RuntimeException($body['data']['message'] ?? 'get_user failed');
}
$reseller = $body['data'];
echo $reseller['username'], ' balance=', $reseller['billing']['credits'], PHP_EOL;
Python raw
import requests
r = requests.get(
"https://<your-panel-domain>/panel-api/xc/panel_api/admin/index.php",
params={"api_key": "<your-api-key>", "action": "get_user", "id": 100260595},
timeout=30,
)
r.raise_for_status()
body = r.json()
if body["status"] != "STATUS_SUCCESS":
raise RuntimeError(body["data"].get("message", "get_user failed"))
reseller = body["data"]
print(reseller["username"], "balance=", reseller["billing"]["credits"])
Errors
HTTP is always 200 for this dialect. The status field is the branch signal, and data.error carries the exact slug.
| status | Error slug | When it happens | How to fix |
|---|---|---|---|
STATUS_INVALID_DATA |
validation_error |
The id parameter is missing or empty. |
Include a numeric id in the query. |
STATUS_FAILURE |
invalid_key |
The api_key (or Authorization: Bearer) is missing, malformed, or unknown. |
Send a live admin key. |
STATUS_NO_PERMISSIONS |
insufficient_scope |
The key does not carry resellers:read. Reseller-issued keys always land here. |
Use an admin key, or call action=user_info from a reseller integration to introspect the caller's own account. |
STATUS_NO_PERMISSIONS |
admin_only_endpoint |
A key that carried the scope but is still tagged as a reseller reached the handler. | Use an admin key. |
STATUS_FAILURE |
not_found |
No reseller exists with that ID. | Verify the ID with action=get_users first. |
STATUS_FAILURE |
rate_limited |
The per-key request budget for the current minute is spent. | Slow the polling loop, then retry after the minute rolls over. |