Skip to main content

Overview

Most order APIs give you a different endpoint per order type, so adding a stop-loss to a working integration means writing a new client method, new error handling, and new state tracking. Bravado does not work that way. Every order goes through one endpoint, POST /v2/trade/order, and a type field selects the execution strategy. A market order and a multi-hour TWAP are the same call with different fields. That design has a consequence worth understanding early: because the types behave very differently, the response you get back and the endpoint you later poll to find your order both change depending on the type you sent. Getting that wrong is the most common reason people think an order vanished. This guide places a real order, then walks all eight types, then covers where each one ends up.
Read time: about 18 minutes. Assumes you can make HTTP requests. No prediction market experience needed.

TL;DR

  • One endpoint, POST /v2/trade/order. The type field picks the strategy.
  • symbol is an outcome token, not a market. A binary market has two of them.
  • Prices are decimal probabilities: "0.62" means 62 cents. Sending "62" is rejected.
  • Size in shares with size, or spend a budget in dollars with quote_amount.
  • Three types return an order_id and live on GET /v2/trade/orders/open. Five return a record_id and live on GET /v2/trade/strategies. Poll the wrong one and your order looks missing.
  • Always send an Idempotency-Key, and always read warnings[] even on a 200.

What you will do

  • Verify your key has the right scope and your wallet has collateral
  • Place a market buy and read the fill back
  • Shape the same request into each of the eight order types
  • Attach take-profit and stop-loss legs to an entry
  • Find your order afterwards, whichever type you used
  • Cancel correctly, including the case where cancel-all does not do what you expect

What you will need

Knowledge
  • Comfort making HTTP requests from curl, Python, or TypeScript
  • No Polymarket background required, though how Polymarket works is useful context
Tools and access
  • A Bravado API key from the Bravado Portal
  • The trade.execute scope for placing orders, plus trade.cancel to cancel them
  • USDC collateral in your Bravado wallet
  • An outcome token id to trade, covered below

Check your account first

Two calls save a lot of confused debugging later. Does this key have permission?
Check trade.execute is in scopes. If onboarding_status is pending, your wallet is not fully provisioned and orders will fail regardless of collateral. The rate_limit_per_min value is worth keeping: it is your polling budget later, and reading it beats hard-coding a guess. Do you have money to spend?
pusd is what you can trade with immediately. usdc_e is bridged USDC.e sitting on Polygon that has not been deposited yet, so it does not count toward buying power.
Those values are strings, not numbers, and that is deliberate. Parsing them as floats reintroduces rounding error that eventually produces a size the venue rejects. Use Decimal, BigDecimal, or your language’s arbitrary-precision equivalent throughout. See Numeric conventions.

Understand what you are trading

This trips up nearly everyone once. A Polymarket market is a question: “Will X happen?” Each possible outcome is a separate ERC-1155 token with its own id and its own price. symbol refers to the outcome token, not the market. So a binary market gives you two symbols: one for YES, one for NO. Buying YES and selling NO are different orders on different symbols, not two sides of one instrument. Prices are decimal probabilities between 0.001 and 0.999. A YES token at 0.62 costs 62 cents per share and implies the market thinks there is roughly a 62% chance. If it resolves true, each share pays $1.

Place your first order

Spend $10 at whatever the book offers:
Expected response:
Reading that back: you spent $10 and received 16.129032 shares at an effective 62 cents each. If the market resolves in your favour those shares pay $1 each, so $16.13 against $10 spent.
Check warnings[] even when the status code is 200. It carries non-fatal problems that would otherwise pass silently: partial fills, prices clamped to a valid range, and bracket legs that could not be placed. An integration that only branches on status codes will misreport its own fills.

Sizing: shares or dollars

Two ways to express quantity, and mixing them up is a common early bug: You cannot sell dollars. A sell needs size, because you are disposing of a specific number of shares you hold.

The eight order types

Same endpoint throughout. Only the distinguishing fields are shown.
Sits on the book until filled or cancelled, and provides liquidity while it waits. Returns an order_id.Constraint: most markets require at least 5 shares on a resting order. size: "1" is rejected by the venue, not by Bravado.
Fills immediately against resting orders. Returns an order_id.Constraint: minimum spend of $1.Watch for: on a thin book, a large market order walks through several price levels and your average is worse than the price you saw. For anything substantial, see Execute a large position.
Splits a budget into clips executed at intervals. Returns a record_id, not an order_id.duration_sec and interval_sec are seconds, and interval_sec has a minimum of 10. price_tolerance_pct is a percent from 0 to 100 that skips clips when price has moved too far, which is your protection against filling into a spike.
Rests a large order while exposing only clip_size at a time. Returns a record_id.Constraint: slices are post-only. For a buy, the price must be at or below the current best bid, or the slice is rejected with order crosses book. Iceberg cannot be used to fill aggressively; it waits to be hit.
Tracks the best bid or ask as it moves, within a budget. Returns a record_id.offset_ticks is in CLOB ticks where one tick is 0.1 cents, so 2 quotes two ticks off the touch. price_ceiling stops it chasing the market up past a level you are unwilling to pay.
Returns a record_id. Before it triggers, this exists only on GET /v2/trade/strategies, because a sell below the market would fill instantly if it were resting on the book. Bravado holds it and places the order when the trigger hits, at which point it also appears on open orders with is_stop_loss: true.
Returns an order_id, unlike the other exit types. A sell above the current price can rest on the book straight away, so it does, and it earns you the spread while it waits.
Follows the price up and fires when it reverses by your offset. Returns a record_id.Units matter here. trailing_offset is a decimal probability, so "0.05" trails by 5 cents. If you want a percentage, use trailing_offset_pct, which takes 0 to 100. Sending "5" to trailing_offset is rejected for falling outside the valid range.

Attach exits at entry

Rather than placing an exit after your entry fills, attach both legs to the entry itself:
When the entry fills, both legs are submitted automatically. This closes the gap where you hold an unprotected position between the fill and your exit order landing.
Brackets are best-effort. If the entry fills but a leg cannot be placed, the entry is not rolled back. You hold the position with no exit attached, and the only sign is in the response body, not the status code.
Always inspect both legs:
The usual cause is a market minimum: a bracket leg below 5 shares fails even though the entry succeeded.

Find your order afterwards

This is the part that generates the most confusion, so it is worth a table: Two rows deserve attention. Iceberg appears in both places at once: the parent is a strategy, the currently resting slice is a CLOB order. And stop-loss migrates from one list to the other when it fires. So a client that polls only open orders will report a running TWAP as missing. Poll both:
A strategy sitting in PENDING has not been rejected. It has been accepted and is waiting for its entry conditions. Cancelling and re-placing on PENDING churns fees and stops the strategy ever working. See Track order and strategy state.

Cancel correctly

POST /v2/trade/orders/cancel-all clears CLOB orders only. Running strategies continue afterwards. A “cancel everything” button wired to just that endpoint leaves a TWAP quietly executing, which is a genuinely bad surprise for someone who believes they are flat.

Confirm the position

Cost basis comes back computed. You do not need to reconstruct it from your own fill history, and you should not try: splits, merges, and redemptions are not ordinary buys and sells, and treating them as such produces a basis that disagrees with the chain.

Wrapping up

One endpoint, eight strategies, and the field set changes with the type. The two things worth carrying forward are that symbol is an outcome and not a market, and that where your order lives afterwards depends on the type you sent. Everything else is detail you can look up. Those two cause the bugs.

Frequently asked questions

Prices are decimal probabilities between 0.001 and 0.999. Sending "62" instead of "0.62" puts you outside the range. The error message names the value it received and suggests the decimal you probably meant.
Most markets require at least 5 shares on a resting order, and market orders need $1 minimum notional. Both are venue rules enforced by the CLOB, not Bravado validation.
Nowhere. Managed strategies live on GET /v2/trade/strategies, not open orders. Only LIMIT, MARKET, and TAKE_PROFIT appear in open orders.
Iceberg slices are post-only. For a buy, the price has to be at or below the current best bid so the slice provides liquidity rather than taking it. Price it above and it is rejected.
You should send one on every mutating request. Without it, a request that times out leaves you unable to tell whether the order was placed, and retrying risks a duplicate position. See Safe retries.
Yes, POST /v2/trade/order/batch submits several at once, and POST /v2/trade/orders/cancel-batch cancels a targeted set. Both still take an idempotency key.
Resolution settles outcome tokens at $1 or $0. An unfilled order on a resolved market will not fill, and a stop that never triggered is not protection. Review open exits as an event approaches. See UMA resolution.

Resources