action=get_movies

Lists VOD entries in the panel catalog. Despite the name, this endpoint returns both standalone movies and individual series episodes; the is_serie flag on each item tells them apart. Both admin and reseller keys see the full catalog; this endpoint is not filtered by reseller (same reasoning as action=get_streams).

Accepts classic XC limit and start pagination, with a next_cursor in the response for callers that want to follow the keyset chain.

Endpoint

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

Also accepted: POST with action=get_movies 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

vods: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_movies.
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 Pagination below.

Response

data.items is the current page of VOD objects. data.next_cursor is the id of the last item in the page, or null on the last page.

{
  "status": "STATUS_SUCCESS",
  "data": {
    "items": [
      {
        "id": 83287,
        "name": "Movie 83287",
        "icon": "",
        "year": 2019,
        "rating": 7.1,
        "is_serie": true,
        "categories": []
      },
      {
        "id": 83288,
        "name": "Movie 83288",
        "icon": "",
        "year": 2019,
        "rating": 7.1,
        "is_serie": true,
        "categories": []
      },
      {
        "id": 83289,
        "name": "Movie 83289",
        "icon": "",
        "year": 2019,
        "rating": 8.6,
        "is_serie": true,
        "categories": []
      }
    ],
    "next_cursor": 83289
  }
}

Field-by-field.

Field Type Description
id int VOD id. Use it with action=get_movie for detail.
name string Title. For series episodes typically formatted as Show - SxxEyy - Episode title.
icon string Absolute URL of the poster or cover image, or empty string if none.
year int Release year, 0 if unknown.
rating float Rating (0-10 scale), 0 if not rated in the panel database.
is_serie bool true if the entry is a series episode, false if a standalone movie.
categories array Zero or more category objects (id, name). May be empty.

Pagination

Same semantics as action=get_streams. The compat layer maps start = N > 0 to native cursor = N, meaning the response contains items with id > N. Feed next_cursor back in as start=<next_cursor> on the next request. Stop when next_cursor is null.

For sparse id spaces (VOD catalogs where entries have been deleted historically), the start mapping is best-effort. For reliable pagination over a large catalog, use the native GET /panel-api/v1/vods 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_movies&limit=50"

# Next page by cursor
curl "https://<your-panel-domain>/panel-api/xc/panel_api/admin/index.php?api_key=<your-api-key>&action=get_movies&limit=50&start=83289"

PHP raw

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

$start   = 0;
$movies  = 0;
$episodes = 0;
do {
    $url = $base . '?' . http_build_query([
        'api_key' => $token,
        'action'  => 'get_movies',
        '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_movies failed: ' . ($body['data']['message'] ?? 'unknown'));
    }
    foreach ($body['data']['items'] as $v) {
        if ($v['is_serie']) $episodes++; else $movies++;
    }
    $start = $body['data']['next_cursor'];
} while ($start !== null);

printf("Catalog: %d movies, %d series episodes\n", $movies, $episodes);

Python raw

import requests

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

start = 0
movies, episodes = 0, 0
while True:
    r = requests.get(base, params={
        "api_key": token,
        "action":  "get_movies",
        "limit":   100,
        "start":   start,
    }, timeout=30)
    r.raise_for_status()
    body = r.json()
    if body["status"] != "STATUS_SUCCESS":
        raise RuntimeError(f"get_movies failed: {body['data'].get('message')}")
    for v in body["data"]["items"]:
        if v["is_serie"]:
            episodes += 1
        else:
            movies += 1
    start = body["data"]["next_cursor"]
    if start is None:
        break

print(f"Catalog: {movies} movies, {episodes} series episodes")

Errors

status error slug When it happens How to fix
STATUS_INVALID_DATA validation_error action parameter is missing. Include action=get_movies.
STATUS_NO_PERMISSIONS insufficient_scope Key does not carry vods: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