List VODs

Returns a paginated list of on-demand entries (movies and series episodes). Filterable by category and by a case-insensitive substring of the entry name. Rows with hide_vod = 1 (hidden by the panel operator) are excluded automatically.

Both admin and reseller keys see the full catalog. Series and movies share the same VOD id space in Xtream AI, so a single response can mix rows with is_serie: true (episodes of series) and is_serie: false (movies). Use the is_serie flag to route the row to the right UI treatment.

Metadata such as year and rating is enriched from the panel's local TMDB cache. The catalog exposes a curated subset of the underlying movie_propeties blob; if you need richer metadata (cast, plot, runtime, backdrops) query TMDB directly on your side using the entry name and year.

Endpoint

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

Authentication

Bearer token in the Authorization header. See Authentication.

Required scope

vods: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 VOD you saw. The server returns entries with id > cursor. Omit for the first page.
q string no (none) Case-insensitive substring match on the entry name.
category_id int no 0 Restrict the response to entries that belong to this category (categories of type='movie'). 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 VOD id.
name string Entry display name. For series episodes this typically includes the season and episode markers baked in by the operator.
icon string Poster URL (cover_big from TMDB). Empty string when the entry has no poster.
year int or null Release year, derived from the release date in the TMDB cache. null when the panel has no release date for the entry.
rating float or null TMDB rating on a 0.0 to 10.0 scale. null when not available.
is_serie bool true for series episodes, false for movies.
categories array List of {id, name} objects. An entry 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 (mostly series episodes).

{
  "items": [
    {
      "id": 83287,
      "name": "Series 42 - S08E01 - Starling City",
      "icon": "",
      "year": 2019,
      "rating": 7.1,
      "is_serie": true,
      "categories": []
    },
    {
      "id": 83288,
      "name": "Series 42 - S08E02 - Welcome to Hong Kong",
      "icon": "",
      "year": 2019,
      "rating": 7.1,
      "is_serie": true,
      "categories": []
    },
    {
      "id": 83289,
      "name": "Series 42 - S08E03 - Leap of Faith",
      "icon": "",
      "year": 2019,
      "rating": 8.6,
      "is_serie": true,
      "categories": []
    },
    {
      "id": 83290,
      "name": "Series 42 - S08E04 - Present Tense",
      "icon": "",
      "year": 2019,
      "rating": 8.2,
      "is_serie": true,
      "categories": []
    },
    {
      "id": 83291,
      "name": "Series 43 - S06E01 - Into the Void",
      "icon": "",
      "year": 2019,
      "rating": 5.7,
      "is_serie": true,
      "categories": []
    }
  ],
  "next_cursor": 83291
}

Filtering by q=matrix returns movies whose name matches.

{
  "items": [
    {
      "id": 109898,
      "name": "Movie 42 (1999)",
      "icon": "https://cdn.example.com/posters/movie-42.jpg",
      "year": 1999,
      "rating": 8,
      "is_serie": false,
      "categories": [
        {"id": 44, "name": "Franchise Films"}
      ]
    },
    {
      "id": 109899,
      "name": "Movie 42 Reloaded (2003)",
      "icon": "https://cdn.example.com/posters/movie-42-reloaded.jpg",
      "year": 2003,
      "rating": 7,
      "is_serie": false,
      "categories": [
        {"id": 44, "name": "Franchise Films"}
      ]
    }
  ],
  "next_cursor": 109899
}

Not every category id is populated in every panel. If a category_id filter returns {"items": [], "next_cursor": null} it usually means that category exists but has no VODs attached, not that the filter is broken. Fetch List bouquets and pick a category id from a category you know has content, or list VODs without the filter and pick an id from categories[*].id on a returned row.

Examples

cURL

curl -G -H "Authorization: Bearer <your-api-key>" \
     --data-urlencode "limit=100" \
     --data-urlencode "q=matrix" \
     https://<your-panel-domain>/panel-api/v1/vods

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->vods(limit: 100, q: 'matrix');
foreach ($page->items as $v) {
    echo $v->id, ' ', $v->name, ' ', ($v->year ?? 'unknown'), 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.vods(limit=100, q="matrix")
for v in page.items:
    print(v.id, v.name, v.year)
next_cursor = page.next_cursor  # pass back as cursor for the next page

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 vods:read. Regenerate the key with vods: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