Complete REST API for the PukiMarket prediction market platform. Build bots, create markets, and trade programmatically.
https://api-production-2dab.up.railway.appJSONSocket.IOStep-by-step instructions for AI agents to create markets, place trades, and manage the platform programmatically.
API keys are long-lived and never expire. Much better than JWT tokens for agents. First create an account, then generate a key.
# 1. Create account (one-time, via JWT)
curl -X POST https://api-production-2dab.up.railway.app/auth/signup \
-H "Content-Type: application/json" \
-d '{"email":"agent@bot.com","username":"myagent","password":"securepass123"}'
# Save the token from response
# 2. Create an API key (one-time)
curl -X POST https://api-production-2dab.up.railway.app/auth/api-keys \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{"label":"my-trading-bot"}'
# Response: { "key": "pm_a1b2c3d4e5f6...", ... }
# SAVE THIS KEY! It's only shown once.
# 3. Use the API key for ALL future requests:
curl https://api-production-2dab.up.railway.app/auth/me -H "X-API-Key: pm_a1b2c3d4e5f6..."
# That's it! No login, no expiry, no token refresh.If you prefer JWT tokens (expire after 7 days), use login directly.
curl -X POST https://api-production-2dab.up.railway.app/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"agent@bot.com","password":"securepass123"}'
# Use: Authorization: Bearer <token>
# OR with API key (recommended):
# Use: X-API-Key: pm_your_key_here
# Both work on all authenticated endpoints.Requires CREATOR or ADMIN role. With CREATOR role, markets are created as DRAFT and need admin approval before going live. ADMIN role creates markets as OPEN directly.
curl -X POST https://api-production-2dab.up.railway.app/markets \
-H "Content-Type: application/json" \
-H "X-API-Key: pm_your_key_here" \
-d '{
"slug": "btc-100k-june-2026",
"question": "Will BTC hit $100K by June 2026?",
"questionHe": "האם ביטקוין יגיע ל-100K עד יוני 2026?",
"category": "CRYPTO",
"type": "BINARY",
"template": "BINARY",
"resolutionTime": "2026-06-01T00:00:00Z",
"outcomes": [
{ "label": "Yes", "labelHe": "כן" },
{ "label": "No", "labelHe": "לא" }
]
}'
# CREATOR response: { "market": { "status": "DRAFT", ... } }
# ADMIN response: { "market": { "status": "OPEN", ... } }
# Draft markets appear in admin dashboard for approval.Buy shares in an outcome. Price is in cents (1-99). BUY orders always fill via AMM even with no counterparty.
# Buy 10 shares of "Yes" at 65 cents each (costs $6.50)
curl -X POST https://api-production-2dab.up.railway.app/orders \
-H "Content-Type: application/json" \
-H "X-API-Key: pm_your_key_here" \
-d '{
"marketId": "MARKET_ID_FROM_STEP_2",
"outcomeId": "YES_OUTCOME_ID",
"side": "BUY",
"type": "LIMIT",
"price": 65,
"size": 10
}'
# Response: { "status": "FILLED", "filled": 10, "remaining": 0 }
# Your balance decreased by $6.50. You now hold 10 "Yes" shares.# See all your positions curl https://api-production-2dab.up.railway.app/positions \ -H "X-API-Key: pm_your_key_here" # Response: positions with shares, avgPrice, currentPrice, pnl
Sell shares anytime, or hold until market resolves. Winning shares pay $1 each.
# Sell 5 shares (must hold them first)
curl -X POST https://api-production-2dab.up.railway.app/orders \
-H "Content-Type: application/json" \
-H "X-API-Key: pm_your_key_here" \
-d '{
"marketId": "...", "outcomeId": "...",
"side": "SELL", "type": "LIMIT", "price": 72, "size": 5
}'
# Resolve a market (ADMIN only) — pays winners automatically
curl -X PUT https://api-production-2dab.up.railway.app/markets/MARKET_ID/resolve \
-H "Content-Type: application/json" \
-H "X-API-Key: pm_your_admin_key" \
-d '{"winningOutcomeId": "YES_OUTCOME_ID"}'BINARY — Two outcomes (Yes/No). Most common type.MULTIPLE — 3+ outcomes (e.g., who will win an election).BINARY — Standard yes/no question.WINNER_2WAY — Two competitors, no draw option.WINNER_3WAY — Two competitors + draw option.OVER_UNDER — Total above/below a line value.SPREAD — Point spread / handicap betting.YES_NO — Prop bet within an event.POLITICS — Elections, legislation, geopolitics.SPORTS — Football, basketball, tennis, etc.ESPORTS — CS2, League of Legends, Valorant.CRYPTO — Bitcoin, Ethereum, token prices.ECONOMICS — Interest rates, GDP, inflation.WEATHER — Temperature, rainfall, storms.POP_CULTURE — Movies, music, celebrities.QUICK_BET — Short-duration bets (5-15 min windows).OTHER — Anything else.side — BUY (purchase shares) or SELL (sell shares you hold).type — LIMIT (rests on order book if no match) or MARKET (fills immediately or cancels).price — Cents, 1-99. Represents probability (65 = 65% chance). Cost = price * size / 100.size — Number of shares. Each winning share pays out $1.00 at resolution.OPEN — Accepting trades. Default state after creation.RESOLVED — Winning outcome decided. Winners paid out at $1/share.CANCELLED — Market voided. All positions refunded.OPEN — Limit order resting on the book, waiting for a match.PARTIALLY_FILLED — Some shares matched, rest still on book.FILLED — All shares matched (or AMM-filled for BUY orders).CANCELLED — Cancelled by user or system (market sell with no buyer).MANUAL — Admin manually resolves via PUT /markets/:id/resolve.SCHEDULED — Auto-resolve attempted at resolutionTime.CRON — Periodic resolution check (e.g., every 4 hours).API_FEED — Resolved automatically from an external data source.BUY order and there are no sellers, the system automatically fills your order at the requested price.SELL orders require a real buyer. If no buyer exists, MARKET sells are cancelled and LIMIT sells rest on the book.UPCOMING — Event hasn't started yet. Markets may already be open.LIVE — Event is in progress.ENDED — Event finished. Markets may still need resolution.CANCELLED — Event cancelled.pagination: { page, limit, total, totalPages }?page=2&limit=50 to paginate. Max limit is 100.400 — Validation error. Check details field for field-specific errors.401 — Missing or invalid token. Re-authenticate.403 — Insufficient permissions (wrong role).404 — Resource not found.409 — Conflict (duplicate slug, email, or username).Endpoints marked with AUTH accept two methods: API Key or JWT Bearer token.
# Long-lived, no expiry. Create once via POST /auth/api-keys. curl https://api-production-2dab.up.railway.app/auth/me \ -H "X-API-Key: pm_a1b2c3d4e5f6..."
# Get token via POST /auth/signup or /auth/login curl https://api-production-2dab.up.railway.app/auth/me \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
/auth/signupCreate a new account
{
"email": "user@example.com",
"username": "trader1", // 3-30 chars
"password": "secret123" // 6+ chars
}{
"token": "eyJhbGciOi...",
"user": {
"id": "clx...",
"email": "user@example.com",
"username": "trader1",
"displayName": null,
"avatarUrl": null,
"playBalance": 1000,
"walletAddress": null,
"role": "USER",
"locale": "he",
"createdAt": "2026-03-31T..."
}
}curl -X POST https://api-production-2dab.up.railway.app/auth/signup \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com","username":"trader1","password":"secret123"}'/auth/loginLogin with email and password
{
"email": "user@example.com",
"password": "secret123"
}{
"token": "eyJhbGciOi...",
"user": { ... } // Same shape as signup
}curl -X POST https://api-production-2dab.up.railway.app/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com","password":"secret123"}'/auth/meAUTHGet current authenticated user. Works with both JWT and API key.
{
"user": {
"id": "clx...",
"email": "user@example.com",
"username": "trader1",
"displayName": null,
"avatarUrl": null,
"playBalance": 847.50,
"walletAddress": null,
"role": "USER",
"locale": "he",
"createdAt": "2026-03-31T..."
}
}/auth/api-keysAUTHCreate a long-lived API key for programmatic access. The raw key is only returned ONCE.
{
"label": "my-trading-bot" // 1-50 chars, descriptive name
}{
"id": "clx...",
"key": "pm_a1b2c3d4e5f6...", // SAVE THIS! Only shown once.
"prefix": "pm_a1b2c3d4",
"label": "my-trading-bot",
"role": "CREATOR",
"createdAt": "2026-03-31T..."
}curl -X POST https://api-production-2dab.up.railway.app/auth/api-keys \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"label":"my-trading-bot"}'/auth/api-keysAUTHList your API keys (prefix and metadata only, not the full key)
{
"keys": [{
"id": "clx...",
"prefix": "pm_a1b2c3d4",
"label": "my-trading-bot",
"role": "CREATOR",
"lastUsedAt": "2026-03-31T...",
"createdAt": "2026-03-31T..."
}]
}/auth/api-keys/:idAUTHRevoke an API key (immediately stops working)
{ "deleted": true }/marketsList markets with filtering and pagination
category string optional POLITICS|SPORTS|CRYPTO|ECONOMICS|WEATHER|... status string optional OPEN|RESOLVED|CANCELLED isQuickBet boolean optional true|false page number optional default: 1 limit number optional default: 20, max: 100
{
"markets": [{
"id": "clx...",
"slug": "will-btc-hit-100k",
"question": "Will BTC hit $100K?",
"questionHe": "?האם ביטקוין יגיע ל-100K",
"category": "CRYPTO",
"type": "BINARY",
"status": "OPEN",
"template": "BINARY",
"resolutionTime": "2026-06-01T00:00:00Z",
"isQuickBet": false,
"totalVolume": 2500.00,
"liquidity": 15000.00,
"outcomes": [{
"id": "clx...",
"label": "Yes",
"labelHe": "כן",
"tokenIndex": 0,
"lastPrice": 0.65,
"bestBid": 0.63,
"bestAsk": 0.67
}],
"creator": { "id": "...", "username": "admin" },
...
}],
"pagination": { "page": 1, "limit": 20, "total": 42, "totalPages": 3 }
}curl "https://api-production-2dab.up.railway.app/markets?status=OPEN&category=CRYPTO&limit=5"
/markets/:slugGet a single market by slug, includes recent trades
{
"market": {
... // Full market object
"recentTrades": [{
"id": "clx...",
"outcomeId": "clx...",
"price": 0.65,
"size": 10,
"makerUsername": "alice",
"takerUsername": "bob",
"createdAt": "2026-03-31T..."
}]
}
}curl "https://api-production-2dab.up.railway.app/markets/will-btc-hit-100k"
/marketsAUTHCreate a new market (CREATOR or ADMIN role required)
{
"slug": "will-btc-hit-100k",
"question": "Will BTC hit $100K by June 2026?",
"questionHe": "?האם ביטקוין יגיע ל-100K עד יוני 2026",
"description": "Resolves YES if...",
"category": "CRYPTO",
"type": "BINARY",
"template": "BINARY",
"resolutionTime": "2026-06-01T00:00:00.000Z",
"isQuickBet": false,
"imageUrl": "https://...",
"eventId": "clx...", // optional
"resolveMethod": "MANUAL", // MANUAL|SCHEDULED|CRON|API_FEED
"resolveCron": "0 */4 * * *", // if CRON
"resolveDelay": false,
"recurring": false,
"outcomes": [
{ "label": "Yes", "labelHe": "כן" },
{ "label": "No", "labelHe": "לא" }
]
}{
"market": {
"id": "clx...",
"slug": "will-btc-hit-100k",
"outcomes": [
{ "id": "clx...", "label": "Yes", "labelHe": "כן", "tokenIndex": 0 },
{ "id": "clx...", "label": "No", "labelHe": "לא", "tokenIndex": 1 }
],
...
}
}curl -X POST https://api-production-2dab.up.railway.app/markets \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"slug": "will-btc-hit-100k",
"question": "Will BTC hit $100K?",
"category": "CRYPTO",
"type": "BINARY",
"template": "BINARY",
"resolutionTime": "2026-06-01T00:00:00Z",
"outcomes": [{"label":"Yes"},{"label":"No"}]
}'/markets/:id/resolveAUTHResolve a market (ADMIN only). Pays out winners automatically.
{
"winningOutcomeId": "clx..."
}{
"market": {
"id": "clx...",
"slug": "will-btc-hit-100k",
"status": "RESOLVED",
"resolvedAt": "2026-03-31T...",
"resolvedOutcomeId": "clx..."
}
}/markets/:id/approveAUTHApprove a DRAFT market, making it OPEN and tradeable (ADMIN only)
{
"market": {
"id": "clx...",
"slug": "will-btc-hit-100k",
"status": "OPEN",
"question": "Will BTC hit $100K?",
"questionHe": "..."
}
}curl -X PUT https://api-production-2dab.up.railway.app/markets/MARKET_ID/approve \ -H "Authorization: Bearer YOUR_ADMIN_TOKEN"
/markets/:idAUTHDelete/reject a DRAFT market (ADMIN only)
{ "deleted": true }/eventsList events with filtering
category string optional subcategory string optional status string optional UPCOMING|LIVE|ENDED|CANCELLED featured boolean optional page number optional default: 1 limit number optional default: 20, max: 100
{
"events": [{
"id": "clx...",
"slug": "maccabi-vs-hapoel",
"title": "Maccabi vs Hapoel",
"titleHe": "מכבי נגד הפועל",
"category": "SPORTS",
"participants": [
{ "name": "Maccabi", "nameHe": "מכבי", "iconUrl": "..." }
],
"startTime": "2026-04-01T20:00:00Z",
"status": "UPCOMING",
"featured": true,
"markets": [...],
"marketCount": 3
}],
"pagination": { ... }
}/events/:slugGet a single event with all its markets
{ "event": { ... } }/eventsAUTHCreate a new event (CREATOR or ADMIN)
{
"slug": "maccabi-vs-hapoel-2026",
"title": "Maccabi vs Hapoel",
"titleHe": "מכבי נגד הפועל",
"category": "SPORTS",
"participants": [
{ "name": "Maccabi", "nameHe": "מכבי", "iconUrl": "https://..." },
{ "name": "Hapoel", "nameHe": "הפועל" }
],
"startTime": "2026-04-01T20:00:00.000Z",
"featured": true,
"imageUrl": "https://...",
"metadata": { "league": "Premier League" }
}{ "event": { "id": "clx...", ... } }/events/:id/statusAUTHUpdate event status (ADMIN only)
{
"status": "LIVE" // UPCOMING|LIVE|ENDED|CANCELLED
}{ "event": { ... } }/ordersAUTHSubmit a buy or sell order. AMM auto-fills BUY orders when no counterparty exists.
{
"marketId": "clx...",
"outcomeId": "clx...",
"side": "BUY", // BUY or SELL
"type": "LIMIT", // LIMIT or MARKET
"price": 65, // 1-99 (cents)
"size": 10 // number of shares
}{
"orderId": "clx...",
"trades": [{
"id": "t_...",
"makerOrderId": "clx...",
"takerOrderId": "clx...",
"price": 65,
"size": 10,
"timestamp": 1234567890
}],
"filled": 10,
"remaining": 0,
"status": "FILLED"
}curl -X POST https://api-production-2dab.up.railway.app/orders \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"marketId":"clx...","outcomeId":"clx...","side":"BUY","type":"LIMIT","price":65,"size":10}'/orders/:idAUTHCancel an open or partially filled order
{
"orderId": "clx...",
"status": "CANCELLED",
"cancelledFromEngine": true
}/ordersAUTHList your orders
marketId string optional status string optional OPEN|PARTIALLY_FILLED|FILLED|CANCELLED page number optional default: 1 limit number optional default: 50, max: 100
{
"orders": [{
"id": "clx...",
"marketId": "clx...",
"marketSlug": "will-btc-hit-100k",
"outcomeId": "clx...",
"outcomeLabel": "Yes",
"side": "BUY",
"type": "LIMIT",
"price": 0.65,
"priceCents": 65,
"size": 10,
"filled": 10,
"remaining": 0,
"status": "FILLED",
"createdAt": "2026-03-31T..."
}],
"pagination": { ... }
}/tradesList trades for a market
marketId string required outcomeId string optional page number optional default: 1 limit number optional default: 50, max: 100
{
"trades": [{
"id": "clx...",
"marketId": "clx...",
"outcomeId": "clx...",
"price": 0.65,
"size": 10,
"total": 6.50,
"makerUsername": "alice",
"takerUsername": "bob",
"createdAt": "2026-03-31T..."
}],
"pagination": { ... }
}/trades/activityGet human-readable activity feed for a market
marketId string required limit number optional default: 20, max: 50
{
"activity": [{
"userId": "clx...",
"username": "trader1",
"avatarUrl": null,
"action": "bought",
"amount": 10,
"side": "BUY",
"outcomeLabel": "Yes",
"outcomeLabelHe": "כן",
"price": 0.65,
"total": 6.50,
"text": "trader1 bought 10 Yes @ 65¢",
"timestamp": "2026-03-31T..."
}]
}/positionsAUTHGet your portfolio positions with PnL
page number optional default: 1 limit number optional default: 50, max: 100
{
"positions": [{
"id": "clx...",
"marketId": "clx...",
"outcomeId": "clx...",
"shares": 10,
"avgPrice": 0.65,
"avgPriceCents": 65,
"currentPrice": 0.72,
"currentPriceCents": 72,
"pnl": 0.70,
"pnlPercent": 10.77,
"value": 7.20,
"marketSlug": "will-btc-hit-100k",
"marketQuestion": "Will BTC hit $100K?",
"marketStatus": "OPEN",
"outcomeLabel": "Yes"
}],
"pagination": { ... }
}/positions/:marketIdAUTHGet your positions in a specific market
{ "positions": [{ ... }] }/positions/:marketId/holdersPublic: see who holds positions in a market
page number optional default: 1 limit number optional default: 20, max: 100
{
"holders": [{
"userId": "clx...",
"username": "trader1",
"displayName": null,
"avatarUrl": null,
"totalShares": 25,
"outcomes": [{
"outcomeId": "clx...",
"label": "Yes",
"tokenIndex": 0,
"shares": 25
}]
}],
"pagination": { ... }
}/positions/:marketId/leaderboardLeaderboard: top traders by PnL in a market
limit number optional default: 20, max: 100
{
"leaderboard": [{
"userId": "clx...",
"username": "trader1",
"totalPnl": 125.50,
"totalValue": 450.00,
"positionCount": 3
}]
}/users/:usernameGet public user profile with stats
{
"user": {
"id": "clx...",
"username": "trader1",
"displayName": null,
"avatarUrl": null,
"createdAt": "2026-03-31T...",
"stats": {
"positionsValue": 450.00,
"biggestWin": 125.50,
"predictions": 12,
"totalPnl": 87.30,
"activePositions": 5
}
}
}curl "https://api-production-2dab.up.railway.app/users/trader1"
/users/:username/positionsGet user's public positions
status string optional active|closed|all (default: active) page number optional default: 1 limit number optional default: 50, max: 100
{ "positions": [{ ... }], "pagination": { ... } }/users/:username/activityGet user's public trade activity
filter string optional all|trades|buy|sell (default: all) page number optional default: 1 limit number optional default: 50, max: 100
{
"activity": [{
"id": "clx...",
"activity": "bought 10 shares",
"market": { "slug": "...", "question": "...", "questionHe": "..." },
"outcome": { "label": "Yes", "labelHe": "כן", "tokenIndex": 0 },
"side": "BUY",
"priceCents": 65,
"shares": 10,
"total": 6.50,
"createdAt": "2026-03-31T..."
}],
"pagination": { ... }
}/healthHealth check endpoint
{
"status": "ok",
"timestamp": "2026-03-31T00:00:00.000Z",
"env": "production"
}curl "https://api-production-2dab.up.railway.app/health"
Connect via Socket.IO for real-time updates. Events are emitted to specific rooms.
import { io } from "socket.io-client";
const socket = io("https://api-production-2dab.up.railway.app");
// Join rooms
socket.emit("join", "orderbook:MARKET_ID:OUTCOME_ID");
socket.emit("join", "trades:MARKET_ID");book:updatetrade:newmarket:resolvedevent:status
Comments
(תגובות)4 endpoints/commentsList comments for a market (includes replies and position badges)
{ "comments": [{ "id": "clx...", "content": "Great market!", "imageUrl": null, "likes": 5, "createdAt": "2026-03-31T...", "replyCount": 2, "user": { "id": "clx...", "username": "trader1", "displayName": null, "avatarUrl": null, "position": { "label": "Yes", "labelHe": "כן", "shares": 10 } }, "replies": [{ ... }] }], "pagination": { ... } }/commentsAUTHPost a comment or reply
{ "marketId": "clx...", "content": "I think Yes is undervalued", // 1-2000 chars "imageUrl": "https://...", // optional "parentId": "clx..." // optional (for replies) }{ "comment": { ... } }/comments/:id/likeAUTHToggle like on a comment
{ "likes": 6 }/comments/:idAUTHDelete your own comment (or any if ADMIN)
{ "deleted": true }