Trading API
Prefer a command-line tool? Check out the community-built Olympus CLI: github.com/fciaf420/olympus-cli.
Use the Olympus Trading API to read your pUSD balance, positions, and equity in one call, or to queue buy and sell trades from your selected Olympus wallet, including buys with built-in stop losses and take profits.
Create an API key
Log in to create a wallet-scoped API key. Keys can trade only from the wallet you select.
Authentication
Create an API key while logged in above. Each key is linked to one wallet:
- Olympus Wallet by default โ your current Olympus-created EOA/Safe/deposit wallet.
- Imported wallet when you select one before creating the key.
The plaintext key is shown only once. After creation, the UI displays only the first four characters and lets you delete the key.
Send the key as a bearer token:
Authorization: Bearer YOUR_API_KEY
You can also send it with X-API-Key, but Authorization: Bearer is preferred.
Base URL
Use the Olympus production API host:
https://api.olympusx.app
Rate limits
Rate limits are enforced per API key:
| Endpoint | Limit |
|---|---|
GET /v1/top-wallets | 60 requests per minute |
GET /v1/wallets/:address | 30 requests per minute |
GET /v1/portfolio | 30 requests per minute |
GET /v1/trades/:tradeId | 100 requests per minute |
POST /v1/trade | 60 requests per minute |
When you exceed a limit, Olympus returns 429 Too Many Requests with a Retry-After header in seconds.
Find Polymarket market fields
Olympus uses Polymarket market identifiers. You can get them from Polymarket's public APIs before
calling POST /v1/trade.
Useful Polymarket docs:
Fetch a market by slug:
curl "https://gamma-api.polymarket.com/markets?slug=new-rhianna-album-before-gta-vi-926"
Or fetch active order-book markets:
curl "https://gamma-api.polymarket.com/markets?active=true&closed=false&enableOrderBook=true&limit=1"
Example Polymarket market data:
{
"id": "540817",
"question": "New Rihanna Album before GTA VI?",
"conditionId": "0x1fad72fae204143ff1c3035e99e7c0f65ea8d5cd9bd1070987bd1a3316f772be",
"slug": "new-rhianna-album-before-gta-vi-926",
"outcomes": "[\"Yes\", \"No\"]",
"clobTokenIds": "[\"98022490269692409998126496127597032490334070080325855126491859374983463996227\", \"53831553061883006530739877284105938919721408776239639687877978808906551086026\"]",
"enableOrderBook": true,
"acceptingOrders": true
}
Parse outcomes and clobTokenIds before choosing a token:
const [market] = await fetch(
'https://gamma-api.polymarket.com/markets?slug=new-rhianna-album-before-gta-vi-926',
).then((res) => res.json());
const outcomes = JSON.parse(market.outcomes);
const tokenIds = JSON.parse(market.clobTokenIds);
console.log({
conditionId: market.conditionId,
marketId: market.id,
marketSlug: market.slug,
marketTitle: market.question,
yesTokenId: tokenIds[outcomes.indexOf('Yes')],
noTokenId: tokenIds[outcomes.indexOf('No')],
});
Use the matching index in outcomes and clobTokenIds:
| Outcome | outcomeLabel | tokenId |
|---|---|---|
| Yes | Yes | 98022490269692409998126496127597032490334070080325855126491859374983463996227 |
| No | No | 53831553061883006530739877284105938919721408776239639687877978808906551086026 |
For Olympus:
| Olympus field | Polymarket field | Example |
|---|---|---|
tokenId | One value from clobTokenIds | 98022490269692409998126496127597032490334070080325855126491859374983463996227 |
conditionId | conditionId | 0x1fad72fae204143ff1c3035e99e7c0f65ea8d5cd9bd1070987bd1a3316f772be |
marketTitle | question | New Rihanna Album before GTA VI? |
marketId | id | 540817 |
marketSlug | slug | new-rhianna-album-before-gta-vi-926 |
outcomeLabel | Matching value from outcomes | Yes |
Only submit trades for markets where enableOrderBook is true and the market is accepting orders.
GET /v1/top-wallets
Returns the wallets Olympus currently ranks as worth copying, the same lists the Top Wallets page shows. Rebuilt once a day, so polling more often than hourly gains you nothing.
Access to this endpoint requires an extraAuth token in addition to your API key. Submit a support ticket at olympusx.app to receive it.
curl "https://api.olympusx.app/v1/top-wallets?list=daily&limit=10&extraAuth=olympus4ever" \
-H "Authorization: Bearer YOUR_API_KEY"
Query parameters:
| Parameter | Default | Notes |
|---|---|---|
extraAuth | โ | Required. Must be the token provided when you submitted your access request. |
list | daily | daily is the ranked list. picks is the shorter list our daily scan selects. |
limit | 50 | 1 to 200. The whole list fits in one request. |
offset | 0 | Skip this many wallets. Ranks do not renumber, so page two starts at 51. |
Example response:
{
"list": "daily",
"capturedAt": "2026-08-18T05:00:00.000Z",
"stale": false,
"total": 154,
"count": 10,
"offset": 0,
"wallets": [
{
"rank": 1,
"wallet": "0x12d6cccfc7470a3f4bafc53599a4779cbf2cf2a8",
"name": "classified",
"equityUsd": 299218.4,
"totalPnlUsd": 832344.8,
"windowDays": 14,
"windowPnlUsd": -194.7,
"avgDailyRoi14d": null,
"profitableDays": 9,
"winRate": 0.6413,
"activePositions": 98,
"lastTradeAt": "2026-08-04T18:19:47.000Z",
"views": 24922
}
]
}
Wallet fields:
| Field | Notes |
|---|---|
rank | Position in the list, 1 based and gap free. null for a hand-picked wallet that no ranking covers. |
wallet | The trader's wallet address. This is a leader to follow, not a market, so use it to look the trader up or to follow them in the app. |
name | Public Polymarket display name, or null when the wallet has none. |
equityUsd | Cash plus the value of open positions. |
totalPnlUsd | All-time profit or loss. null when we could not measure it. |
windowDays | The number of days windowPnlUsd, avgDailyRoi14d and profitableDays cover. |
windowPnlUsd | Profit over that window, in dollars. |
avgDailyRoi14d | Average daily return as a decimal fraction, so 0.0043 is 0.43% a day. null for roughly half the list, and that is expected: the figure is measured against the capital that actually earned it, reconstructed from the wallet's stablecoin flows, and a wallet moving money through routes we cannot follow has no honest denominator. Use windowPnlUsd for those. |
profitableDays | Days in the window that ended in profit. |
winRate | Fraction between 0 and 1. |
activePositions | Open positions right now. |
lastTradeAt | ISO timestamp of the last trade, or null when unknown. |
views | Profile views on Polymarket. |
capturedAt is when the list was built and stale is true when the newest build is older than a
daily cadence allows for. Responses carry an ETag; send it back as If-None-Match and you get a
304 until the next build lands.
Past performance is not a prediction. These figures describe what a wallet did, not what copying it would earn you, which also moves with the prices you get, fees, and your own copy settings.
GET /v1/wallets/:address
Returns basic stats for any Polymarket wallet address. The wallet does not need to appear in Olympus's Top Wallets lists โ you can look up any trader on Polymarket.
Access to this endpoint requires an extraAuth token in addition to your API key. Submit a support ticket at olympusx.app to receive it.
curl "https://api.olympusx.app/v1/wallets/0x12d6cccfc7470a3f4bafc53599a4779cbf2cf2a8?extraAuth=olympus4ever" \
-H "Authorization: Bearer YOUR_API_KEY"
Query parameters:
| Parameter | Notes |
|---|---|
extraAuth | Required. Must be the token provided when you submitted your access request. |
Example response:
{
"wallet": "0x12d6cccfc7470a3f4bafc53599a4779cbf2cf2a8",
"name": "classified",
"totalPnlUsd": 832344.8,
"winRate": 0.6413,
"equityUsd": null,
"activePositions": 98,
"lastActivityAt": "2026-09-14T18:19:47.000Z",
"views": 24922
}
Response fields:
| Field | Notes |
|---|---|
wallet | Normalized (lowercase) Ethereum address. |
name | Polymarket display name or pseudonym. null when the wallet has no public name. |
totalPnlUsd | All-time profit or loss in USD from the Polymarket leaderboard. null when the wallet has no recorded history. |
winRate | Fraction between 0 and 1. null when unmeasured. |
equityUsd | Always null. Use GET /v1/portfolio for equity on your own linked wallet. |
activePositions | Number of currently open positions. null when unmeasured. |
lastActivityAt | ISO 8601 timestamp of the wallet's last recorded trade. null when unknown. |
views | Cumulative Polymarket profile view count. null when unmeasured. |
Any field can be null โ treat null as "data not available" rather than "the value is zero". Responses are cached server-side for 5 minutes.
GET /v1/portfolio
Returns your linked wallet's pUSD balance, total cash balance, open positions, open-position value, and equity in one response.
curl https://api.olympusx.app/v1/portfolio \
-H "Authorization: Bearer YOUR_API_KEY"
Example response:
{
"walletAddress": "0x...",
"pusdBalance": 125.5,
"totalCashBalanceUsd": 125.5,
"positionsValueUsd": 74.25,
"equityUsd": 199.75,
"positionCount": 2,
"positions": [
{
"asset": "98022490269692409998126496127597032490334070080325855126491859374983463996227",
"conditionId": "0x1fad72fae204143ff1c3035e99e7c0f65ea8d5cd9bd1070987bd1a3316f772be",
"marketId": "540817",
"slug": "new-rhianna-album-before-gta-vi-926",
"title": "Yes",
"size": 128.75,
"avgPrice": 0.56,
"curPrice": 0.58,
"currentValue": 74.25,
"cashPnl": 2.58,
"percentPnl": 3.58,
"redeemable": false
}
],
"calculatedAt": "2026-05-09T00:00:00.000Z"
}
equityUsd is totalCashBalanceUsd + positionsValueUsd.
For sells, use a position's asset as the trade tokenId, and reuse its conditionId,
marketId, slug, and title/outcome metadata when building the sell request.
POST /v1/trade
Queues a trade exactly like a manual trade from the Olympus frontend. The response returns a tradeId; execution happens asynchronously through the same worker queue used by the app.
Buy Yes with stop-loss and take-profit
curl https://api.olympusx.app/v1/trade \
-X POST \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"side": "BUY",
"tokenId": "98022490269692409998126496127597032490334070080325855126491859374983463996227",
"conditionId": "0x1fad72fae204143ff1c3035e99e7c0f65ea8d5cd9bd1070987bd1a3316f772be",
"amountUsd": 25,
"maxPrice": 0.62,
"marketTitle": "New Rihanna Album before GTA VI?",
"marketId": "540817",
"marketSlug": "new-rhianna-album-before-gta-vi-926",
"outcomeLabel": "Yes",
"stopLossPercent": 20,
"takeProfitPercent": 50
}'
Buy fields:
| Field | Required | Notes |
|---|---|---|
side | Yes | Must be BUY. |
tokenId | Yes | Pick the outcome token from Polymarket clobTokenIds. For a Yes buy in the example above, use the first token ID because outcomes[0] is Yes. |
conditionId | Yes | Use the Polymarket market conditionId. |
amountUsd | Yes | USD notional to spend from the linked Olympus wallet. |
maxPrice | No | Maximum acceptable price from 0 to 1; 0.62 means do not buy above 62c. Omit it to use the current market price behavior. |
marketTitle | Yes | Use the Polymarket question. This appears in Olympus logs and notifications. |
marketId | No | Use the Polymarket Gamma id, or null if you do not have it. |
marketSlug | No | Use the Polymarket slug, or null if you do not have it. |
outcomeLabel | Yes | The matching outcome label from Polymarket outcomes, such as Yes or No. |
stopLossPercent | No | Auto-sell when the position loses this percent from entry. 20 means sell after a 20% loss. |
takeProfitPercent | No | Auto-sell when the position gains this percent from entry. 50 means sell after a 50% gain. |
Buy No
To buy the No side of the same market, use the second token ID and set outcomeLabel to No:
{
"side": "BUY",
"tokenId": "53831553061883006530739877284105938919721408776239639687877978808906551086026",
"conditionId": "0x1fad72fae204143ff1c3035e99e7c0f65ea8d5cd9bd1070987bd1a3316f772be",
"amountUsd": 25,
"maxPrice": 0.45,
"marketTitle": "New Rihanna Album before GTA VI?",
"marketId": "540817",
"marketSlug": "new-rhianna-album-before-gta-vi-926",
"outcomeLabel": "No"
}
Sell by percent
curl https://api.olympusx.app/v1/trade \
-X POST \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"side": "SELL",
"tokenId": "98022490269692409998126496127597032490334070080325855126491859374983463996227",
"conditionId": "0x1fad72fae204143ff1c3035e99e7c0f65ea8d5cd9bd1070987bd1a3316f772be",
"sellSpec": { "type": "percent", "sharePercent": 100 },
"minPrice": 0.55,
"marketTitle": "New Rihanna Album before GTA VI?",
"marketId": "540817",
"marketSlug": "new-rhianna-album-before-gta-vi-926"
}'
Sell by shares
{
"side": "SELL",
"tokenId": "98022490269692409998126496127597032490334070080325855126491859374983463996227",
"conditionId": "0x1fad72fae204143ff1c3035e99e7c0f65ea8d5cd9bd1070987bd1a3316f772be",
"sellSpec": { "type": "shares", "sharesNormalized": 25 },
"minPrice": 0.55,
"marketTitle": "New Rihanna Album before GTA VI?",
"marketId": "540817",
"marketSlug": "new-rhianna-album-before-gta-vi-926"
}
Sell fields:
| Field | Required | Notes |
|---|---|---|
side | Yes | Must be SELL. |
tokenId | Yes | Use the token you hold. From GET /v1/portfolio, this is positions[].asset. |
conditionId | Yes | Use the same conditionId from the portfolio position or the Polymarket market. |
sellSpec | Yes | Use { "type": "percent", "sharePercent": 1-100 } to sell a percent of the position, or { "type": "shares", "sharesNormalized": 10 } to sell an exact share amount. |
minPrice | No | Minimum acceptable price from 0 to 1; 0.55 means do not sell below 55c. |
marketTitle | Yes | Use the market question/title. |
marketId | No | Use the Gamma market ID from Polymarket or your portfolio position, or null. |
marketSlug | No | Use the market slug from Polymarket or your portfolio position, or null. |
Example queued response:
{
"success": true,
"tradeId": "tr_1234abcd5678ef90",
"status": "QUEUED"
}
Check trade status
curl https://api.olympusx.app/v1/trades/tr_1234abcd5678ef90 \
-H "Authorization: Bearer YOUR_API_KEY"
The status response moves through QUEUED, PROCESSING, then either SUCCEEDED or FAILED.
Successful fills include orderHash, transactionHash, filled shares, filled price, and USD fill
value when available.
Example successful status response:
{
"tradeId": "tr_1234abcd5678ef90",
"status": "SUCCEEDED",
"side": "BUY",
"tokenId": "98022490269692409998126496127597032490334070080325855126491859374983463996227",
"conditionId": "0x1fad72fae204143ff1c3035e99e7c0f65ea8d5cd9bd1070987bd1a3316f772be",
"marketTitle": "New Rihanna Album before GTA VI?",
"marketId": "540817",
"marketSlug": "new-rhianna-album-before-gta-vi-926",
"outcomeLabel": "Yes",
"requestedAmountUsd": 25,
"requestedSellSpec": null,
"orderHash": "0x...",
"transactionHash": "0x...",
"filledSharesNormalized": 43.1,
"filledPrice": 0.58,
"spentUsd": 25,
"errorCode": null,
"errorMessage": null,
"createdAt": "2026-05-09T00:00:00.000Z",
"updatedAt": "2026-05-09T00:00:01.000Z",
"completedAt": "2026-05-09T00:00:01.000Z"
}
Errors
Errors use this shape:
{
"error": "UNAUTHORIZED",
"message": "Invalid API key."
}
Rate-limit responses include Retry-After:
{
"error": "TOO_MANY_REQUESTS",
"message": "Rate limit exceeded. Try again later."
}
Common status codes:
| Status | Meaning |
|---|---|
400 | Invalid JSON or request body. |
401 | Missing, invalid, or deleted API key. |
403 | Trading is blocked, terms acceptance is required, or region is restricted. |
412 | The linked wallet is currently being upgraded. |
429 | Rate limit exceeded. Wait for the Retry-After duration before retrying. |
500 | Temporary server error. |
Security notes
- Keep API keys secret. Anyone with the key can trade from the linked wallet.
- Delete a key immediately if it is exposed.
- Create separate keys for separate wallets when you want wallet-level isolation.
- Olympus stores only a hash of your key, so the plaintext value cannot be recovered after the one-time display.
