Agara

Orders

Place, list, look up, and cancel orders.

Place an order

POST /trade/v1/orders

Scope: orders:place

Request

{
  "token_id": "21742633143463906290569050155826241533067272736897614950488156847949938836455",
  "side": "BUY",
  "type": "LIMIT",
  "time_in_force": "GTC",
  "price_micro": "600000",
  "shares_micro": "1000000",
  "post_only": false
}
FieldTypeRequiredNotes
token_idstringyesMarket outcome — discover via Markets
sideBUY | SELLyes
typeLIMIT | MARKETyesMARKET requires FAK or FOK time-in-force; GTC/GTD reject
time_in_forceGTC | FAK | FOK | GTDyesSee time-in-force
price_microstringfor LIMITLimit price in millionths of one collateral unit per share. Number also accepted; see Conventions
shares_microstringdependsRequired for limit BUYs and every SELL; forbidden for market BUYs. Number also accepted
collateral_amount_microstringdependsRequired for market BUYs; forbidden for limit BUYs and every SELL. Number also accepted
post_onlyboolnoReject if the order would take liquidity. See post-only
expiration_unix_secondsintegerfor GTDWhen the order auto-expires

Every field, response shape, and status code for these endpoints is in the Orders API reference.

The order shape determines the amount field. A limit order uses shares_micro; a market BUY uses collateral_amount_micro; a market SELL uses shares_micro. Limit orders require price_micro, while market orders forbid it.

Response

{
  "order_id": "9c4a3e7d-12b4-4f8e-9a3c-d2c7f0a45e1b",
  "source": "AGARA",
  "status": "PENDING",
  "pending_operation": "SUBMIT",
  "as_of": "2026-05-12T10:23:45.678Z"
}

HTTP 202 — we've accepted the order, but the match hasn't happened yet. Poll GET /trade/v1/orders/{order_id} to watch status.

Errors

StatusReason
400Body malformed, both amounts set, GTD with no expiration_unix_seconds or one less than 30 seconds ahead, order notional outside the beta limits, or the market isn't accepting orders
401Token missing, invalid, expired, or revoked
403Token missing orders:place
404The wallet isn't registered for this exchange
422We checked your order against your live balance and positions and it can't be placed right now — not enough collateral, not enough shares, a post-only order that would cross, or a FOK that can't fully fill. The error field carries the specific reason
424Wallet setup or signer authorization isn't complete

The 422 check is best-effort and runs before we accept the order, so an order that clearly can't fill is rejected immediately instead of after the fact. It's not a guarantee: your balance or the book can move between the check and the match, so an order can still be accepted (202 / PENDING) and then land as a REJECTED order with the reason in its error field. When that happens you'll also get an order_rejected event on the account stream — or poll GET /trade/v1/orders/{id} until the status is terminal.

Examples

curl -X POST "$AGARA_BASE_URL/trade/v1/orders" \
  -H "Authorization: Bearer $AGARA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "token_id": "2174…36455",
    "side": "BUY",
    "type": "LIMIT",
    "time_in_force": "GTC",
    "price_micro": "600000",
    "shares_micro": "1000000"
  }'
import os, requests
 
resp = requests.post(
    f"{os.environ['AGARA_BASE_URL']}/trade/v1/orders",
    headers={"Authorization": f"Bearer {token}"},
    json={
        "token_id": token_id,
        "side": "BUY",
        "type": "LIMIT",
        "time_in_force": "GTC",
        "price_micro": str(int(0.60 * 1_000_000)),
        "shares_micro": str(1 * 1_000_000),
    },
)
resp.raise_for_status()
order_id = resp.json()["order_id"]

List your orders

POST /trade/v1/orders/list

Scope: orders:read

Request

{ "limit": 100 }

Both limit and cursor are optional; omit cursor for the first page. See pagination.

Response

{
  "orders": [ /* see "Look up one order" below */ ],
  "markets": { /* token_id → market metadata; see "Look up one order" */ },
  "pagination": {
    "next_cursor": "eyJ2IjoxLCJyIjoib3JkZXJzL2xpc3Qi…",
    "limit": 100
  },
  "as_of": "2026-05-12T10:23:45.678Z"
}

Orders are sorted newest-first. Both open and terminal orders come back — filter on status if you only want one or the other. To walk the full list, pass pagination.next_cursor back as cursor until it comes back null.

Look up one order

GET /trade/v1/orders/{order_id}

Scope: orders:read

Response

{
  "order": {
    "internal_id": "9c4a3e7d-12b4-4f8e-9a3c-d2c7f0a45e1b",
    "exchange": "AGARA",
    "token_id": "2174…36455",
    "condition_id": "0x2174…",
    "side": "BUY",
    "type": "LIMIT",
    "price_micro": "600000",
    "original_size_micro": "1000000",
    "collateral_amount_micro": "600000",
    "size_matched_micro": "400000",
    "avg_fill_price_micro": "595000",
    "status": "PARTIALLY_FILLED",
    "error": null,
    "expiration": "2026-05-12T10:53:45.678Z",
    "created_at": "2026-05-12T10:23:45.678Z",
    "cancel_requested_at": null
  },
  "markets": {
    "2174…36455": {
      "market_id": "1f9e566d-7823-49fb-a5c5-5a193eba3209",
      "market_title": "Will it rain in NYC on May 12?",
      "outcome_name": "Yes",
      "logo_url": null,
      "event_slug": "nyc-weather-may-12",
      "display": { "market": {}, "outcome": {} }
    }
  }
}

Both this endpoint and the list endpoint return a markets sidecar: a map keyed by token_id carrying the market_id, market_title, outcome_name, logo_url, and event_slug for each order's market, so you can label and link orders without a second lookup.

FieldWhat it tells you
internal_idThe order's UUID — pass it to GET /trade/v1/orders/{order_id}
exchangeThe exchange the order routed to; these docs cover AGARA
typeLIMIT or MARKET
original_size_microThe size you placed, in micro units. null for market BUYs (which are sized in collateral)
size_matched_microShares matched so far, in micro units
avg_fill_price_microVolume-weighted average price of the fills so far. null before the first fill
statusWhere the order is in its lifecycle. See status
errorFree-text reason if the order was rejected; null otherwise
expirationExpiration timestamp — meaningful for GTD orders
cancel_requested_atWhen a durable cancellation request was recorded; null if none

Errors

StatusReason
400Order doesn't exist or isn't yours

Look up an order by hash

GET /trade/v1/orders/by-hash/{order_hash}

Scope: orders:read

For signed orders you know the order_hash before you submit, so this is how you find an order when you have its hash but not its order_id — you missed the placement response, you got a 409 resubmitting, or you're reconciling after a reconnect. Returns the same order object as Look up one order.

Errors

StatusReason
404No order with this hash, or it isn't yours

List an order's fills

GET /trade/v1/orders/{order_id}/trades

Scope: orders:read

Every fill for one order, newest-first, returned in full — there's no pagination here, since a single order rarely has many fills. Each row is your order's own side of the fill: an order can rest as the maker on some fills and cross as the taker on others, so role is per-fill, and side, price_micro, and fee_micro are that leg's values.

Response

{
  "trades": [
    {
      "exchange": "AGARA",
      "trade_id": "5f1c8b20-7e3a-4d61-9c0f-1a2b3c4d5e6f",
      "fill_id": "4187342",
      "order_id": "9c4a3e7d-12b4-4f8e-9a3c-d2c7f0a45e1b",
      "token_id": "2174…36455",
      "side": "BUY",
      "shares_micro": "300000",
      "price_micro": "596000",
      "fee_micro": "1430",
      "role": "TAKER",
      "status": "MATCHED",
      "transaction_hash": null,
      "executed_at": "2026-05-12T10:24:01.123Z"
    },
    {
      "exchange": "AGARA",
      "trade_id": "8a2d4f10-3b6c-4e90-8d12-0f9e8a7b6c5d",
      "fill_id": "4187280",
      "order_id": "9c4a3e7d-12b4-4f8e-9a3c-d2c7f0a45e1b",
      "token_id": "2174…36455",
      "side": "BUY",
      "shares_micro": "100000",
      "price_micro": "592000",
      "fee_micro": "0",
      "role": "MAKER",
      "status": "MATCHED",
      "transaction_hash": null,
      "executed_at": "2026-05-12T10:23:58.004Z"
    }
  ],
  "as_of": "2026-05-12T10:24:05.000Z"
}

Each row is the shared Fill shape, so it carries the same fields as /trade/v1/portfolio/trades.

FieldWhat it tells you
roleWhether your order was the MAKER or TAKER on this fill
sideYour order's side on this fill — BUY or SELL
order_idThe order this fill belongs to (the one you looked up)
price_microThe price your order filled at on this fill
fee_microFee charged to your order for this fill
statusSettlement state of this fill
transaction_hashOn-chain settlement tx; null until settled (and currently always null on this endpoint — use /trade/v1/portfolio/trades for the hash)
fill_idStable id for this fill — matches the fill_id on the live trade stream

For fills across your whole account rather than one order, use GET /trade/v1/portfolio/trades.

Errors

StatusReason
400Order doesn't exist or isn't yours

Cancel an order

DELETE /trade/v1/orders/{order_id}

Scope: orders:cancel

Response

{
  "order_id": "9c4a3e7d-12b4-4f8e-9a3c-d2c7f0a45e1b",
  "pending_operation": "CANCEL",
  "as_of": "2026-05-12T10:25:00.000Z"
}

HTTP 202 — cancellation is processed asynchronously. Poll the order detail to confirm the status becomes CANCELLED.

Errors

StatusReason
400Order doesn't exist or isn't yours, or it's no longer active (already terminal, or its placement is still in flight)

Cancel everything open

POST /trade/v1/orders/cancel-all

Scope: orders:cancel_all

Cancels every open order on your account. No request body.

Response

{
  "wallet_ids": ["e7a1…"],
  "pending_operation": "CANCEL_ALL",
  "as_of": "2026-05-12T10:25:00.000Z"
}

Cancellations are dispatched as a background job — poll POST /trade/v1/orders/list, paging through with cursor until next_cursor is null, and confirm nothing remains in OPEN or PARTIALLY_FILLED.

Errors

StatusReason
403Token missing orders:cancel_all

Time in force

ValueBehavior
GTCGood-til-cancelled. Rests on the book until filled or cancelled
GTDGood-til-date. Same as GTC but auto-cancels at (or shortly after) expiration_unix_seconds, which must be at least 30 seconds in the future
FAKFill-and-kill. Matches whatever's available immediately, cancels any remainder
FOKFill-or-kill. Either fully fills right now or doesn't place at all

Post-only

Set post_only: true if your strategy depends on resting on the book (maker side) rather than taking liquidity. If your order would cross the spread, it's rejected and nothing rests. Most of the time this comes back immediately as a 422 with post-only order would cross the book in the error field; if the book moves after that check, it can instead land as a REJECTED order (an order_rejected event, or poll GET /trade/v1/orders/{id}).

Use this when your strategy must never take liquidity or pay a taker fee.

Order size limits

While we're in beta, every order is bounded on entry. Limit orders and market buys are bounded by notional in collateral units; market sells are bounded by share count, because the fill price isn't known at placement.

OrderBounded byMinMax
Limit (buy or sell)notional (price × shares)0.10 collateral units100,000 collateral units
Market buynotional (the collateral_amount you supply)0.10 collateral units100,000 collateral units
Market sellshare count1.0 share100,000 shares

Orders outside their range reject with 400 invalid order request before they reach the matching engine. Notional rejections read order notional is below the $0.10 beta minimum / …exceeds the $100,000 beta maximum; market-sell rejections read market sell size is below the 1.0-share minimum / …exceeds the 100,000-share maximum.

Validate these bounds before submitting. Track this page for changes.

Common patterns

Place and wait for terminal status:

order_id = place_order(...)
TERMINAL = {"MATCHED", "CANCELLED", "EXPIRED", "REJECTED", "FAILED"}
 
while True:
    order = get_order(order_id)
    if order["status"] in TERMINAL:
        break
    time.sleep(0.5)

Replace a resting bid (no atomic replace — cancel then re-place):

cancel(old_order_id)
wait_until_cancelled(old_order_id)
new_order_id = place_order(...)

Take only, never rest: use time_in_force: "FAK" — any unfilled portion vanishes instead of resting.

Maker only: post_only: true. If the order would cross, it's normally rejected immediately with 422. If the book changes after that check, the accepted order may instead become REJECTED; back off and re-quote.