Markets
Trading-pair metadata: tick size, lot size, minimum notional, fees, listing status.
List Markets
Public — returns every trading pair with its full configuration.
GET /spot/markets
Response — 200 OK
[
{
"id": "DFUSDT",
"base_token": "DF",
"quote_token": "USDT",
"tick_size": "0.0001",
"lot_size": "0.01",
"min_notional": "1",
"maker_fee_bps": 0,
"taker_fee_bps": 0,
"status": "listed"
}
]
Fields
| Field | Type | Description |
|---|---|---|
id | string | Symbol the rest of the API uses (e.g. DFUSDT). |
base_token / quote_token | string | Token symbols. Order math: notional = price × quantity (in quote). |
tick_size | string | Allowed price increment. Orders with price % tick_size != 0 are rejected with INVALID_TICK. |
lot_size | string | Allowed quantity increment. Orders with quantity % lot_size != 0 are rejected with INVALID_LOT. |
min_notional | string | Minimum price × quantity for an order. Below → BELOW_MIN_NOTIONAL. |
maker_fee_bps / taker_fee_bps | number | In basis points (1 bps = 0.01%). MVP testnet defaults: both 0. |
status | string | listed (trading open) / halted (cancels OK, no new orders) / delisted (terminal — all open orders auto-canceled at transition). |
Admin: Create Market
Creates a new trading pair. Idempotent on id collision (returns 409 MARKET_EXISTS).
Requires X-API-Key header matching the server's ADMIN_API_KEY env var.
POST /admin/spot/markets
Request Body
{
"id": "DFUSDT",
"base_token": "DF",
"quote_token": "USDT",
"tick_size": "0.0001",
"lot_size": "0.01",
"min_notional": "1",
"maker_fee_bps": 0,
"taker_fee_bps": 0
}
| Field | Type | Required | Notes |
|---|---|---|---|
id | string | Yes | Unique. Convention: BASE + QUOTE. |
tick_size / lot_size / min_notional | string / number | Yes | Must be > 0. |
maker_fee_bps / taker_fee_bps | number | Yes | Must be >= 0. |
status is always listed on create. Use the status endpoint below to halt or delist.
Response — 200 OK
{ "ok": true, "id": "DFUSDT" }
After commit, the engine receives a ReloadMarket signal and refreshes its in-memory cache without restart.
Admin: Update Market Config
Patch any subset of tick_size, lot_size, min_notional, maker_fee_bps, taker_fee_bps. Omitted fields stay unchanged.
PATCH /admin/spot/markets/:id
Request Body (all optional)
{
"tick_size": "0.001",
"maker_fee_bps": 5,
"taker_fee_bps": 10
}
Response — 200 OK
{ "ok": true, "id": "DFUSDT" }
Existing open orders are not retroactively re-validated against new tick/lot/min_notional. Only new orders see the updated rules.
Admin: Change Status
Transitions: listed → halted, halted → listed, listed | halted → delisted. delisted is terminal.
PATCH /admin/spot/markets/:id/status
Request Body
{ "status": "halted" }
Status semantics
status | place new order | cancel existing | open book |
|---|---|---|---|
listed | accepted | accepted | unchanged |
halted | rejected MARKET_HALTED | accepted | preserved |
delisted | rejected MARKET_DELISTED | n/a | drained — all open orders auto-canceled with funds unfrozen, before status flip |
Response — 200 OK
{ "ok": true, "id": "DFUSDT", "status": "halted" }
Admin: Credit Balance (testnet only)
Direct off-chain credit to a user's spot wallet. Bypasses the on-chain deposit flow — used for seeding test accounts on testnet.
Returns 404 DISABLED unless server env has TESTNET_ONLY=true. Mainnet builds answer 404 even with valid auth.
POST /admin/spot/balances/credit
Request Body
{
"user_address": "0xab12...",
"token": "DF",
"amount": "10000",
"reason": "seed mm bot inventory"
}
| Field | Type | Required | Notes |
|---|---|---|---|
user_address | string | Yes | Lowercased server-side. |
token | string | Yes | DF or USDT. |
amount | string / number | Yes | Decimal, must be > 0. Added to available; frozen unchanged. |
reason | string | No | Free-text audit note. |
Both the balance update and an audit row in spot_admin_credits happen in a single DB transaction.
Response — 200 OK
{ "ok": true }
Code Example
import requests
BASE = "https://api-sepolia.p99.world/api/v1"
ADMIN_K = "your_admin_api_key"
# 1. Create the only market
requests.post(f"{BASE}/admin/spot/markets",
headers={"X-API-Key": ADMIN_K, "Content-Type": "application/json"},
json={"id": "DFUSDT", "base_token": "DF", "quote_token": "USDT",
"tick_size": "0.0001", "lot_size": "0.01", "min_notional": "1",
"maker_fee_bps": 0, "taker_fee_bps": 0})
# 2. Seed a test account (testnet only)
for token, amt in [("DF", "10000"), ("USDT", "10000")]:
requests.post(f"{BASE}/admin/spot/balances/credit",
headers={"X-API-Key": ADMIN_K, "Content-Type": "application/json"},
json={"user_address": "0xab12...", "token": token,
"amount": amt, "reason": "manual test"})
# 3. List what's live
print(requests.get(f"{BASE}/spot/markets").json())