---
title: "action=packages"
description: "List every package the calling key can use to create a line. Returned unpaginated. Alias action=get_packages behaves identically."
---

# 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.

```json
{
  "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

```bash
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

```php
$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

```python
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](/docs/?page=panel-api-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](/docs/?page=xai-ref-overview#packages)).

## See also

- [XC and XUI API Reference](/docs/?page=xc-ref-overview). Envelope, authentication, action list.
- [action=get_bouquets](/docs/?page=xc-ref-bouquets). Companion listing of the bouquets a package can assign.
- [Panel API Catalog](/docs/?page=xai-ref-overview). Native v1 read-only endpoints with richer detail.
