List live streams

Returns a paginated list of live channels defined in the panel. Filterable by category and by a case-insensitive substring of the channel name. Both admin and reseller keys see the full catalog: streams are not filtered by reseller, because a storefront often needs to render what exists, not only what the caller personally sells.

The catalog is a management surface. It never returns source URLs, primary origins, DRM keys, or FFmpeg command flags. If your integration needs to play a channel, use the subscriber's Xtream Codes playlist after creating a line.

Endpoint

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

Authentication

Bearer token in the Authorization header. See Authentication.

Required scope

streams:read.

Query parameters

Name Type Required Default Description
limit int no 50 Page size. Minimum 1, maximum 100. Values above 100 are silently clamped.
cursor int no 0 Numeric id of the last channel you saw. The server returns channels with id > cursor. Omit for the first page.
q string no (none) Case-insensitive substring match on the channel name.
category_id int no 0 Restrict the response to channels that belong to this category (categories of type='channel'). Ignored when 0.

Pagination is keyset. Feed the previous response's next_cursor back in as cursor for the next page. When the last page is reached, next_cursor is null. Offset or page number parameters (?page=2, ?offset=100) are ignored; use cursor.

Response

The response wraps the page in items and adds next_cursor.

Field Type Description
id int Numeric channel id.
name string Channel display name.
icon string Logo URL. Empty string when the channel has no logo.
categories array List of {id, name} objects. A channel can belong to more than one category.
next_cursor int or null Feed back as cursor to fetch the next page. null on the last page.

Example first page.

{
  "items": [
    {
      "id": 30,
      "name": "Channel 30 HD",
      "icon": "https://cdn.example.com/logos/channel-30.png",
      "categories": [
        {"id": 1, "name": "Regional TV"}
      ]
    },
    {
      "id": 38,
      "name": "Channel 38 SD",
      "icon": "https://cdn.example.com/logos/channel-38.png",
      "categories": [
        {"id": 2, "name": "Movies SD"}
      ]
    },
    {
      "id": 39,
      "name": "Channel 39 SD",
      "icon": "https://cdn.example.com/logos/channel-39.png",
      "categories": [
        {"id": 2, "name": "Movies SD"}
      ]
    },
    {
      "id": 40,
      "name": "Channel 40 SD",
      "icon": "https://cdn.example.com/logos/channel-40.png",
      "categories": [
        {"id": 2, "name": "Movies SD"}
      ]
    },
    {
      "id": 41,
      "name": "Channel 41 SD",
      "icon": "https://cdn.example.com/logos/channel-41.png",
      "categories": [
        {"id": 2, "name": "Movies SD"}
      ]
    }
  ],
  "next_cursor": 41
}

Fetching the next page.

{
  "items": [
    {
      "id": 48,
      "name": "Channel 48 SD",
      "icon": "https://cdn.example.com/logos/channel-48.png",
      "categories": [
        {"id": 2, "name": "Movies SD"}
      ]
    }
  ],
  "next_cursor": 52
}

Filtering by category (category_id=1) returns only channels attached to that category, keeping the same shape and pagination.

Examples

cURL

curl -G -H "Authorization: Bearer <your-api-key>" \
     --data-urlencode "limit=50" \
     --data-urlencode "category_id=1" \
     https://<your-panel-domain>/panel-api/v1/streams

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>');

$page = $client->catalog->streams(limit: 50, categoryId: 1);
foreach ($page->items as $s) {
    echo $s->id, ' ', $s->name, PHP_EOL;
}
$next = $page->nextCursor;  // pass back as cursor for the next page

Python SDK

from xtream_ai_panel_api import PanelApiClient

client = PanelApiClient(base_url="https://<your-panel-domain>", token="<your-api-key>")

page = client.catalog.streams(limit=50, category_id=1)
for s in page.items:
    print(s.id, s.name)
next_cursor = page.next_cursor  # pass back as cursor for the next page

The response does not include a top level category_id. A channel can belong to more than one category, so the API returns the full list under categories. Read from categories[*].id and categories[*].name on your side; do not look for a scalar category_id on the channel object.

Errors

HTTP Error slug When it happens How to fix
401 invalid_key The Authorization header is missing, malformed, or names a key that does not exist. Check the header. See Authentication.
403 insufficient_scope The key does not carry streams:read. Regenerate the key with streams:read in its scope list, or use a key that has it.
429 rate_limited The per key rate limit has been exceeded. Response carries Retry-After: 60 and X-RateLimit-* headers. Back off for the number of seconds in Retry-After and retry.

See also