home

API

Trading

Place, batch and cancel orders on Seeryx: limit, market and post-only orders, idempotency keys, expiry rules, split and merge, with code samples.

Overview

You trade with POST /api/orders (one order) or POST /api/orders/batch (up to 15), cancel with DELETE /api/orders/:id or DELETE /api/orders, and turn cash into pairs and back with /api/split and /api/merge. On seeryx.com every order, split and merge carries a signed object made by your trading session key: Signing orders shows how; the samples on this page do it for you.

Headers and idempotency

FieldTypeDescription
X-Auth-TokenheaderrequiredstringThe sign-in token (or Authorization: Bearer).
Idempotency-KeyheaderrequiredstringOn every POST and DELETE that trades: 8-128 letters, digits, _ or -, new for each intent. The same key with the same body answers what the first request answered and changes nothing; with another body it is refused. Keys are remembered for 24 hours.
Content-Typeheaderrequiredstringapplication/json with a body.

Order types

TypeFieldsWhat happens
Limitshares, limitTrades at once against what is offered at its price or better, and the rest rests in the book until it fills, you cancel it or the round closes.
Limit, post onlyshares, limit, postOnly: trueRests in full or is refused (400 would_take): it never takes liquidity, so it never pays the taker fee.
Market buyamount, capSpends up to amount USDC (fee included) at prices up to cap, at once; the rest is dropped, never rested.
Market saleshares, capSells up to shares at prices down to cap, at once; the rest is dropped.
FieldTypeDescription
slugrequiredstringA round that trades (live or upcoming) or an open event market.
siderequiredup | downThe outcome you trade (YES is up, NO is down in an event market).
actionrequiredbuy | sellA sale needs shares you hold that no other resting sale has reserved.
typerequiredlimit | marketSee above.
sharesoptionalnumberLimit orders and every sale.
amountoptionalnumberMarket buys: the most USDC to spend, taker fee included.
limitoptionalnumberLimit orders: the price in cents.
capoptionalnumberMarket orders: the worst price in cents. A buy pays no more, a sale takes no less.
postOnlyoptionalbooleanLimit orders only.
signedoptionalobjectThe order signed by your session key (Signing orders).
You want toBody (plus slug and signed)
Buy 10 Up at 45¢ or better, resting{"side":"up","action":"buy","type":"limit","shares":10,"limit":45}
Spend up to $5 on Down now, at most 60¢ a share{"side":"down","action":"buy","type":"market","amount":5,"cap":60}
Sell 10 Up now, for at least 40¢{"side":"up","action":"sell","type":"market","shares":10,"cap":40}
Offer 10 Up at 70¢, only as a maker{"side":"up","action":"sell","type":"limit","shares":10,"limit":70,"postOnly":true}

The answer

Responses
{ "order": 16901, "filled": 0, "remaining": 10, "spent": 0, "proceeds": 0 }

{ "order": null, "filled": 10.6844, "remaining": 0, "spent": 4.999974, "proceeds": 0,
  "requested": 5, "fee": 0.18515, "avgPrice": 45.064056006888556, "unspent": 0.000026, "status": "filled" }

{ "order": null, "filled": 0, "remaining": 0, "spent": 0, "proceeds": 0, "requested": 5, "fee": 0, "avgPrice": null,
  "unspent": 5, "status": "none", "reason": "beyond_limit", "limit": 1, "bestPrice": 16 }
A resting limit order; a market buy of $5 that filled; a market buy whose cap was below the best ask.
  • order is the id of what rests in the book, null when nothing rests. filled is shares traded now; spent and proceeds are USDC paid and received, fees included.
  • A market order adds requested, fee, avgPrice (cents, without the fee), unspent or unsold, status (filled, partial or none) and, when it did not fill in full, reason: no_liquidity, beyond_limit (with bestPrice) or share_limit.
  • A market order that fills nothing is still a 200, not an error.

Expiry and the close

  • Limit orders are signed until the market's close (the round's end, or an event's closesAt) and rest until then.
  • Market orders, splits and merges settle within seconds, so sign them for 5 minutes, never past the close. The server accepts an expiry from 5 seconds ahead up to the close.
  • The last 5 seconds of a round take no new orders (400 "Round is closing"). Resting orders expire at the close and show as type expire in your activity.
  • Rounds ahead. The next ten 5m and 15m rounds (one of 1h and 1d) already take orders, so a bot can quote a round before it starts.

Batches and cancels

POST /api/orders/batch places up to 15 orders in one request, in the order given, each with its own checks: the answer has one { status, body } per order, exactly what POST /api/orders would have said. Order i is recorded under <your key>-<i>, so retrying the whole batch with the same key places nothing twice. DELETE /api/orders cancels all your open orders, or with ?slug= those of one market, in one transaction.

curl
# two orders in one request (on seeryx.com each also carries its own "signed")
curl -s -X POST https://seeryx.com/api/orders/batch \
  -H "X-Auth-Token: $TOKEN" -H "Idempotency-Key: $(uuidgen)" -H "Content-Type: application/json" \
  -d '{"orders":[{"slug":"btc-updown-5m-1791916200","side":"up","action":"buy","type":"limit","shares":5,"limit":40},
                 {"slug":"btc-updown-5m-1791916200","side":"down","action":"buy","type":"limit","shares":5,"limit":40}]}'
# {"results":[{"status":200,"body":{"order":21306,...}},{"status":200,"body":{"order":21307,...}}]}

# cancel everything still resting in the round
curl -s -X DELETE "https://seeryx.com/api/orders?slug=btc-updown-5m-1791916200" -H "X-Auth-Token: $TOKEN" -H "Idempotency-Key: $(uuidgen)"
# {"success":true,"cancelled":[21306,21307]}

Split and merge

POST /api/split with { slug, amount } turns amount USDC into as many Up and Down shares; POST /api/merge with { slug, shares } turns pairs back into $1.00 each. No fee; at least 5 pairs; signed with a PMS1 message on seeryx.com.

Cash and limits

  • Cash. Resting buys hold no cash, but all your open buys in one market, with the taker fee at their limit, must fit your available cash. Cash moves only when an order fills; a buy you can no longer pay for is cancelled.
  • Your own orders. An order may trade with your own resting orders and then pays the taker fee like any other; post only refuses such an order instead.
  • Resting orders. At most 200 per account and 100 in one market (400 open_orders_limit).
  • Request rate. 250 order, cancel, split and merge requests per 10 seconds per account; each order of a batch counts (429 rate_limited). See Rate limits and errors.

Code

Limit, market and batch orders, cancels, a split and a merge, with seeryx.ts and chain.ts (or their Python twins) next to it.

trading.ts
// trading.ts: limit, market and batch orders, cancels, split and merge. npx tsx trading.ts
import { api, loadWallet, signIn, type Book, type Round } from './seeryx';
import { signOrder, signPair, tradingSession, type Funds, type Order } from './chain';

const wallet = await loadWallet();
await signIn(wallet);
const funds = await api<Funds>('/funds');
// where GET /api/funds says onChainTrading (always on seeryx.com) every order carries `signed`
const session = funds.mode === 'chain' && funds.onChainTrading ? await tradingSession(funds, wallet) : null;
const sign = async (order: Order) => (session ? signOrder(funds, session, wallet.address, order) : order);
const place = async (order: Order) =>
  api<{ order: number | null }>('/orders', { method: 'POST', body: await sign(order) });
const { slug } = await api<Round>('/markets/BTC/5m/current');
const up = await api<Book>(`/orderbook/${slug}?side=up`), down = await api<Book>(`/orderbook/${slug}?side=down`);
const below = (book: Book, cents: number) => Math.max(1, Math.floor(book.asks[0]?.[0] ?? 50) - cents); // under the best ask

// limit: buy 10 Up shares 5c under the best ask, resting in the book; postOnly refuses it if it would trade at once
const limit = await place({
  slug, side: 'up', action: 'buy', type: 'limit', shares: 10, limit: below(up, 5), postOnly: true,
});

// market: spend up to $5 on Down, paying at most 2c over the best ask; what the book cannot fill is dropped
const cap = Math.min(99, Math.ceil(down.asks[0]?.[0] ?? 50) + 2);
const market = await place({ slug, side: 'down', action: 'buy', type: 'market', amount: 5, cap });
console.log({ limit, market });

// up to 15 orders in one request, placed in this order; each answer is what POST /orders would have said
const orders = await Promise.all([
  sign({ slug, side: 'up', action: 'buy', type: 'limit', shares: 5, limit: below(up, 10) }),
  sign({ slug, side: 'down', action: 'buy', type: 'limit', shares: 5, limit: below(down, 10) }),
]);
console.log({ batch: await api('/orders/batch', { method: 'POST', body: { orders } }) });

// cancel one order, then everything still resting in this round
if (limit.order) await api(`/orders/${limit.order}`, { method: 'DELETE' });
console.log({ cancelled: await api(`/orders?slug=${slug}`, { method: 'DELETE' }) });

// split $5 into 5 Up + 5 Down shares, then merge them back into $5
const pair = (merge: boolean) =>
  (session ? signPair(funds, session, wallet.address, slug, merge, 5) : undefined);
await api('/split', { method: 'POST', body: { slug, amount: 5, signed: await pair(false) } });
await api('/merge', { method: 'POST', body: { slug, shares: 5, signed: await pair(true) } });

Order endpoints

Every request here needs X-Auth-Token and an Idempotency-Key header.

POST/api/orders

Place an order

Signed in + Idempotency-Key
FieldTypeDescription
slugbodyrequiredstringA round that trades (live or upcoming) or an open event market
sidebodyrequiredup | downThe outcome
actionbodyrequiredbuy | sellA sale needs shares you hold that no other sale has reserved
typebodyrequiredlimit | marketA limit order rests; a market order trades at once and drops the rest
sharesbodyoptionalnumberLimit orders and every sale: shares, up to 4 decimals
amountbodyoptionalnumberMarket buys: the most USDC to spend, taker fee included
limitbodyoptionalnumberLimit orders: the price in cents (see the price grid)
capbodyoptionalnumberMarket orders: the worst price in cents, required with signed orders
postOnlybodyoptionalbooleanLimit orders: refuse the order if any of it would trade at once
signedbodyoptionalobjectThe order signed by your trading session key
Request
curl -X POST "https://seeryx.com/api/orders" \
  -H "X-Auth-Token: $TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"slug":"btc-updown-5m-1791916200","side":"up","action":"buy","type":"limit","shares":10,"limit":45,"postOnly":true}'
Response
{ "order": 16901, "filled": 0, "remaining": 10, "spent": 0, "proceeds": 0 }
  • Where GET /api/funds says onChainTrading (always on seeryx.com) the body also carries "signed", made by your trading session key: see Signing orders.
  • order is the id of the part that rests in the book, null when nothing rests. filled is shares traded now; spent and proceeds are USDC paid and received, fees included.
  • A market order answers its result in full: requested, fee, avgPrice (cents, without the fee), unspent (a buy) or unsold (a sale), status "filled", "partial" or "none", and for less than a full fill a reason: "no_liquidity" (the book ran out), "beyond_limit" with bestPrice (the rest is priced past your cap) or "share_limit" (a market buy signed in shares got all of them below its cap). A market order that fills nothing is still a 200.
StatusWhen
400Invalid input ("Invalid limit", "Invalid slug"), "Round is not live", "Round is closing", "Insufficient balance", "Insufficient free shares", a signed order that does not match
400 below_minimumFewer shares than minOrderShares; the answer adds "minimum"
400 would_takeA post-only order that would trade at once
400 open_orders_limitToo many resting orders; the answer adds "limit": { account, round }
400 trading_pausedThe session key that signed it was closed: open a new session and sign again
429 rate_limitedMore than the order requests allowed per window; wait "retryAfter" seconds
503 trading_haltedTrading is halted for everyone (see GET /api/notice); cancels still work
503Exchange clock unavailable: the server cannot confirm the round time; try again in a few seconds
POST/api/orders/batch

Place up to 15 orders in one request

Signed in + Idempotency-Key
FieldTypeDescription
ordersbodyrequiredarray1 to 15 orders, each exactly the body of POST /api/orders with its own signed object
Request
curl -X POST "https://seeryx.com/api/orders/batch" \
  -H "X-Auth-Token: $TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"orders":[{"slug":"btc-updown-5m-1791916200","side":"up","action":"buy","type":"limit","shares":5,"limit":40},{"slug":"btc-updown-5m-1791916200","side":"down","action":"buy","type":"limit","shares":5,"limit":0}]}'
Response
{ "results": [{ "status": 200, "body": { "order": 21306, "filled": 0, "remaining": 5, "spent": 0, "proceeds": 0 } },
  { "status": 400, "body": { "error": "Invalid limit" } }] }
  • Placed in the order given, each with its own checks; a refused order does not stop the others. Each order counts as one request against the rate limit.
  • Order i is recorded under the idempotency key <your key>-<i> (i from 0; your key at most 125 characters), so retrying the whole batch places nothing twice.
StatusWhen
400orders is not a list of 1 to 15 orders
429 rate_limitedThe batch would pass the order requests allowed per window
DELETE/api/orders/:id

Cancel one open order

Signed in + Idempotency-Key
FieldTypeDescription
idpathrequirednumberOrder id
Request
curl -X DELETE "https://seeryx.com/api/orders/16901" \
  -H "X-Auth-Token: $TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"
Response
{ "success": true }
StatusWhen
400Order not found: not yours, or no longer open
DELETE/api/orders

Cancel all your open orders, or those of one market

Signed in + Idempotency-Key
FieldTypeDescription
slugqueryoptionalstringOnly this round or event market
Request
curl -X DELETE "https://seeryx.com/api/orders?slug=btc-updown-5m-1791916200" \
  -H "X-Auth-Token: $TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"
Response
{ "success": true, "cancelled": [21306, 21307] }
  • One request and one transaction, however many orders.
POST/api/split

Turn USDC into as many Up and Down shares

Signed in + Idempotency-Key
FieldTypeDescription
slugbodyrequiredstringA market that trades
amountbodyrequirednumberUSDC; 1 USDC makes 1 Up + 1 Down share
signedbodyoptionalobjectSigned by the session key (PMS1)
Request
curl -X POST "https://seeryx.com/api/split" \
  -H "X-Auth-Token: $TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"slug":"btc-updown-5m-1791916200","amount":5}'
Response
{ "success": true }
  • At least minOrderShares pairs. No fee.
StatusWhen
400 below_minimumFewer pairs than the minimum
400"Insufficient balance", a signed request that does not match
POST/api/merge

Turn pairs of Up and Down shares back into USDC

Signed in + Idempotency-Key
FieldTypeDescription
slugbodyrequiredstringA market that trades
sharesbodyrequirednumberPairs to merge; each pays $1.00
signedbodyoptionalobjectSigned by the session key (PMS1)
Request
curl -X POST "https://seeryx.com/api/merge" \
  -H "X-Auth-Token: $TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"slug":"btc-updown-5m-1791916200","shares":5}'
Response
{ "success": true }
  • At least minOrderShares pairs, or every pair you hold. No fee.