---
title: "Profile of the authenticated key, GET /ext/profile"
description: "Returns the identity behind the calling API key in the OneStream shape: id, name, username, is_admin, credits."
---

# GET /ext/profile

`/ext/profile` is the introspection endpoint. It answers "who am I to this panel?" from the point of view of the API key that made the request, in the OneStream field shape that legacy clients already parse. It is the first call most integrations make after a base URL swap, because it confirms three things in one round trip: the panel accepted the token, the request reached the OneStream dialect, and the credits balance (when applicable) is the one you expected.

The response is a flat JSON object with five fields. The exact values you get back depend on the type of key you used, because the underlying v1 identity model distinguishes admin keys from reseller keys.

## Endpoint

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

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

None. Every authenticated key can call `/ext/profile`. Introspection has to work before the caller knows which scopes it has.

## Response

The response is a flat object with a fixed shape. It never returns a list.

| Field | Type | Description |
| ----- | ---- | ----------- |
| `id` | int | Numeric identity of the caller. For reseller keys the current release returns `0` because the underlying v1 `/me` endpoint carries the id as `reg_user_id`, which the dialect does not project into this field. For admin keys the value is also `0`. Treat this field as informational only and identify keys by the token itself. |
| `name` | string | Human-readable name of the caller. Empty string in the current release, for the same reason as `id`. |
| `username` | string | Same as `name`. Emitted twice under two names because different OneStream client libraries pick one or the other. |
| `is_admin` | bool | `true` for an admin key, `false` for a reseller key. In the current release this always returns `false` because the underlying `/me` endpoint uses `type: "admin"` instead of an `is_admin` boolean, and the dialect does not translate the two. Treat as informational. |
| `credits` | float | Current credit balance for the caller. For a reseller key this is the reseller's own balance. For an admin key it is `0`, because admin keys are panel-scoped and have no billing state. |

Example response (reseller key with 0.25 credits):

```json
{
  "id": 0,
  "name": "",
  "username": "",
  "is_admin": false,
  "credits": 0.25
}
```

Example response (admin key on a fresh panel):

```json
{
  "id": 0,
  "name": "",
  "username": "",
  "is_admin": false,
  "credits": 0
}
```

> [!NOTE]
> If your integration relies on `id`, `name`, `username`, or `is_admin` to identify the caller, use the native [`GET /panel-api/v1/me`](/docs/?page=xai-ref-me) endpoint instead. It returns `type: "admin" | "reseller"`, the reseller's `reg_user_id`, the member group, and the full billing snapshot. The OneStream `/ext/profile` shape is preserved for compatibility with client code that parses this exact set of five fields.

## Examples

### cURL

```bash
curl -H "X-Api-Key: <your-api-key>" \
     https://<your-panel-domain>/panel-api/onestream/ext/profile
```

### PHP raw

```php
$ch = curl_init('https://<your-panel-domain>/panel-api/onestream/ext/profile');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-Api-Key: <your-api-key>']);
$body = json_decode(curl_exec($ch), true);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo "credits: " . $body['credits'] . PHP_EOL;
```

### Python raw

```python
import requests

r = requests.get(
    "https://<your-panel-domain>/panel-api/onestream/ext/profile",
    headers={"X-Api-Key": "<your-api-key>"},
    timeout=30,
)
r.raise_for_status()
data = r.json()
print("credits:", data["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. |
| 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`. Cache the profile response, it does not change often. |
| 403 | `api_disabled` | An admin has turned the Panel API off for this panel. | Ask the admin to re-enable it in the panel settings. |

## See also

- [OneStream overview](/docs/?page=os-ref-overview)
- [Native identity endpoint](/docs/?page=xai-ref-me)
- [Authentication](/docs/?page=panel-api-authentication)
