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 undercategories. Read fromcategories[*].idandcategories[*].nameon your side; do not look for a scalarcategory_idon 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. |