---
title: "Get a line, action=get_line"
description: "Xtream Codes compatibility read of a single subscriber line by numeric id. Returns the same shape used by every other Line endpoint."
---

# 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}`](/docs/?page=xai-ref-lines-get) 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](/docs/?page=panel-api-xtream-codes-compatibility) 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](/docs/?page=panel-api-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}`](/docs/?page=xai-ref-lines-get).

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

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

> [!NOTE]
> `password` is 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

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

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

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

## See also

- [List lines, action=get_lines](/docs/?page=xc-ref-lines-list)
- [Edit a line, action=edit_line](/docs/?page=xc-ref-lines-edit)
- [Xtream Codes / XUI.one / OTT Panel compatibility](/docs/?page=panel-api-xtream-codes-compatibility)
- [Native get endpoint, GET /lines/{id}](/docs/?page=xai-ref-lines-get)
