Quick Start
Create API credentials, call a public endpoint, sign a private request, and place your first order in the UAT environment.
Choose API endpoints
| Environment | REST endpoint | WebSocket endpoint |
|---|---|---|
| UAT | https://api.uat.udx.underdogsports.com/ |
wss://api.uat.udx.underdogsports.com/ws |
| Production | https://api.udx.underdogsports.com/ |
wss://api.udx.underdogsports.com/ws |
Set the UAT REST base URL once before running the examples:
export UDX_BASE_URL="https://api.uat.udx.underdogsports.com"
List markets
The markets catalog is public and does not require authentication:
curl "${UDX_BASE_URL}/markets?limit=1&sort=event_date&order=asc"
Use a returned ticker to request its aggregated order book:
curl "${UDX_BASE_URL}/markets/UDX-EXAMPLE-MARKET/orderbook?depth=10"
See Pagination for additional pages and the WebSocket API for live updates.
Create an API Key
Open the UDX UAT app and create an API key with the trade scope and access to the account you will use. Save the API key ID and private key, then set their values in your environment:
export UDX_ACCESS_KEY="<api-key-id>"
export UDX_PRIVATE_KEY_PATH="<path-to-private-key.pem>"
Place an order
This example uses Node.js 18 or newer and only built-in modules. Save it as place-order.mjs, replace the example ticker with one returned by the markets request, then run node place-order.mjs:
import {
constants,
createHash,
createPrivateKey,
randomUUID,
sign,
} from "node:crypto";
import { readFileSync } from "node:fs";
const baseUrl = process.env.UDX_BASE_URL;
const accessKey = process.env.UDX_ACCESS_KEY;
const privateKeyPath = process.env.UDX_PRIVATE_KEY_PATH;
if (!baseUrl || !accessKey || !privateKeyPath) {
throw new Error(
"Set UDX_BASE_URL, UDX_ACCESS_KEY, and UDX_PRIVATE_KEY_PATH first.",
);
}
const method = "POST";
const path = "/orders";
const timestamp = Date.now().toString();
const idempotencyKey = randomUUID();
const body = JSON.stringify({
ticker: "UDX-EXAMPLE-MARKET",
client_order_id: "first-order",
side: "buy",
quantity: "1",
price: "0.55",
time_in_force: "gtc",
});
const bodyHash = createHash("sha256").update(body).digest("hex");
const message = timestamp + method + path + bodyHash + idempotencyKey;
const privateKey = createPrivateKey(
readFileSync(privateKeyPath, "utf8"),
);
const signature = sign("sha256", Buffer.from(message), {
key: privateKey,
padding: constants.RSA_PKCS1_PSS_PADDING,
saltLength: 32,
}).toString("base64");
const response = await fetch(`${baseUrl}${path}`, {
method,
headers: {
"Content-Type": "application/json",
"UDX-Access-Key": accessKey,
"UDX-Access-Timestamp": timestamp,
"UDX-Access-Signature": signature,
"UDX-Idempotency-Key": idempotencyKey,
},
body,
});
const result = await response.json();
if (!response.ok) {
throw new Error(`${response.status}: ${JSON.stringify(result)}`);
}
console.log(result);
The timestamp is the current Unix time in milliseconds. For this first order, always send the newly generated UUID in UDX-Idempotency-Key and include its exact value in the signed message. Reuse that key only to retry this same order. See Authentication for the complete signing rules, Idempotency for optional-header behavior outside this walkthrough, and Place an order for the full request and response.