---
title: "Get one sub-reseller, action=get_user"
description: "Fetch a single reseller by numeric ID in the XC dialect. Admin-only. Maps to the native GET /panel-api/v1/resellers/{id} endpoint."
---

# 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}`](/docs/?page=xai-ref-resellers-get) 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](/docs/?page=panel-api-xtream-codes-compatibility#base-url-and-authentication) 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`:

```json
{
  "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:

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

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

```php
$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

```python
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. |

## See also

- [List sub-resellers, action=get_users](/docs/?page=xc-ref-users-list)
- [Create a sub-reseller, action=create_user](/docs/?page=xc-ref-users-create)
- [Update a sub-reseller, action=edit_user](/docs/?page=xc-ref-users-edit)
- [Adjust reseller credits, action=adjust_credits](/docs/?page=xc-ref-adjust-credits)
- [Xtream Codes compatibility overview](/docs/?page=panel-api-xtream-codes-compatibility)
- [Native v1: get a reseller](/docs/?page=xai-ref-resellers-get)
