XC and XUI API Reference
The XC compatibility dialect keeps the wire format of the classic Xtream Codes, Xtream-Masters, XUI.one, and OTT Panel API. If your integration was written against any of those panels, you point it at Xtream AI by changing the base URL and swapping in a new API key. Nothing else in your code has to move.
This reference is written for readers who already know the classic contract. Every page in the section documents one action in the same shape: request, response, examples, errors, cross-references. If you are starting a new integration instead of migrating an old one, prefer the native v1 API with the official SDKs. The compatibility dialect is designed to unblock migration, not to be the recommended long-term shape.
A guided introduction to the dialect, including the full field-by-field mapping table and idempotency semantics via
rid, lives in the Xtream Codes / XUI.one / OTT Panel Compatibility migration guide. This reference cross-links back to that page for cases where the mapping is not obvious.
Base URL
Every action goes through one of these two paths.
https://<your-panel-domain>/panel-api/xc/{accesscode}/admin/index.php
https://<your-panel-domain>/panel-api/xc/{accesscode}/reseller/index.php
The {accesscode} path segment is decorative. Any non-empty string works. Classic panels used it as a shared secret mixed with the API key. Xtream AI enforces security purely through the API key, so you can leave whatever value your current integration hardcodes (panel_api, xc, xtream, whatever). The routing behavior is identical.
The admin and reseller sub-paths are also interchangeable. The API infers the caller identity from the key itself, not from the URL. If your existing integration hardcodes admin/index.php, keep that. If it hardcodes reseller/index.php, keep that. Both accept the same key types.
Authentication
The dialect accepts three ways to send the API key.
?api_key=<token>in the query string. This is the classic XC form and is accepted for both GET and POST.api_key=<token>as a POST body field. Useful if your integration prefers to keep credentials out of URLs.Authorization: Bearer <token>HTTP header. A modern alternative if you want the key out of logs and referrer headers without rewriting every call.
If you send the key both in the query and in the body, or in both the query and the Authorization header, the values must match. Conflicting values are rejected. See Panel API Authentication for the token shape, scopes, IP allow-lists, and per-key rate limits.
Response envelope
Every response has the classic XC shape, always over HTTP 200.
{
"status": "STATUS_SUCCESS",
"data": { "...": "..." }
}
HTTP 200 is used even for authentication failures, missing parameters, forbidden actions, and upstream errors. Widely deployed XC clients branch on the status field, not on the HTTP status code, and we preserve that contract. The only cases where you will see a non-200 HTTP status are real network failures and infrastructure errors from the load balancer.
On failure, data contains a small object you can log.
{
"status": "STATUS_FAILURE",
"data": {
"error": "not_found",
"message": "Line not found"
}
}
The status values that this dialect can return are STATUS_SUCCESS, STATUS_INVALID_DATA, STATUS_NO_PERMISSIONS, STATUS_INSUFFICIENT_CREDITS, STATUS_INVALID_PACKAGE, and STATUS_FAILURE. The full mapping from native error slugs to XC status values is in the compatibility guide. The error slugs themselves are catalogued on the Panel API Errors page.
Supported actions
Every action below is implemented today and translated transparently to the underlying native handler.
Info and read-only
| Action | Description | Reference |
|---|---|---|
user_info |
Identity of the calling key, plus scopes and (for reseller keys) billing shape. | action=user_info |
packages (alias get_packages) |
Every package the caller can use to create a line. | action=packages |
get_bouquets |
Every bouquet the caller can assign to a line. | action=get_bouquets |
get_streams |
List of live streams in the panel catalog. | action=get_streams |
get_stream |
A single live stream by id. | action=get_stream |
get_movies |
List of VOD entries (movies and episodes). | action=get_movies |
get_movie |
A single VOD entry by id. | action=get_movie |
Lines (read)
| Action | Description | Reference |
|---|---|---|
get_lines |
Lines belonging to the caller. Supports username filter and DataTables-style search[value]. |
Xtream Codes / XUI.one compat page |
get_line |
A single line by id. | Xtream Codes / XUI.one compat page |
Lines (write)
| Action | Description | Reference |
|---|---|---|
create_line |
Create a new subscriber line. | Xtream Codes / XUI.one compat page |
edit_line |
Update password, expiry, connections, restreamer flag, enabled state, IP or UA allow-lists. | Xtream Codes / XUI.one compat page |
extend_line |
Renew a line using a package. | Xtream Codes / XUI.one compat page |
enable_line |
Re-enable a suspended line. | Xtream Codes / XUI.one compat page |
disable_line |
Suspend a line without refund. | Xtream Codes / XUI.one compat page |
delete_line |
Permanently delete a line. | Xtream Codes / XUI.one compat page |
Sub-resellers
| Action | Description | Reference |
|---|---|---|
get_users |
List of sub-resellers under the caller. | Xtream Codes / XUI.one compat page |
get_user |
A single reseller by id. | Xtream Codes / XUI.one compat page |
create_user |
Create a sub-reseller (billing mode, credits, quotas). | Xtream Codes / XUI.one compat page |
edit_user |
Update fields on an existing sub-reseller. | Xtream Codes / XUI.one compat page |
adjust_credits |
Credit or debit a reseller balance with an audit note. | Xtream Codes / XUI.one compat page |
Unsupported actions
Two families of classic actions are rejected on purpose.
mysql_queryreturnsSTATUS_FAILUREwitherror: "forbidden_action". Arbitrary SQL execution over HTTP is a foot-gun and is intentionally not exposed by this API.- Every MAG action (
get_mag,create_mag,delete_mag, and the rest) and every Enigma action (get_enigma,create_enigma, and the rest) returnsSTATUS_FAILUREwitherror: "not_implemented". Xtream AI does not provision physical MAG or Enigma devices; the equivalent product concept is a subscriber Line with the appropriate flags on the client device.
The full list and the exact response bodies are on the unsupported actions page.
Idempotency via rid
Classic XC panels have no idempotency layer. Xtream AI adds one on top of the compatibility dialect: on any write action, pass a rid=<unique-per-operation> parameter (in the query string or the POST body) and the same request repeated with the same rid returns the original response back without running the operation twice. The rid is optional (each write runs independently if you omit it) and lives in a 24-hour window scoped to the API key. See Idempotency via rid for the full contract.
Examples
cURL
curl "https://<your-panel-domain>/panel-api/xc/panel_api/admin/index.php?api_key=<your-api-key>&action=user_info"
PHP raw
$url = 'https://<your-panel-domain>/panel-api/xc/panel_api/admin/index.php'
. '?' . http_build_query([
'api_key' => '<your-api-key>',
'action' => 'user_info',
]);
$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('Panel error: ' . ($body['data']['message'] ?? 'unknown'));
}
Python raw
import requests
r = requests.get(
"https://<your-panel-domain>/panel-api/xc/panel_api/admin/index.php",
params={"api_key": "<your-api-key>", "action": "user_info"},
timeout=30,
)
r.raise_for_status()
body = r.json()
assert body["status"] == "STATUS_SUCCESS"
See also
- Xtream Codes / XUI.one / OTT Panel Compatibility. Migration guide with the full field mapping, boolean coercion rules, numeric strictness, pagination limits, and gotchas.
- Panel API Authentication. Token shape, scopes, IP allow-lists, rotation.
- Panel API Errors. Full slug catalog.
- Panel API Overview. Three dialects and where each fits.
- XC unsupported actions. What we deliberately do not implement.