Get a line
action=get_line reads a single subscriber line by its panel-wide numeric id and returns the canonical Line object wrapped in the classic {"status": "STATUS_SUCCESS", "data": {...}} envelope. The request is translated to the native GET /panel-api/v1/lines/{id} and its response is rewrapped, so scoping and shape are identical to the native endpoint.
Tenant isolation is enforced. An admin key can fetch any line on the panel. A reseller key can only fetch lines it owns (member_id == reg_user_id); every other id returns STATUS_FAILURE with not_found, even if that id exists on the panel. The API never leaks the existence of another tenant's line.
Endpoint
GET https://<your-panel-domain>/panel-api/xc/{accesscode}/admin/index.php?action=get_line&id=<id>
Both /admin/index.php and /reseller/index.php are accepted. The path segment is decorative; the admin-versus-reseller decision comes from the key. See the compatibility overview for the full URL contract.
Authentication
Any one of these three forms:
?api_key=<your-api-key>in the query string.api_key=<your-api-key>in a POST body form field.Authorization: Bearer <your-api-key>HTTP header.
See Authentication.
Required scope
lines:read.
Query parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
action |
string | yes | Must be get_line. |
|
api_key |
string | yes (unless sent via Bearer header or body) | Your Panel API token. | |
id |
int | yes | The line's panel-wide numeric id. Non-numeric values are treated as 0 and return STATUS_FAILURE with not_found. Missing values return STATUS_INVALID_DATA with validation_error. |
Response
data is the Line object. Same shape used by every other lines action in the XC dialect and by the native GET /panel-api/v1/lines/{id}.
| Field | Type | Description |
|---|---|---|
id |
int | Panel-wide numeric id. Stable for the life of the line. |
username |
string | The stream username. Autogenerated as u_<8hex> (or trial_<8hex> for trials) when the line was created without an explicit value. |
password |
string | The stream password. Returned in clear, unhashed. |
member_id |
int | Owner's reg_user_id. Reseller keys always see their own id here. |
exp_date |
int or null | Expiry as a UTC Unix epoch. null means the line never expires (perpetual). |
max_connections |
int | Concurrent-connection cap, in the range [1, 100]. |
is_trial |
bool | true if the line was created from a trial package. |
is_restreamer |
bool | true if the line is allowed to restream through the panel. |
enabled |
bool | Reseller-visible toggle. enable_line and disable_line move this field. |
admin_enabled |
bool | Admin override. If false, the line is blocked no matter what enabled says. Only edit_line on an admin key can change it. |
bouquets |
int[] | Bouquet ids assigned to the line. |
created_at |
int or null | Creation timestamp (UTC Unix epoch). Can be null on very old lines migrated in without a timestamp. |
{
"status": "STATUS_SUCCESS",
"data": {
"id": 1512227,
"username": "u_ab12cd34",
"password": "9f3c1a77",
"member_id": 42,
"exp_date": 1813449600,
"max_connections": 4,
"is_trial": false,
"is_restreamer": false,
"enabled": true,
"admin_enabled": true,
"bouquets": [2, 4, 14, 15, 82, 107, 108, 110, 112, 114, 115, 118, 120, 134],
"created_at": 1574874852
}
}
passwordis returned in clear on every response that includes the Line object. If your integration must not log passwords, mask this field before writing to your own logs. The panel's own access-log format already masks the query string on/panel-api/*routes, but the response body is your responsibility.
HTTP status is always 200, even for not_found and invalid_key. Branch on status inside the envelope, 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_line&id=1512227"
PHP (raw HTTP)
$url = 'https://<your-panel-domain>/panel-api/xc/panel_api/admin/index.php'
. '?' . http_build_query([
'api_key' => '<your-api-key>',
'action' => 'get_line',
'id' => 1512227,
]);
$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_line failed');
}
$line = $body['data'];
echo $line['username'], ' exp=', $line['exp_date'], PHP_EOL;
Python (raw HTTP)
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_line", "id": 1512227},
timeout=30,
)
r.raise_for_status()
body = r.json()
if body.get("status") != "STATUS_SUCCESS":
raise RuntimeError(body.get("data", {}).get("message", "get_line failed"))
line = body["data"]
print(line["username"], "exp=", line["exp_date"])
Errors
Response is always HTTP 200. Branch on status, then on data.error for the slug.
| status | Error slug | When it happens | How to fix |
|---|---|---|---|
STATUS_INVALID_DATA |
validation_error |
The id query parameter is missing or empty. data.details.field is "id". |
Send a numeric id. |
STATUS_FAILURE |
not_found |
The id does not exist, or a reseller key targeted a line owned by another reseller. | Verify the id and ownership. |
STATUS_FAILURE |
invalid_key |
The token is missing, unknown, disabled, expired, or the source IP is not in the key's allow-list. | Verify the token. Check the IP allow-list. |
STATUS_NO_PERMISSIONS |
insufficient_scope |
The key does not have lines:read. |
Grant lines:read or issue a new key. |
STATUS_FAILURE |
rate_limited |
Per-minute cap or per-IP cap exceeded. | Back off. |
STATUS_FAILURE |
api_disabled |
An admin has switched the Panel API off for this panel. | Contact the panel admin. |