action=get_streams

Lists live streams (channels) in the panel catalog. Both admin and reseller keys see the full catalog; this endpoint is not filtered by reseller. That is intentional. A reseller building a storefront needs to display what content exists even for packages or bouquets they do not personally sell.

The endpoint accepts the classic XC limit and start pagination parameters. start is a best-effort bridge to the native cursor-based pagination; the response also carries a next_cursor field for callers that prefer to walk pages by the last id they saw.

Endpoint

GET https://<your-panel-domain>/panel-api/xc/{accesscode}/admin/index.php?action=get_streams

Also accepted: POST with action=get_streams in the body, and the /reseller/index.php sub-path.

Authentication

Send the API key as ?api_key=<token>, as an api_key=<token> POST field, or as Authorization: Bearer <token>.

Required scope

streams:read. A key without the scope gets STATUS_NO_PERMISSIONS with error: "insufficient_scope".

Query parameters

Name Type Required Default Description
action string Yes Must be get_streams.
api_key string Yes (if not using Bearer) The API key.
limit int No 50 Page size. Minimum 1, maximum 100. Values above 100 are silently clamped.
start int No 0 Best-effort offset. Mapped to the native cursor (return items with id > start). See the note under Pagination below.

Response

data.items is the current page of Stream objects. data.next_cursor is the numeric id you feed back in as start=<next_cursor> for the next page, or null when the last page is reached.

{
  "status": "STATUS_SUCCESS",
  "data": {
    "items": [
      {
        "id": 30,
        "name": "Channel 30",
        "icon": "https://cdn.example.com/logos/channel-30.png",
        "categories": [
          { "id": 1, "name": "Category 1" }
        ]
      },
      {
        "id": 38,
        "name": "Channel 38",
        "icon": "https://cdn.example.com/logos/channel-38.png",
        "categories": [
          { "id": 2, "name": "Category 2" }
        ]
      },
      {
        "id": 39,
        "name": "Channel 39",
        "icon": "https://cdn.example.com/logos/channel-39.png",
        "categories": [
          { "id": 2, "name": "Category 2" }
        ]
      }
    ],
    "next_cursor": 39
  }
}

Field-by-field.

Field Type Description
id int Stream id. Use it with action=get_stream for detail.
name string Human-readable channel name.
icon string Absolute URL of the channel logo, or empty string if none.
categories array Zero or more category objects the stream belongs to. Each has id and name.

Pagination

The XC dialect maps classic start-based offset paging to the native cursor keyset paging.

  • With start omitted or 0, you get the first page starting from the lowest id.
  • With start = N > 0, the compat layer sends cursor = N to the native handler, which returns items with id > N.
  • The response's next_cursor is the id of the last item on the current page. Pass it as start=<next_cursor> on the next request. When next_cursor is null, there are no more pages.

Because the mapping is cursor > id, not a true offset, start=<N> skips ids up to and including N, not "the first N rows". If your ids are dense and monotonic (typical for a healthy panel), that is what you want. If your ids are sparse (rows deleted over time), the two behaviors diverge. For reliable large-scale pagination, migrate to the native GET /panel-api/v1/streams and consume next_cursor directly (see Panel API Catalog).

Examples

cURL

# First page
curl "https://<your-panel-domain>/panel-api/xc/panel_api/admin/index.php?api_key=<your-api-key>&action=get_streams&limit=50"

# Follow-up page by cursor id from the previous response
curl "https://<your-panel-domain>/panel-api/xc/panel_api/admin/index.php?api_key=<your-api-key>&action=get_streams&limit=50&start=39"

PHP raw

$base = 'https://<your-panel-domain>/panel-api/xc/panel_api/admin/index.php';
$token = '<your-api-key>';

$start = 0;
$all = [];
do {
    $url = $base . '?' . http_build_query([
        'api_key' => $token,
        'action'  => 'get_streams',
        'limit'   => 100,
        'start'   => $start,
    ]);
    $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('get_streams failed: ' . ($body['data']['message'] ?? 'unknown'));
    }
    $all = array_merge($all, $body['data']['items']);
    $start = $body['data']['next_cursor'];
} while ($start !== null);

echo count($all), " streams total\n";

Python raw

import requests

base = "https://<your-panel-domain>/panel-api/xc/panel_api/admin/index.php"
token = "<your-api-key>"

start = 0
all_streams = []
while True:
    r = requests.get(base, params={
        "api_key": token,
        "action":  "get_streams",
        "limit":   100,
        "start":   start,
    }, timeout=30)
    r.raise_for_status()
    body = r.json()
    if body["status"] != "STATUS_SUCCESS":
        raise RuntimeError(f"get_streams failed: {body['data'].get('message')}")
    all_streams.extend(body["data"]["items"])
    start = body["data"]["next_cursor"]
    if start is None:
        break

print(len(all_streams), "streams total")

Errors

status error slug When it happens How to fix
STATUS_INVALID_DATA validation_error action parameter is missing. Include action=get_streams.
STATUS_NO_PERMISSIONS insufficient_scope The key does not carry streams:read. Issue a new key with the scope.
STATUS_FAILURE invalid_key Key not sent or not recognized. Check the key value.
STATUS_FAILURE rate_limited Per-minute request budget exceeded. Slow down. See Rate limits & Idempotency.

See also