action=packages

Lists every package visible to the calling key. Admin keys see every package on the panel. Reseller keys see only packages whose groups list contains their member_group_id, meaning packages the admin has authorized the reseller's group to sell. The response is unpaginated; a healthy panel has fewer than a few hundred packages and the full list ships in one call.

The action name get_packages is accepted as an alias and returns the same shape. Some classic XC clients used one, some used the other. Both work.

Endpoint

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

Also accepted:

  • action=get_packages (alias, identical response).
  • POST with action=packages or action=get_packages in the body.
  • /reseller/index.php sub-path (identical behavior).

Authentication

Send the API key as ?api_key=<token>, as an api_key=<token> POST field, or as Authorization: Bearer <token>.

Required scope

packages:read. A key without the scope gets STATUS_NO_PERMISSIONS with error: "insufficient_scope".

Query parameters

Name Type Required Default Description
action string Yes packages or get_packages.
api_key string Yes (if not using Bearer) The API key.

There are no pagination parameters. The full package list is returned in a single response.

Response

data.items is the list of Package objects. There is no next_cursor field.

{
  "status": "STATUS_SUCCESS",
  "data": {
    "items": [
      {
        "id": 76,
        "package_name": "Package 42",
        "is_trial": false,
        "is_official": true,
        "official_credits": 12,
        "official_duration": 12,
        "official_duration_in": "months",
        "trial_credits": 0,
        "trial_duration": 0,
        "trial_duration_in": "hours",
        "max_connections": 1,
        "is_restreamer": false,
        "forced_country": ""
      },
      {
        "id": 67,
        "package_name": "Package 43",
        "is_trial": false,
        "is_official": true,
        "official_credits": 1,
        "official_duration": 1,
        "official_duration_in": "months",
        "trial_credits": 0,
        "trial_duration": 0,
        "trial_duration_in": "hours",
        "max_connections": 1,
        "is_restreamer": false,
        "forced_country": ""
      }
    ]
  }
}

Field-by-field.

Field Type Description
id int Package id, used as package in create_line and extend_line.
package_name string Human-readable name shown in the panel UI.
is_trial bool true for trial packages. Trial packages cannot be used in extend_line.
is_official bool Whether the panel admin flagged the package as an "official" catalog entry.
official_credits int Credits charged when the package is used to create a non-trial line (credits billing mode).
official_duration int Numeric part of the package validity. Combined with official_duration_in.
official_duration_in string Time unit for official_duration. Typical values: months, days, hours.
trial_credits int Credits charged for the trial variant of the package. 0 when the package has no trial variant.
trial_duration int Numeric part of the trial validity.
trial_duration_in string Time unit for trial_duration.
max_connections int Concurrent connection cap that will be stamped on new lines created from this package.
is_restreamer bool Whether lines from this package are flagged as restreamers (allowed to be used from a datacenter).
forced_country string ISO country code that forces lines created from this package to a specific region, or empty string.

Examples

cURL

curl "https://<your-panel-domain>/panel-api/xc/panel_api/admin/index.php?api_key=<your-api-key>&action=packages"

# Alias
curl "https://<your-panel-domain>/panel-api/xc/panel_api/admin/index.php?api_key=<your-api-key>&action=get_packages"

PHP raw

$url = 'https://<your-panel-domain>/panel-api/xc/panel_api/admin/index.php'
     . '?' . http_build_query([
         'api_key' => '<your-api-key>',
         'action'  => 'packages',
       ]);
$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('packages failed: ' . ($body['data']['message'] ?? 'unknown'));
}
foreach ($body['data']['items'] as $p) {
    printf("%d\t%s\t%d credits\t%s\n",
        $p['id'], $p['package_name'], $p['official_credits'],
        $p['is_trial'] ? 'TRIAL' : ''
    );
}

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": "packages"},
    timeout=30,
)
r.raise_for_status()
body = r.json()
if body["status"] != "STATUS_SUCCESS":
    raise RuntimeError(f"packages failed: {body['data'].get('message')}")
for p in body["data"]["items"]:
    print(p["id"], p["package_name"], p["official_credits"], "credits",
          "TRIAL" if p["is_trial"] else "")

Errors

status error slug When it happens How to fix
STATUS_INVALID_DATA validation_error action parameter is missing. Include action=packages.
STATUS_NO_PERMISSIONS insufficient_scope The key does not carry packages:read. Issue a new key with the scope, or add it in the panel.
STATUS_FAILURE invalid_key Key not sent or not recognized. Check the key value and that it was not rotated.
STATUS_FAILURE rate_limited Per-minute request budget exceeded. Slow down. See Rate limits & Idempotency.
STATUS_FAILURE api_disabled Panel operator flipped the API kill switch. Wait for the API to be re-enabled.

Difference between the XC list and the native list

The XC dialect returns the full package list in a single response with no pagination. The native GET /panel-api/v1/packages endpoint returns the same shape. The compat layer is passing the response through unchanged inside the XC envelope. If you need any of the richer fields (bouquets attached to the package, groups authorized, quotas), migrate the call to the native endpoint (see Panel API Catalog).

See also