REST APIHow-tos
Send an order
Send a signed order to the REST API, and read its status.
Sending an order takes two parts: signing it, and sending it. The SDK does the signing, because it computes the order's amounts and its EIP-712 signature for the right exchange contract. This page shows the requests around it. Place orders explains the order types, the options and the rules the API checks.
You need your API key, a JWT for the wallet (see Log in), and the approvals for the market.
1. Read the market
GET /v1/markets/{id} returns what the order needs: each outcome's onChainId (the order's tokenId), the market's feeRateBps, and isNegRisk and isYieldBearing, which pick the exchange contract the order is signed for.
2. Sign the order
The SDK builds the order from a price and a quantity, signs it, and computes its hash. See Place orders for limit and market orders.
3. Send it
POST /v1/orders takes the signed order, its hash, the price and the strategy. It needs your API key and the wallet's JWT.
{
"data": {
"order": {
"hash": "0x…",
"salt": "…",
"maker": "0x…",
"signer": "0x…",
"taker": "0x0000000000000000000000000000000000000000",
"tokenId": "…",
"makerAmount": "4000000000000000000",
"takerAmount": "10000000000000000000",
"expiration": "4102444800",
"nonce": "0",
"feeRateBps": "200",
"side": 0,
"signatureType": 0,
"signature": "0x…"
},
"pricePerShare": "400000000000000000",
"strategy": "LIMIT"
}
}An accepted order returns 201:
{
"success": true,
"data": {
"code": "OK",
"orderId": "123456789",
"orderHash": "0x…",
"removalLockedUntil": null
}
}Keep orderId to remove the order later.
A 201 means the order passed its first checks, not that it filled. Some balance and allowance checks finish after the response, and the order is cancelled if they fail. Read its status, or follow it live through wallet events.
4. Read its status
GET /v1/orders/{hash} returns the order with its status (OPEN, FILLED, CANCELLED, EXPIRED or INVALIDATED) and its amountFilled in wei. Order lifecycle explains each status.
Right after you send an order, this can briefly return 404. Retry for a few seconds before you treat the hash as unknown.
The whole flow
import { ChainId, OrderBuilder, Side } from "@predictdotfun/sdk";
import { Wallet } from "ethers";
const apiKey = "YOUR_API_KEY";
const apiUrl = "https://api.predict.fun";
const jwt = "YOUR_JWT"; // from the Log in how-to
// a market ID, like the ones the Market data example prints.
const marketId = "MARKET_ID";
const wallet = new Wallet(process.env.WALLET_PRIVATE_KEY!);
const headers = {
"Content-Type": "application/json",
"x-api-key": apiKey,
Authorization: `Bearer ${jwt}`,
};
// 1. Read the market: its outcomes, its fee, and which exchange it trades on.
const marketResponse = await fetch(`${apiUrl}/v1/markets/${marketId}`, {
headers,
});
const market = (await marketResponse.json()).data;
// 2. Build and sign the order with the SDK. Place orders explains each step.
const orderBuilder = await OrderBuilder.make(ChainId.BnbMainnet, wallet);
const { pricePerShare, makerAmount, takerAmount } =
orderBuilder.getLimitOrderAmounts({
side: Side.BUY,
pricePerShareWei: 400000000000000000n, // 0.40 USDT
quantityWei: 10000000000000000000n, // 10 shares
});
const order = orderBuilder.buildOrder("LIMIT", {
side: Side.BUY,
tokenId: market.outcomes[0].onChainId,
makerAmount,
takerAmount,
feeRateBps: market.feeRateBps,
});
const typedData = orderBuilder.buildTypedData(order, {
isNegRisk: market.isNegRisk,
isYieldBearing: market.isYieldBearing,
});
const signedOrder = await orderBuilder.signTypedDataOrder(typedData);
const hash = orderBuilder.buildTypedDataHash(typedData);
// 3. Send it.
const createResponse = await fetch(`${apiUrl}/v1/orders`, {
method: "POST",
headers,
body: JSON.stringify({
data: {
order: { ...signedOrder, hash },
pricePerShare: pricePerShare.toString(),
strategy: "LIMIT",
},
}),
});
const created = await createResponse.json();
if (!createResponse.ok) throw new Error(`${created.error}: ${created.message}`);
console.log(`Order ${created.data.orderId} accepted`);
// 4. Read its status. A new order can briefly return 404, so retry for a
// few seconds.
const statusUrl = `${apiUrl}/v1/orders/${hash}`;
let statusResponse = await fetch(statusUrl, { headers });
for (let retry = 0; retry < 4 && statusResponse.status === 404; retry++) {
await new Promise((resolve) => setTimeout(resolve, 1000));
statusResponse = await fetch(statusUrl, { headers });
}
const orderStatus = await statusResponse.json();
if (!statusResponse.ok) {
throw new Error(`${orderStatus.error}: ${orderStatus.message}`);
}
const { status, amountFilled } = orderStatus.data;
console.log(status, amountFilled);When it fails
A rejected order returns 400, and error names the rule it broke:
{
"success": false,
"code": 400,
"error": "create_order_insufficient_collateral_allowance",
"message": "Insufficient collateral: USDT allowance is less than the total bid amount."
}Errors lists every order code with its fix. A missing or expired JWT returns 401, and so does an order whose signer isn't the JWT's wallet. A request from a restricted region or wallet returns 403. In step 4, a hash that is still unknown after a few seconds returns 404 with error: "not_found".