List lines

GET /lines returns subscriber lines in creation order (ascending numeric id), one page at a time. It is the workhorse endpoint for reconciliation loops, bulk exports, and any dashboard that wants to render "the lines I own".

The list is auto-scoped by key type. An admin key sees every line on the panel. A reseller key sees only lines whose member_id matches the reseller's own reg_user_id. There is no way to widen or narrow the scope from the query, and cursor values cannot be forged to jump across tenants (a reseller cursor pointing to another reseller's line simply returns an empty page).

Every item in the list has the same shape as the object returned by GET /lines/{id}. See the "line object" section on that page for the meaning of every field.

Endpoint

GET https://<your-panel-domain>/panel-api/v1/lines

Authentication

Bearer token in the Authorization header. See Authentication.

Required scope

lines:read.

Pagination

Cursor-based, forward-only. The response includes next_cursor when more rows are available, and null when you have reached the end.

The cursor is opaque from the caller's perspective, but the server implements it as id > cursor, so the ordering is stable across concurrent inserts and no row is ever returned twice. Persist the last cursor you received and pass it back on the next call.

Requesting limit=N reads up to N + 1 rows internally. If the extra row is present the response contains exactly N items and sets next_cursor to the id of the last returned item. If it is absent, next_cursor is null.

Query parameters

Name Type Required Default Description
limit int no 50 Page size. Clamped to the range [1, 100]. Values outside the range are silently coerced to the nearest bound.
cursor int no (none) Opaque forward cursor. Pass the next_cursor from the previous response. Omit on the first call.
username string no (none) Exact-match filter on username. Case-sensitive.
password string no (none) Exact-match filter on password. Case-sensitive. See the security note below.
enabled bool no (none) Filter by the reseller-visible toggle. Accepts true, false, 1, 0, yes, no, on, off.
is_trial bool no (none) Filter by the trial flag. Accepts the same values as enabled.

The password filter is intended for the reconciliation flow that mirrors OneStream's "find line" behavior. The value ends up in the query string and therefore in every access log along the way (your panel's Nginx, any client-side proxy, and any intermediary). Our own access-log format masks the query string on Panel API requests, but we cannot mask logs owned by third parties. When you already know the id, use GET /lines/{id} instead. When you need to look up by password, prefer the alternate username= + password= combo over pure password= searches.

Response

An object with two fields:

Field Type Description
items Line[] Page of lines in ascending id order. Each item has the shape documented on GET /lines/{id}.
next_cursor int or null Cursor to pass on the next call, or null if this was the last page.
{
  "items": [
    {
      "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
    },
    {
      "id": 1527378,
      "username": "u_e4f56789",
      "password": "1a2b3c4d",
      "member_id": 42,
      "exp_date": 1790985600,
      "max_connections": 3,
      "is_trial": false,
      "is_restreamer": false,
      "enabled": true,
      "admin_enabled": true,
      "bouquets": [2, 4, 15, 82, 107, 108, 110, 112, 114, 115, 118, 119, 120, 134],
      "created_at": 1578154802
    },
    {
      "id": 1532884,
      "username": "u_9a0b1c2d",
      "password": "e5f6a7b8",
      "member_id": 260641,
      "exp_date": 1784513884,
      "max_connections": 5,
      "is_trial": false,
      "is_restreamer": false,
      "enabled": true,
      "admin_enabled": true,
      "bouquets": [15, 110, 118],
      "created_at": 1579290632
    }
  ],
  "next_cursor": 1532884
}

Examples

cURL

curl -X GET "https://<your-panel-domain>/panel-api/v1/lines?limit=50" \
  -H "Authorization: Bearer <your-api-key>"

Paginating through the whole set:

CURSOR=""
while :; do
  URL="https://<your-panel-domain>/panel-api/v1/lines?limit=100${CURSOR:+&cursor=$CURSOR}"
  BODY=$(curl -s -H "Authorization: Bearer <your-api-key>" "$URL")
  echo "$BODY" | jq '.items[]'
  CURSOR=$(echo "$BODY" | jq -r '.next_cursor // empty')
  [ -z "$CURSOR" ] && break
done

PHP SDK

require __DIR__ . '/api-panel-php-sdk-1.0.0/autoload.php';
use XtreamAI\PanelApi\PanelApiClient;

$client = new PanelApiClient(baseUrl: 'https://<your-panel-domain>', token: '<your-api-key>');
$cursor = null;
do {
    $page = $client->lines->list(limit: 100, cursor: $cursor);
    foreach ($page->items as $line) {
        echo $line->id . " " . $line->username . "\n";
    }
    $cursor = $page->nextCursor;
} while ($cursor !== null);

Python SDK

from xtream_ai_panel_api import PanelApiClient

client = PanelApiClient(base_url="https://<your-panel-domain>", token="<your-api-key>")
cursor = None
while True:
    page = client.lines.list(limit=100, cursor=cursor)
    for line in page.items:
        print(line.id, line.username)
    if page.next_cursor is None:
        break
    cursor = str(page.next_cursor)

Errors

HTTP Error slug When it happens How to fix
401 invalid_key The Authorization header is missing, malformed, points to an unknown key, or the key is disabled, expired, deleted, or IP-restricted. Verify the token. Reissue if it was rotated.
403 insufficient_scope The key does not have lines:read. Grant lines:read to the key from the panel, or issue a new key with that scope.
429 rate_limited You exceeded the key's per-minute rate limit or the panel-wide per-IP limit. Honor the Retry-After header. X-RateLimit-Remaining on successful responses tells you how close you are to the limit.
503 api_disabled An admin has turned the Panel API off for this panel. Contact the panel admin.

See also