Place Paper Order
This route places one paper trade by hand. No money moves, and you do not need a venue account. Rivo prices the order against the venue's live order book and folds the venue's taker fee into the price. It records only what fills right now, and nothing rests on the book.
To name the side, set market to a contract's ref and outcome to the side's name or label. You can find all three under outcomes[] on Get Market. You can also pass yes or no as outcome. The order comes back with the side's name.
- A buy spends
usd. A sell gives backcontracts, or"all"to sell everything held by hand on that side. maxPriceon a buy, orminPriceon a sell, fills only the part of the book inside that price. If nothing inside that price can fill, the API refuses the order and records nothing.- A sell closes only paper you bought by hand. It never touches a bot's holdings.
Retries
Send an Idempotency-Key header and reuse it when you retry. The same key always names the same trade. A retry returns the first fill with Idempotent-Replayed: true and never places a second trade. If the API refuses an order, you can use its key again. See Errors.
Example request
curl -X POST "https://api.rivo.markets/v2/paper-orders" \
-H "Authorization: Bearer rivo_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-2026-10-15-milwaukee-1" \
-d '{
"market": "a2Fsc2hpOm1hcmtldDpLWE1MQkdBTUUtMjZPQ1QxNDIwMDBNSUxMQUQtTUlM",
"outcome": "Milwaukee",
"action": "buy",
"usd": 250,
"maxPrice": 0.9
}'import requests
url = "https://api.rivo.markets/v2/paper-orders"
headers = {
"Authorization": "Bearer rivo_live_...",
"Idempotency-Key": "order-2026-10-15-milwaukee-1",
}
body = {
"market": "a2Fsc2hpOm1hcmtldDpLWE1MQkdBTUUtMjZPQ1QxNDIwMDBNSUxMQUQtTUlM",
"outcome": "Milwaukee",
"action": "buy",
"usd": 250,
"maxPrice": 0.9,
}
res = requests.post(url, headers=headers, json=body)
res.raise_for_status()
print(res.json())const res = await fetch("https://api.rivo.markets/v2/paper-orders", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.RIVO_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "order-2026-10-15-milwaukee-1",
},
body: JSON.stringify({
"market": "a2Fsc2hpOm1hcmtldDpLWE1MQkdBTUUtMjZPQ1QxNDIwMDBNSUxMQUQtTUlM",
"outcome": "Milwaukee",
"action": "buy",
"usd": 250,
"maxPrice": 0.9
}),
});
if (!res.ok) throw new Error(`Rivo API ${res.status}`);
const { data } = await res.json();Headers
Idempotency-KeystringAn optional unique value for each order, up to 200 characters. Reuse it when you retry. Rivo keeps it for 24 hours.
Body
marketmarket refrequiredOne contract's
ref. The API refuses a market with several contracts, so use areffrom itsoutcomes[]instead.outcomestringrequiredThe side's
outcomes[].nameoroutcomes[].label, oryesorno.actionstringrequiredWhether to buy or sell:
buyorsell.usdnumberThe dollars to spend, above 0 and up to 1,000,000. Send it on a buy, where it is required.
contractsnumber or "all"The number of contracts to sell, up to 10,000,000, or
"all". Send it on a sell, where it is required.maxPricenumberOn a buy, the most to pay for each contract, between 0 and 1.
minPricenumberOn a sell, the least to accept for each contract, between 0 and 1.
Example response
{
"data": {
"id": "5b7e0c1a-2f4d-4c8e-9a61-3d2f7b9e0a14",
"market": {
"ref": "a2Fsc2hpOm1hcmtldDpLWE1MQkdBTUUtMjZPQ1QxNDIwMDBNSUxMQUQtTUlM",
"contract": "a2Fsc2hpOm1hcmtldDpLWE1MQkdBTUUtMjZPQ1QxNDIwMDBNSUxMQUQtTUlM",
"title": "Milwaukee wins",
"venue": "kalshi",
"category": "sports",
"kind": "mlb"
},
"outcome": "Yes",
"action": "buy",
"contracts": 284,
"avgPrice": 0.8796,
"cost": 249.81,
"unfilledContracts": 0,
"filledAt": "2026-10-15T02:31:07.412Z"
}
}Response fields
dataobjectThe filled order.
Show 9 data fields
idstringThe paper order's id.
marketobjectThe market this row is about.
Show 6 market fields
refmarket refThe market's ref. You can pass it to any
marketfilter or to Get Market. On a market of several outcomes, it names the whole event.contractmarket ref or nullThe ref of the exact contract. It is the same as
refon a market with one contract. On a market of several outcomes, such as a game with a moneyline and a spread, it tells the contracts apart. Pass it to Place Paper Order or a market bot.titlestringThe market's title, as the venue writes it.
venuestringThe venue that lists the market: one of
kalshi,polymarket,polymarket_usorgemini.categorystring or nullThe market's category: one of
sports,politics,crypto,finance,culture. It isnullwhen Rivo has not classified the market.kindstring or nullThe kind of market, such as
nflorbitcoin. It isnullwhen Rivo has not classified the market yet.
outcomestringThe side traded, using the name that Get Market gives it.
actionstringWhether the order bought or sold:
buyorsell.contractsnumberThe number of contracts that filled.
avgPricenumberThe average price for each contract, from 0 to 1, with the venue's fee included.
costnumberWhat the buy cost, or what the sale brought in, in USD with the fee included.
unfilledContractsnumberThe contracts the book could not fill right away. Paper orders never rest on the book, so Rivo drops this part.
filledAtstringWhen the order filled, in ISO 8601 UTC.
Paper holdings settle when the market settles. You can see them in Get Paper Account.
Errors
| Code | When |
|---|---|
SCOPE_REQUIRED | The key does not have the write scope. |
VALIDATION | A required field is missing, or you sent a field that does not fit the action. market names a market with several contracts, or outcome is not one of the market's sides. You hold nothing by hand to sell, or nothing in the book fills inside your price limit. Or you already used the Idempotency-Key for a different order. |
NOT_FOUND | No market has that ref. |
IDEMPOTENCY_IN_PROGRESS | Rivo is still placing an order with the same Idempotency-Key. Wait, then retry with the same key. |