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
- action=get_movie. Detail for a single VOD entry.
- action=get_streams. Companion listing for live channels.
- Panel API Catalog. Native v1 endpoint with richer filters.