# X-Trader Public API: what it does and how to authenticate

URL: https://docs.blizz.id/xtrader/overview/
Updated: 2026-10-16

The X-Trader Public API v1.0.0 is a REST interface for managing the caller's own sniper, copy-trade and transfer-snipe configurations, sniper and copy-trade templates, and a shared set of task labels. It never exposes wallet keys, trade exits, fund transfers or administrative capabilities. Every request is authenticated with a personal bearer token of the form `xt_live_<public_id>.<secret>`, and the account is derived from that token alone.

Primary source: [X-Trader API reference](https://x-trader.ai/api/docs). This page is an independent explanation of X-Trader Public API v1.0.0; it is not affiliated with X-Trader.

## Scope of the API

| Managed by the API | Not available through the API |
|---|---|
| Sniper configurations (`/sniper/configs`) | Wallet private keys |
| Copy-trade configurations (`/copy-trade/configs`) | Executing or closing trades (exits) |
| Transfer-snipe tasks (`/transfer-snipe/configs`), one watched wallet per task | Fund transfers |
| Sniper templates (`/sniper/templates`) | Administrative functions |
| Copy-trade templates (`/copy-trade/templates`) | Native backend (Supabase) APIs |
| Shared labels (`/labels`) | Any other user's resources |
| Credential metadata (`/me`) | Bulk delete |

There is no transfer-snipe template endpoint in v1.0.0. Configurations are always created paused and must be resumed explicitly (see [write rules](/xtrader/write-rules/)).

## Base URL and transport

| Item | Value |
|---|---|
| Base URL | `https://x-trader.ai/api/v1` (the only server listed, marked Production) |
| Format | JSON request and response bodies |
| Auth header | `Authorization: Bearer <token>` on every request |
| Response envelope | `{"data": ..., "meta": {"request_id": ...}}` on success, `{"error": ..., "request_id": ...}` on failure |

Write responses carry `meta.persistence = "committed"` (the database write finished) and `meta.propagation = "eventual"` (execution nodes pick the change up shortly after).

## Token format and creation

The security scheme is HTTP bearer with `bearerFormat` `xt_live_<public_id>.<secret>`.

| Part | Meaning |
|---|---|
| `xt_live_` | Fixed prefix for live tokens |
| `<public_id>` | Non-secret identifier. `GET /me` returns it as `token_prefix`, matching `^xt_live_[a-f0-9]{8}$` (8 lowercase hex characters) |
| `.` | Separator |
| `<secret>` | Secret part. Shown only once |

Tokens are created in the X-Trader web app under **Settings, Developer API**. The documented setup steps are:

1. Sign in and open the Developer API settings.
2. Select only the scopes the integration needs.
3. Add an IP allowlist and an expiration date whenever possible.
4. Copy the token into a server-side secret store immediately; X-Trader cannot show it again.
5. Call `GET /me` to verify it.

The spec advises keeping tokens in trusted server-side code only: never in browser JavaScript, mobile bundles, URLs, logs, Git repositories or third-party proxies.

## Scopes

A token carries up to 9 scopes. Each operation declares a required scope.

| Scope | Allows |
|---|---|
| `sniper:read` | List and get sniper configs and sniper templates; list and get labels |
| `sniper:write` | Create, update, pause and resume sniper configs; create and update sniper templates; create, update and delete labels |
| `sniper:delete` | Delete sniper configs and sniper templates |
| `copytrade:read` | List and get copy-trade configs and copy-trade templates; list and get labels |
| `copytrade:write` | Create, update, pause and resume copy-trade configs; create and update copy-trade templates; create, update and delete labels |
| `copytrade:delete` | Delete copy-trade configs and copy-trade templates |
| `transfersnipe:read` | List and get transfer-snipe tasks |
| `transfersnipe:write` | Create, update, pause and resume transfer-snipe tasks |
| `transfersnipe:delete` | Delete transfer-snipe tasks |

Notes derived from the per-operation requirements:

- Label endpoints accept either the sniper or the copy-trade scope (`sniper:read` or `copytrade:read` for reads, `sniper:write` or `copytrade:write` for writes, including `DELETE /labels/{id}`). The spec does not list any `transfersnipe:*` scope for label endpoints, so a token with only transfer-snipe scopes cannot manage labels.
- Deleting a label needs a `:write` scope, not a `:delete` scope.
- `GET /me` declares no specific scope; any valid token can call it.
- A call without the required scope fails with `insufficient_scope` (see [errors](/xtrader/errors/)).

## GET /me

`GET /me` returns metadata about the credential used for the request. It returns no internal user identifier. It is the recommended first call after creating or rotating a token.

| Field | Type | Description |
|---|---|---|
| `token_prefix` | string, `^xt_live_[a-f0-9]{8}$` | Non-secret identifier of this credential |
| `token_version` | integer, greater than 0 | Credential version; rotation increments it |
| `scopes` | array of scope strings (max 9) | Exact permissions granted to this token |
| `expires_at` | date-time or `null` | Expiry timestamp; `null` means the token does not expire |
| `quota.max` | integer | Maximum number of configurations for the account |
| `quota.used` | integer | Current number of sniper, copy-trade and transfer-snipe configurations combined |
| `quota.remaining` | integer | Configurations that can still be created |
| `login_username` | string or `null` (default `null`) | Login name of the account; the spec gives no further description |
| `family_role` | enum `none`, `primary`, `member` (default `none`) | Account family role; the spec gives no further description |

The quota is shared: sniper, copy-trade and transfer-snipe configurations all count against the same `max`. Templates and labels have their own separate limits (1,000 templates shared by sniper and copy-trade, 20 labels).

```bash
export X_TRADER_API_TOKEN='xt_live_xxxxxxxx.your_secret'

curl 'https://x-trader.ai/api/v1/me' \
  --header "Authorization: Bearer $X_TRADER_API_TOKEN"
```

```python
import os
import requests

BASE = "https://x-trader.ai/api/v1"
headers = {"Authorization": f"Bearer {os.environ['X_TRADER_API_TOKEN']}"}

r = requests.get(f"{BASE}/me", headers=headers, timeout=10)
r.raise_for_status()
me = r.json()["data"]
print(me["token_prefix"], me["scopes"])
print("quota remaining:", me["quota"]["remaining"], "of", me["quota"]["max"])
```

## IP allowlist and expiry

A token can be restricted to an IP allowlist and given an expiration date when it is created. The spec recommends both. A request from an address outside the allowlist is rejected with `ip_not_allowed`. The expiry is visible in `expires_at`. The spec does not document the allowlist format, whether these settings can be edited after creation, or which error code an expired token returns. As practical guidance, treat `invalid_api_token` as the likely result for a token that is no longer accepted and check `expires_at` proactively.

## Rotation and revocation

- Rotate or revoke the token immediately if exposure is suspected.
- Rotation invalidates the previous token at once; there is no grace period in the documented behaviour.
- After rotation, `token_version` is incremented. Call `GET /me` with the new token to confirm it.

Rotation and revocation are performed in the web app; the API has no endpoint for them.

## Identity and ownership isolation

- No request body, query parameter or path accepts `user_id`; sending it in a body is rejected.
- Every list returns only the token owner's resources.
- A config, template or label id that belongs to another account returns `404 not_found`, exactly like a missing id. This also applies to a foreign `risk_label_id` in a request body and a foreign `label_id` list filter.

## Endpoint map

| Method and path | Scope | Purpose | Page |
|---|---|---|---|
| `GET /me` | any | Credential metadata and shared quota | this page |
| `GET /sniper/configs` | `sniper:read` | List sniper configs | [sniper](/xtrader/sniper/) |
| `POST /sniper/configs` | `sniper:write` | Create a paused sniper config | [sniper](/xtrader/sniper/) |
| `GET /sniper/configs/{id}` | `sniper:read` | Get one sniper config | [sniper](/xtrader/sniper/) |
| `PATCH /sniper/configs/{id}` | `sniper:write` | Update a sniper config | [sniper](/xtrader/sniper/) |
| `DELETE /sniper/configs/{id}` | `sniper:delete` | Delete a paused sniper config | [sniper](/xtrader/sniper/) |
| `POST /sniper/configs/{id}/pause` | `sniper:write` | Pause | [sniper](/xtrader/sniper/) |
| `POST /sniper/configs/{id}/resume` | `sniper:write` | Revalidate and resume | [sniper](/xtrader/sniper/) |
| `GET, POST /copy-trade/configs` | `copytrade:read`, `copytrade:write` | List, create | [copy-trade](/xtrader/copy-trade/) |
| `GET, PATCH, DELETE /copy-trade/configs/{id}` | `copytrade:read`, `:write`, `:delete` | Get, update, delete | [copy-trade](/xtrader/copy-trade/) |
| `POST /copy-trade/configs/{id}/pause`, `/resume` | `copytrade:write` | Pause, resume | [copy-trade](/xtrader/copy-trade/) |
| `GET, POST /transfer-snipe/configs` | `transfersnipe:read`, `transfersnipe:write` | List, create | [transfer-snipe](/xtrader/transfer-snipe/) |
| `GET, PATCH, DELETE /transfer-snipe/configs/{id}` | `transfersnipe:read`, `:write`, `:delete` | Get, update, delete | [transfer-snipe](/xtrader/transfer-snipe/) |
| `POST /transfer-snipe/configs/{id}/pause`, `/resume` | `transfersnipe:write` | Pause, resume | [transfer-snipe](/xtrader/transfer-snipe/) |
| `GET, POST /sniper/templates` | `sniper:read`, `sniper:write` | List, create | [templates](/xtrader/templates/) |
| `GET, PATCH, DELETE /sniper/templates/{id}` | `sniper:read`, `:write`, `:delete` | Get, update, delete | [templates](/xtrader/templates/) |
| `GET, POST /copy-trade/templates` | `copytrade:read`, `copytrade:write` | List, create | [templates](/xtrader/templates/) |
| `GET, PATCH, DELETE /copy-trade/templates/{id}` | `copytrade:read`, `:write`, `:delete` | Get, update, delete | [templates](/xtrader/templates/) |
| `GET, POST /labels` | `sniper:*` or `copytrade:*` read / write | List, create | [labels](/xtrader/labels/) |
| `GET, PATCH, DELETE /labels/{id}` | read / write | Get, update, delete | [labels](/xtrader/labels/) |

Related reference pages: [write rules](/xtrader/write-rules/), [errors](/xtrader/errors/), [pagination](/xtrader/pagination/), and the config field references [sniper config](/xtrader/config-sniper/), [copy-trade config](/xtrader/config-copy-trade/), [auto-sell](/xtrader/auto-sell/), [auto strategy](/xtrader/auto-strategy/), [timed strategy](/xtrader/timed-strategy/).

## FAQ

### What can the X-Trader Public API change?

Only the caller's own sniper, copy-trade and transfer-snipe configurations, sniper and copy-trade templates, and shared labels. Wallet keys, trade exits, fund transfers and administrative functions are not exposed.

### What does an X-Trader API token look like?

A personal token has the form `xt_live_<public_id>.<secret>` and is sent as `Authorization: Bearer <token>`. The full token is shown once at creation and cannot be recovered.

### Which call should I make first?

`GET /me`. It confirms the token works and returns its prefix, version, scopes, expiry and the shared configuration quota.

### Why does a config id from another account return 404?

The user is derived only from the token. An id owned by someone else returns the same `404 not_found` as an id that does not exist, so existence is never disclosed.

### What happens to the old token when I rotate it?

Rotation invalidates the previous token immediately and increments `token_version`. Update every client before or right after rotating.
