Skip to content

X-Trader sniper configs API

The sniper configs API is the set of seven operations under /sniper/configs that manage the caller’s own sniper tasks: list, create, get, patch, delete, pause and resume. A configuration is always created paused (is_active=false) and starts executing only after POST /sniper/configs/{id}/resume. Reads need sniper:read, writes need sniper:write, and delete needs sniper:delete.

Primary source: X-Trader API reference. This page is an independent explanation of X-Trader Public API v1.0.0; it is not affiliated with X-Trader.

Method and path Purpose Scope Required headers Success
GET /sniper/configs List owned configurations sniper:read Authorization 200 list envelope
POST /sniper/configs Create a paused configuration sniper:write Authorization, Idempotency-Key 201 resource envelope
GET /sniper/configs/{id} Read one configuration sniper:read Authorization 200 resource envelope
PATCH /sniper/configs/{id} Update mutable fields sniper:write Authorization, If-Match 200 resource envelope
DELETE /sniper/configs/{id} Delete a paused configuration sniper:delete Authorization, If-Match 204 no body
POST /sniper/configs/{id}/pause Stop execution sniper:write Authorization, If-Match 200 resource envelope
POST /sniper/configs/{id}/resume Revalidate and start execution sniper:write Authorization, If-Match 200 resource envelope

Base URL: https://x-trader.ai/api/v1. Every operation also lists 400, 401, 403, 404, 409, 429, 500 and 503 as possible error statuses. Bulk delete does not exist.

Header Where Rule
Authorization All requests Bearer xt_live_<id>.<secret>. The user is derived from the token only.
Idempotency-Key POST /sniper/configs Required. 8-128 characters from A-Z a-z 0-9 . _ : -. Kept for 24 hours; the same key with a different body is rejected (idempotency_conflict).
If-Match PATCH, DELETE, pause, resume Required. The latest ETag of the resource, matching "v<number>", for example "v3". A stale value returns version_conflict.
ETag (response) 201 and 200 single-resource responses Current version. Send it as If-Match on the next write.
X-Request-Id (response) Single-resource and delete responses Request identifier for support.

General write rules (64 KiB body limit, decimal strings, strict objects) are covered on /xtrader/write-rules/.

Returns only configurations owned by the token’s user, ordered by updated_at and id so that cursor pagination is stable.

Query parameter Type Rule
limit integer 1-100, default 20
cursor string Max 2048 characters. Opaque next_cursor from the previous page, bound to the token and filters.
status string active or paused
q string Max 64 characters. Searches remark and target_wallet within the caller’s data.
label_id string A label UUID owned by the account, or the literal unlabeled. A foreign or missing UUID returns 404.

The response is { "data": { "items": [SniperResource...], "next_cursor": string or null }, "meta": { "request_id": ... } }. Restart from the first page after changing a filter. Details: /xtrader/pagination/.

Request body (SniperCreate, strict, unknown fields rejected; user_id, version and template are explicitly rejected):

Field Type Required Rule
remark string yes (default "") Max 64 characters, trimmed.
target_wallet string yes Solana address, 32-44 characters. Immutable after creation.
risk_label_id string (UUID) or null no Label owned by the same account. See /xtrader/labels/.
config object yes Complete sniper strategy. Field reference: /xtrader/config-sniper/.

Required keys inside config: buy_amount, jito_mode, priority_mode, selected_dexes, dex_source, buy_once, sol_slippage_mode, snipe_external, auto_sell, auto_strategy, timed_strategy. The sell-side objects are documented on /xtrader/auto-sell/, /xtrader/auto-strategy/ and /xtrader/timed-strategy/.

The spec describes target_wallet only as a valid Solana address; it does not define its role in sniper execution beyond that. A successful create returns 201 with is_active: false, an ETag (initially "v1" in the spec’s header example) and the stored resource. The new configuration counts toward the shared configuration quota reported by GET /me (quota.max, quota.used, quota.remaining), which sniper, copy-trade and transfer-snipe tasks share.

Terminal window
curl --request POST 'https://x-trader.ai/api/v1/sniper/configs' \
--header "Authorization: Bearer $X_TRADER_API_TOKEN" \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: create-sniper-20261016-001' \
--data '{
"remark": "Primary Pump sniper",
"target_wallet": "11111111111111111111111111111111",
"config": {
"buy_amount": "0.1",
"jito_mode": {"tip_amount": "0.01", "priority_fee": "0.0005", "slippage": "15", "bundle_amount": ""},
"priority_mode": {"tip_amount": "0.001", "priority_fee": "0.005", "slippage": "15", "bundle_amount": ""},
"selected_dexes": ["pump-fun"],
"dex_source": "global",
"dev_min_amount": "",
"dev_max_amount": "",
"buy_once": false,
"sol_slippage_mode": false,
"snipe_external": false,
"auto_sell": {
"enabled": false, "tab": "tpsl", "expiry_time": "10",
"tp_orders": [{"trigger": "90", "sell": "100"}], "sl_orders": [],
"follow_sell": false, "proportional_sell": false,
"trailing_take_profit": {"enabled": false, "tiers": []}
},
"auto_strategy": {"migration_clear": false, "timed_sell": false, "sell_delay": "30"},
"timed_strategy": {"enabled": false, "orders": []}
}
}'

Response 201 Created (headers ETag: "v1", X-Request-Id). IDs, timestamps and request ID are illustrative:

{
"data": {
"id": "3f2b8c1e-5d4a-4b7e-9c2f-1a6d8e0b4c37",
"remark": "Primary Pump sniper",
"target_wallet": "11111111111111111111111111111111",
"is_active": false,
"risk_label_id": null,
"version": 1,
"created_at": "2026-10-16T09:00:00Z",
"updated_at": "2026-10-16T09:00:00Z",
"config": { "buy_amount": "0.1", "...": "stored config as sent" }
},
"meta": {
"request_id": "req_0123456789abcdef",
"persistence": "committed",
"propagation": "eventual"
}
}

meta.persistence and meta.propagation are optional in the schema; when present, committed means the database write finished and eventual means execution nodes may need a short time to see it.

id is a UUID. A missing ID and an ID owned by another user both return 404 not_found. The 200 response carries the ETag needed for the next write. List and get return the stored snapshot as it is, so older configurations may contain legacy web-UI fields that the current create schema would not accept.

Body (SniperPatch): any non-empty subset of remark, risk_label_id, config. Rules:

  • At least one top-level field must be present.
  • target_wallet cannot be changed.
  • On an active configuration only remark and risk_label_id may change. Changing config requires the configuration to be paused, otherwise the request fails (the error enum provides config_must_be_paused for this case).
  • config, when present, replaces the whole stored config and must satisfy the full schema. Send the complete object, not a diff.
  • risk_label_id: null removes the label.

The operation description also says to pause before changing linked_wallets, but SniperPatch has no linked_wallets property; that field exists only on copy-trade.

POST /sniper/configs/{id}/pause sets the stored state to paused immediately; propagation to execution nodes is eventual. POST /sniper/configs/{id}/resume revalidates the complete stored config against the current schema before activating it. A stored config that no longer validates (for example legacy data) is not activated, and the request fails with a validation error. Neither call takes a body; both require If-Match and return the updated resource with a new ETag.

Terminal window
curl --request POST 'https://x-trader.ai/api/v1/sniper/configs/3f2b8c1e-5d4a-4b7e-9c2f-1a6d8e0b4c37/resume' \
--header "Authorization: Bearer $X_TRADER_API_TOKEN" \
--header 'If-Match: "v1"'

Response 200 OK (header ETag with the new version; values illustrative):

{
"data": {
"id": "3f2b8c1e-5d4a-4b7e-9c2f-1a6d8e0b4c37",
"remark": "Primary Pump sniper",
"target_wallet": "11111111111111111111111111111111",
"is_active": true,
"risk_label_id": null,
"version": 2,
"created_at": "2026-10-16T09:00:00Z",
"updated_at": "2026-10-16T09:01:10Z",
"config": { "buy_amount": "0.1", "...": "unchanged stored config" }
},
"meta": {
"request_id": "req_0123456789abcdef",
"persistence": "committed",
"propagation": "eventual"
}
}

Read the version from the ETag header rather than computing it; the spec only states that version is a positive concurrency version.

import os
import uuid
import requests
BASE = "https://x-trader.ai/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['X_TRADER_API_TOKEN']}"}
body = {
"remark": "Primary Pump sniper",
"target_wallet": "11111111111111111111111111111111",
"config": {
"buy_amount": "0.1",
"jito_mode": {"tip_amount": "0.01", "priority_fee": "0.0005", "slippage": "15", "bundle_amount": ""},
"priority_mode": {"tip_amount": "0.001", "priority_fee": "0.005", "slippage": "15", "bundle_amount": ""},
"selected_dexes": ["pump-fun"],
"dex_source": "global",
"dev_min_amount": "",
"dev_max_amount": "",
"buy_once": False,
"sol_slippage_mode": False,
"snipe_external": False,
"auto_sell": {
"enabled": False, "tab": "tpsl", "expiry_time": "10",
"tp_orders": [{"trigger": "90", "sell": "100"}], "sl_orders": [],
"follow_sell": False, "proportional_sell": False,
"trailing_take_profit": {"enabled": False, "tiers": []},
},
"auto_strategy": {"migration_clear": False, "timed_sell": False, "sell_delay": "30"},
"timed_strategy": {"enabled": False, "orders": []},
},
}
r = requests.post(f"{BASE}/sniper/configs", json=body,
headers={**HEADERS, "Idempotency-Key": f"sniper-{uuid.uuid4()}"})
r.raise_for_status()
config_id = r.json()["data"]["id"]
etag = r.headers["ETag"]
r = requests.post(f"{BASE}/sniper/configs/{config_id}/resume",
headers={**HEADERS, "If-Match": etag})
r.raise_for_status()
print(r.json()["data"]["is_active"], r.headers["ETag"])

Store the generated idempotency key with the request if you may need to retry it; a retry with the same key and the same body is safe.

Requires sniper:delete and If-Match. Only paused configurations can be deleted; an active one is refused. Success is 204 with no body.

  1. POST /sniper/configs creates the task paused and returns ETag.
  2. Inspect the returned resource, then POST .../resume with that ETag.
  3. To change strategy: POST .../pause, then PATCH with the full config and the latest ETag, then POST .../resume.
  4. remark and risk_label_id can be patched without pausing, but still need the latest If-Match.
  5. To remove: pause, then DELETE.

Every write returns a new ETag; always use the one from the latest response.

Success bodies have data (one SniperResource, or items plus next_cursor for lists) and meta.request_id. SniperResource fields: id, remark, target_wallet, is_active, risk_label_id, version, created_at, updated_at, config.

Errors use { "error": { "code", "message", "fields"? }, "request_id" }, where fields lists up to 20 { path, reason } validation failures. Branch on error.code. Codes most relevant to this resource:

Code Typical cause on /sniper/configs
validation_error Unknown property, JSON number instead of decimal string, out-of-range value, empty PATCH, failed revalidation on resume
insufficient_scope Token lacks sniper:read, sniper:write or sniper:delete
not_found Unknown ID, another user’s ID, or a foreign risk_label_id / label_id
config_must_be_paused config PATCH or delete on an active configuration
version_conflict If-Match is not the latest version; GET again before retrying
idempotency_required / idempotency_conflict Missing Idempotency-Key, or key reused with a different body
quota_exceeded Shared configuration quota reached
rate_limit_exceeded, service_unavailable 429 / 503, with Retry-After

The spec lists the error codes and the HTTP statuses per operation but does not publish a code-to-status table. See /xtrader/errors/.

Why is my new sniper config not running after create?

Section titled “Why is my new sniper config not running after create?”

Every create returns a configuration with is_active=false. Call POST /sniper/configs/{id}/resume with the ETag from the create response as If-Match to enable it.

Can I change the config of an active sniper?

Section titled “Can I change the config of an active sniper?”

No. While a sniper is active only remark and risk_label_id can be patched. Pause it first, then PATCH config, then resume.

Can I change target_wallet after creation?

Section titled “Can I change target_wallet after creation?”

No. target_wallet is immutable. Create a new configuration for a different address and delete the old one after pausing it.

sniper:read for list and get, sniper:write for create, patch, pause and resume, and sniper:delete for delete.

What does a PATCH with config do to fields I leave out?

Section titled “What does a PATCH with config do to fields I leave out?”

The config object in a PATCH replaces the stored config completely and must pass the full schema, so omitted nested fields are not merged from the old value.