---
title: "Find a sub-reseller, GET /ext/user/find"
description: "List or filter sub-resellers under the calling key by name, in the OneStream flat-array shape."
---

# GET /ext/user/find

`/ext/user/find` returns sub-resellers under the calling key. Its main use is looking up a specific sub-reseller by name before a follow-up call to `/ext/user/{id}/update` or `/ext/user/{id}/credit`. Called without a filter, it returns the full list.

The endpoint maps to the panel's resellers listing under the hood. In OneStream terminology the field name for the filter is `name`; the dialect translates it to `username` for the v1 API. The response objects carry the same shape the native `/panel-api/v1/resellers` endpoint returns, without the `{"items": ...}` envelope.

## Endpoint

`GET https://<your-panel-domain>/panel-api/onestream/ext/user/find`

## Authentication

Send the API key in `X-Api-Key`, `X-Auth-User`, or `Authorization: Bearer`. See the [OneStream overview](/docs/?page=os-ref-overview#base-url-and-authentication).

## Required scope

`resellers:read`. Reseller keys typically do not carry this scope, so this endpoint is normally reachable only by admin keys.

## Query parameters

| Name | Type | Required | Default | Description |
| ---- | ---- | -------- | ------- | ----------- |
| `name` | string | no | (none) | Exact-match filter on the sub-reseller's `username`. Case-sensitive. Translated internally to `username`. |

If `name` is omitted, the endpoint returns the full paginated first page of sub-resellers the calling key can see. To list beyond the first page, use the native [`GET /panel-api/v1/resellers?cursor=<value>`](/docs/?page=xai-ref-resellers-list) endpoint. The OneStream dialect does not project a cursor into this response.

## Response

A plain JSON array. Each element has the following fields (a subset of the native resellers shape, kept identical for compatibility).

| Field | Type | Description |
| ----- | ---- | ----------- |
| `id` | int | Numeric sub-reseller id. Pass this as `{id}` in `/ext/user/{id}/update` and `/ext/user/{id}/credit`. |
| `username` | string | Sub-reseller username. |
| `email` | string | Sub-reseller email. May be empty. |
| `member_group_id` | int | Id of the member group this sub-reseller belongs to. |
| `member_group_name` | string | Human-readable name of the member group. |
| `status` | int | `1` when the sub-reseller is enabled, `0` when disabled. |
| `billing_mode` | string | Either `"credits"` or `"users"`. Decides how the sub-reseller pays for new lines. |
| `credits` | float | Credit balance, when `billing_mode == "credits"`. `0` otherwise. |
| `max_users` | int | Line cap, when `billing_mode == "users"`. `0` otherwise. |
| `active_users` | int or null | Currently active non-trial lines counted against `max_users`. `null` in credits mode. |
| `billing_expires` | int or null | Unix epoch when the sub-reseller's subscription expires. `null` means no expiry. |
| `created_at` | int or null | Unix epoch when the sub-reseller was created. May be `null` for legacy accounts imported before the field was populated. |

Example response for `?name=reseller1`:

```json
[
  {
    "id": 50,
    "username": "reseller1",
    "email": "reseller1@example.com",
    "member_group_id": 4,
    "member_group_name": "RESELLER",
    "status": 1,
    "billing_mode": "credits",
    "credits": 0.25,
    "max_users": 0,
    "active_users": null,
    "billing_expires": null,
    "created_at": 1578098423
  }
]
```

Example response for a name that does not match anyone:

```json
[]
```

## Examples

### cURL

```bash
curl -H "X-Api-Key: <your-api-key>" \
     "https://<your-panel-domain>/panel-api/onestream/ext/user/find?name=reseller1"
```

### PHP raw

```php
$name = 'reseller1';
$url = 'https://<your-panel-domain>/panel-api/onestream/ext/user/find?name=' . urlencode($name);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-Api-Key: <your-api-key>']);
$rows = json_decode(curl_exec($ch), true);
curl_close($ch);

if ($rows === []) {
    echo "no match\n";
} else {
    foreach ($rows as $r) {
        echo $r['id'] . ' ' . $r['username'] . ' (' . $r['credits'] . " credits)\n";
    }
}
```

### Python raw

```python
import requests

r = requests.get(
    "https://<your-panel-domain>/panel-api/onestream/ext/user/find",
    headers={"X-Api-Key": "<your-api-key>"},
    params={"name": "reseller1"},
    timeout=30,
)
r.raise_for_status()
rows = r.json()

if not rows:
    print("no match")
else:
    for row in rows:
        print(row["id"], row["username"], row["credits"])
```

## Errors

| HTTP | Error slug | When it happens | How to fix |
| ---- | ---------- | --------------- | ---------- |
| 401 | `invalid_key` | Header is missing, the token is unknown, the key was disabled, expired, deleted, or the caller IP is not on the key's IP allow-list. | Check the header. If the key was rotated, mint a new one from the panel. |
| 403 | `insufficient_scope` | The key does not carry `resellers:read`. Reseller keys typically hit this. | Use an admin key, or regenerate the key with `resellers:read`. |
| 429 | `rate_limited` | The per-key or per-IP rate limit was hit. Response carries `Retry-After` and `X-RateLimit-*` headers. | Back off for the number of seconds in `Retry-After`. |
| 403 | `api_disabled` | An admin has turned the Panel API off for this panel. | Ask the admin to re-enable it. |

## See also

- [OneStream overview](/docs/?page=os-ref-overview)
- [Native resellers listing](/docs/?page=xai-ref-resellers-list) with cursor pagination.
- [OneStream sub-reseller writes](/docs/?page=panel-api-onestream-compatibility#post-ext-user-create) for create, update, and credit adjustments.
