Developers

API documentation

Build apps that trade on a user's behalf and earn a share of the revenue they generate. Your users authorize your app with OAuth 2.0, you receive a scoped token, and you trade over the real-time /ws/v1 gateway.

You earn on every trade your users place. Both the WebSocket /ws/v1 gateway and REST /api/v1/contracts attribute trades to your app when placed with your OAuth-issued token. Your app earns a flat 10% of the net house profit those users generate.

Base URLs

REST APIhttps://api.miletrades.site
WebSocket gatewaywss://api.miletrades.site/ws/v1
OAuth authorizehttps://api.miletrades.site/api/v1/oauth/authorize
OAuth tokenhttps://api.miletrades.site/api/v1/oauth/token

Quickstart

  1. Register your app on the Developer apps page. You get an app_id and a one-time app_secret.
  2. Send the user through OAuth (with PKCE) to get a scopedaccess_token for that user.
  3. Connect to the gateway and authorize with the token.
  4. Get a price (proposal) and buy (buy). Track it to settlement.
  5. Earn your configured markup on the revenue those trades generate; claim it on the Refer & Earn page.

Scopes

Request only the scopes your app needs.

readView markets, balance, portfolio and history.
tradeBuy and sell contracts on the user's behalf.
paymentsDeposit and withdraw from the user's account.
adminManage API apps and tokens (satisfies any scope check).

1 · Register your app

Register on the Developer apps page (or POST https://api.miletrades.site/api/v1/apps with your logged-in session). Provide a name, one or more redirect_uris, and the scopes you need. Revenue-share is a flat platform rate (see Earnings) — you don't set it.

// Response (the secret is shown ONCE — store it securely)
{
  "app": {
    "app_id": "08123456",          // your public client id
    "name": "My Trading App",
    "redirect_uris": ["https://myapp.com/callback"],
    "scopes": ["read", "trade"],
    "verified": false,
    "created_at": "2026-08-07T..."
  },
  "app_secret": "mps_xxxxxxxx"     // shown once; not needed for the PKCE flow
}

2 · Authorize a user (OAuth 2.0 + PKCE)

MilePredictions uses the authorization-code flow with PKCE (S256). PKCE is required — no client secret is used in the exchange.

a. Create a PKCE verifier & challenge

function base64url(bytes) {
  return btoa(String.fromCharCode(...new Uint8Array(bytes)))
    .replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
}
const verifier  = base64url(crypto.getRandomValues(new Uint8Array(32)));
const challenge = base64url(
  await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier))
);
// keep 'verifier' for the token exchange; send 'challenge' now

b. Redirect the user to the authorize URL

https://api.miletrades.site/api/v1/oauth/authorize
  ?app_id=YOUR_APP_ID
  &redirect_uri=https://myapp.com/callback   // must exactly match a registered URI
  &response_type=code
  &scope=read trade                          // space- or comma-separated; subset of your app's scopes
  &state=RANDOM_ANTI_CSRF
  &code_challenge=CHALLENGE
  &code_challenge_method=S256

The user sees a consent screen, signs in, and approves. On approval they're redirected to redirect_uri?code=mpc_…&state=… (the code is one-time, valid 5 minutes). On denial: ?error=access_denied&state=….

c. Exchange the code for a token

curl -X POST https://api.miletrades.site/api/v1/oauth/token \
  -d grant_type=authorization_code \
  -d code=mpc_xxxxxxxx \
  -d redirect_uri=https://myapp.com/callback \
  -d code_verifier=YOUR_VERIFIER \
  -d app_id=YOUR_APP_ID

// 200 OK
{
  "access_token": "mp_xxxxxxxx",  // a scoped, app-bound token (does not expire)
  "token_type":   "bearer",
  "scope":        "read trade",
  "app_id":       "08123456"
}

Errors return {"error":"...","error_description":"..."} (e.g. invalid_grant, unsupported_grant_type).

3 · Trade over the gateway (/ws/v1)

The gateway is a Deriv-style JSON WebSocket. Connect, authorize with the user's token, then call. Pass your app_id as a query param so trades are attributed to your app.

Message envelope

Each request is a JSON object whose first key names the call. Optional envelope fields: req_id (echoed back), subscribe: 1 (stream updates), passthrough. Responses carry msg_type, echo_req, the payload under the msg_type key, and subscription.id when streaming. Errors: {"error":{"code":"...","message":"..."}}.

End-to-end example — connect, price, buy, settle

const ws = new WebSocket("wss://api.miletrades.site/ws/v1?app_id=YOUR_APP_ID");

ws.onopen = () => ws.send(JSON.stringify({ authorize: "mp_USER_TOKEN" }));

ws.onmessage = (ev) => {
  const msg = JSON.parse(ev.data);
  if (msg.error) return console.error(msg.error.code, msg.error.message);

  switch (msg.msg_type) {
    case "authorize":
      // Ask for a price: "digit over 1" on Volatility 100, $1 stake.
      ws.send(JSON.stringify({
        proposal: 1, req_id: 1,
        contract_type: "DIGITOVER",   // OVER/UNDER/MATCH/DIFFER/EVEN/ODD
        symbol: "volatility_100",     // call active_symbols to list markets
        amount: 1.0,                  // stake in the account's currency
        barrier: 1                    // 0-9 for over/under & match/differ; omit for even/odd
      }));
      break;

    case "proposal":
      // Buy at (up to) the quoted ask price.
      ws.send(JSON.stringify({
        buy: msg.proposal.id, price: msg.proposal.ask_price, req_id: 2
      }));
      break;

    case "buy":
      console.log("Bought", msg.buy.contract_id, "for", msg.buy.buy_price);
      // Subscribe to follow it to settlement (~5 ticks later).
      ws.send(JSON.stringify({
        proposal_open_contract: msg.buy.contract_id, subscribe: 1, req_id: 3
      }));
      break;

    case "proposal_open_contract": {
      const c = msg.proposal_open_contract;
      if (c.is_sold) console.log("Settled:", c.status, "profit", c.profit);
      break;
    }
  }
};

Common calls

CallScopeShape
authorize{"authorize":"mp_..."}
active_symbols{"active_symbols":"brief"}
ticks{"ticks":"volatility_100","subscribe":1}
ticks_history{"ticks_history":"volatility_100","count":100}
balanceread{"balance":1,"account":"real","subscribe":1}
portfolioread{"portfolio":1}
proposalread{"proposal":1,"contract_type":"DIGITEVEN","symbol":"volatility_100","amount":1.0}
buytrade{"buy":"prop_...","price":1.05}
proposal_open_contractread{"proposal_open_contract":"<id>","subscribe":1}
statement / profit_tableread{"statement":1,"limit":50}
forget / forget_all{"forget":"sub_..."}

Contract types

DIGITOVER/OVER, DIGITUNDER/UNDER, DIGITMATCH/MATCH, DIGITDIFF/DIFFER, DIGITEVEN/EVEN, DIGITODD/ODD (case-insensitive). Over/under and match/differ take a barrier (0–9); even/odd take none. Contracts run ~5 ticks (≈5s); entry and duration are set by the server.

Limits & keep-alive

Rate limit ≈ 30 messages/sec (burst 60) per connection — over-limit returns a RateLimit error. The server sends WebSocket PING frames every 25s; you can also send {"ping":1}. Error codes: InvalidToken, AuthorizationRequired, UnrecognisedRequest, InputValidationFailed, RateLimit, UnknownSymbol, PermissionDenied, InternalError.

REST API

Present the token as Authorization: Bearer mp_…. Scope gates apply to token callers. Reads (list/get) need any valid token. Trades placed here are attributed to your app and earn the same 10% revenue-share as the gateway.

EndpointScope
POST/api/v1/contractstrade
POST/api/v1/contracts/bulktrade
GET/api/v1/contracts?status=open|closedread
POST/api/v1/deposits/{mpesa|ugx|tzs|crypto}payments
POST/api/v1/withdrawals/{mpesa|ugx|tzs|crypto}payments
POST/api/v1/accountstrade
POST/api/v1/accounts/transferpayments
GET/api/v1/currenciesread

POST /api/v1/contracts

curl -X POST https://api.miletrades.site/api/v1/contracts \
  -H "Authorization: Bearer mp_USER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "symbol": "volatility_100",
    "contract_type": "over",        // even|odd|match|differ|over|under
    "stake_cents": 100,             // minor units of the wallet currency
    "wallet_kind": "real",          // "real" | "demo"
    "params": { "threshold": 1 }    // over/under -> threshold 0-9; match/differ -> digit 0-9; even/odd -> none
  }'

// 200 OK
{ "contract": { "id": "...", "status": "pending", "payout_multiplier_bp": 12375, ... },
  "new_balance_cents": 41500 }

4 · Earnings & revenue-share

Your app earns a flat 10% of the net house profit (NGR = stake − payouts on settled real-money contracts, when the house wins) generated by the users who trade through your app. This is a platform rate — the same for every app. Attribution happens on both the gateway buy call and REST POST /api/v1/contracts, as long as the trade is placed with your OAuth-issued token (which carries your app_id).

GET https://api.miletrades.site/api/v1/apps/earnings   // Authorization: Bearer <your session or admin token>
{
  "apps": [
    { "app_id":"08123456", "name":"My Trading App",
      "ngr_cents":124500, "commission_cents":12450, "users":42 }  // 10% of NGR
  ]
}

Earnings also appear on the Developer apps page and are claimed on Refer & Earn.

Reference

app_idPublic client id — 8-digit numeric string.
mps_…App secret (client secret), shown once at registration.
mpc_…OAuth authorization code — one-time, 5-minute TTL.
mp_…Access token — scoped, app-bound, does not expire.

Need the exact request/response fields for deposits, withdrawals, or transfers? Ask us and we'll add them here.

Trading involves risk. Build responsibly. 18+.