API Reference

Single endpoint, 20 operations, 5 DEXes. Every request is normalized — same params, same response shape, regardless of exchange.

Quick start

1
Get an API key
Create a key in your dashboard. Keys are prefixed mt_live_ (mainnet) or mt_test_ (testnet). Copy the full key — it's only shown once.
2
Make your first request
bash
curl -X POST https://api.mithril.money/v1 \
  -H "Content-Type: application/json" \
  -H "X-API-Key: mt_live_YOUR_KEY" \
  -d '{
    "operation": "getBalance",
    "credentialId": "YOUR_CREDENTIAL_ID"
  }'
3
Handle the response
json
{
  "success": true,
  "exchange_type": "paradex",
  "equity": "10452.38",
  "freeCollateral": "8200.00",
  "accountValue": "10452.38"
}

Authentication

Pass your API key via the X-API-Key header (preferred), or as a Bearer token:

bash
# Option 1: X-API-Key header (recommended)
curl -H "X-API-Key: mt_live_A7Kx..." https://api.mithril.money/v1

# Option 2: Authorization header
curl -H "Authorization: Bearer mt_live_A7Kx..." https://api.mithril.money/v1
Key prefix
mt_live_ (mainnet) · mt_test_ (testnet)
Key length
51 chars total (mt_live_ + 43 char body)
Key scope
Optional: restrict to credentials, operations, exchanges
Key display
Only first 12 chars shown after creation (mt_live_A7Kx)

A testnet key cannot access mainnet credentials and vice versa. Scopes are enforced per-key and cannot be widened after creation.

Request format

Endpoint
POST https://api.mithril.money/v1
Content-Type
application/json
Method
POST only (GET returns 405)

Every request body must include operation. Most operations also require credentialId — your exchange credential UUID from the Mithril platform.

json
{
  "operation":    "placeLimitOrder",   // required — operation name (camelCase)
  "credentialId": "9f2a...d841",       // required for most operations — exchange credential UUID
  "market":       "BTC-USD-PERP",      // market symbol (auto-translated per exchange)
  ...params                             // operation-specific parameters
}
Market symbols are normalized automatically. BTC-USD-PERP, BTC/USDT-P, and BTC all resolve correctly per exchange.

Operations

Operations are split by tier: Free for read-only market + account data, Builder+ for order placement and management.

Market data

Free
getOrderbook

Current orderbook for a market.

Parameters
credentialId
string
Exchange credential UUID
market
string
Market symbol (e.g. BTC-USD-PERP)
depth?
number
Number of levels to return (default: 20)
Response
{
  "success": true,
  "bids": [["94200.00", "1.5"], ["94190.00", "3.2"], ...],
  "asks": [["94210.00", "0.8"], ["94220.00", "2.1"], ...]
}
getMarkPrice

Current mark price for a perpetual market.

Parameters
credentialId
string
Exchange credential UUID
market
string
Market symbol
Response
{
  "success": true,
  "markPrice": "94205.50",
  "indexPrice": "94198.20"
}
getCandles

OHLCV candlestick data. Interval is normalized per exchange.

Parameters
credentialId
string
Exchange credential UUID
market
string
Market symbol
interval?
string
Candle interval in minutes: 1, 5, 15, 60, 240, 1440 (default: 60)
limit?
number
Number of candles (default: 100, max: 500)
Response
{
  "success": true,
  "candles": [
    { "time": 1710000000, "open": "93800", "high": "94500", "low": "93600", "close": "94200", "volume": "1204.5" },
    ...
  ]
}
getFundingRate

Current perpetual funding rate.

Parameters
credentialId
string
Exchange credential UUID
market
string
Market symbol
Response
{
  "success": true,
  "fundingRate": "0.0001",
  "nextFundingTime": 1710014400
}
getMarketsWithFunding

All available markets with current funding rates.

Parameters
credentialId
string
Exchange credential UUID
Response
{
  "success": true,
  "markets": [
    { "symbol": "BTC-USD-PERP", "markPrice": "94205", "fundingRate": "0.0001", "openInterest": "12400000" },
    ...
  ]
}
getFeeInfo

Maker/taker fees for a market in basis points (1 bps = 0.01%).

Parameters
credentialId
string
Exchange credential UUID
market
string
Market symbol
Response
{
  "success": true,
  "makerFee": "2",
  "takerFee": "5"
}

Account

Free
getBalance

Account equity, free collateral, and total account value.

Parameters
credentialId
string
Exchange credential UUID
Response
{
  "success": true,
  "exchange_type": "paradex",
  "equity": "10452.38",
  "freeCollateral": "8200.00",
  "accountValue": "10452.38"
}
getPositions

All open positions with entry price, size, and unrealized PnL.

Parameters
credentialId
string
Exchange credential UUID
market?
string
Filter to a specific market
Response
{
  "success": true,
  "positions": [
    {
      "market": "BTC-USD-PERP",
      "side": "LONG",
      "size": "0.1",
      "entryPrice": "92000.00",
      "markPrice": "94200.00",
      "unrealizedPnl": "220.00",
      "leverage": "10"
    }
  ]
}
getOrders

All open/pending orders.

Parameters
credentialId
string
Exchange credential UUID
market?
string
Filter to a specific market
Response
{
  "success": true,
  "orders": [
    {
      "id": "ord_abc123",
      "market": "ETH-USD-PERP",
      "side": "BUY",
      "type": "LIMIT",
      "size": "1.0",
      "price": "3200.00",
      "filledSize": "0",
      "status": "OPEN",
      "createdAt": 1710000000
    }
  ]
}
getFills

Trade fill history.

Parameters
credentialId
string
Exchange credential UUID
market
string
Market symbol
limit?
number
Max results (default: 100)
Response
{
  "success": true,
  "fills": [
    {
      "id": "fill_xyz",
      "market": "BTC-USD-PERP",
      "side": "BUY",
      "size": "0.05",
      "price": "93800.00",
      "fee": "0.235",
      "timestamp": 1710001000
    }
  ]
}
testCredentials

Verify that exchange credentials are valid and active.

Parameters
credentialId
string
Exchange credential UUID
Response
{
  "success": true,
  "valid": true,
  "exchange": "paradex"
}

Order placement

Builder+
placeLimitOrder

Place a limit order at a specific price.

Parameters
credentialId
string
Exchange credential UUID
market
string
Market symbol
side
"BUY" | "SELL"
Order direction
size
string
Size in base asset units
price
string
Limit price
postOnly?
boolean
Post-only mode — rejects if it would take (default: false)
reduceOnly?
boolean
Reduce-only — only decreases position size (default: false)
clientOrderId?
string
Custom order ID for deduplication
Request body
{
  "operation": "placeLimitOrder",
  "credentialId": "9f2a...d841",
  "market": "BTC-USD-PERP",
  "side": "BUY",
  "size": "0.01",
  "price": "94000"
}
Response
{
  "success": true,
  "orderId": "ord_abc123",
  "market": "BTC-USD-PERP",
  "side": "BUY",
  "size": "0.01",
  "price": "94000.00",
  "status": "OPEN"
}
placeMarketOrder

Execute immediately at the best available price.

Parameters
credentialId
string
Exchange credential UUID
market
string
Market symbol
side
"BUY" | "SELL"
Order direction
size
string
Size in base asset units
reduceOnly?
boolean
Reduce-only (default: false)
Response
{
  "success": true,
  "orderId": "ord_xyz789",
  "filledPrice": "94212.50",
  "filledSize": "0.01",
  "fee": "0.047"
}
placeStopOrder

Place a stop/trigger order. Activates when mark price hits trigger_price.

Parameters
credentialId
string
Exchange credential UUID
market
string
Market symbol
side
"BUY" | "SELL"
Order direction
size
string
Size in base asset units
triggerPrice
string
Price that activates the order
reduceOnly?
boolean
Default: true
Response
{
  "success": true,
  "orderId": "ord_stop1",
  "status": "PENDING",
  "triggerPrice": "92000.00"
}
placeBatchOrder

Submit up to 10 orders in a single request (atomic per exchange).

Parameters
credentialId
string
Exchange credential UUID
orders
Order[]
Array of order objects (max 10)
Request body
{
  "operation": "placeBatchOrder",
  "credentialId": "9f2a...d841",
  "orders": [
    { "market": "BTC-USD-PERP", "side": "BUY", "size": "0.01", "price": "93000", "orderType": "LIMIT" },
    { "market": "BTC-USD-PERP", "side": "SELL", "size": "0.01", "price": "95000", "orderType": "LIMIT" }
  ]
}
Response
{
  "success": true,
  "results": [
    { "orderId": "ord_a1", "status": "OPEN" },
    { "orderId": "ord_a2", "status": "OPEN" }
  ]
}
placeProtectionOrders

Place TP/SL orders on an existing position in one call.

Parameters
credentialId
string
Exchange credential UUID
market
string
Market symbol
side
"LONG" | "SHORT"
Position side
size
string
Position size
entryPrice
string
Position entry price
tpPercent?
number
Take-profit % from entry (e.g. 5 = 5%)
slPercent?
number
Stop-loss % from entry (e.g. 2 = 2%)
Response
{
  "success": true,
  "tpOrderId": "ord_tp1",
  "slOrderId": "ord_sl1"
}

Order management

Builder+
cancelOrder

Cancel a specific order by its exchange order ID.

Parameters
credentialId
string
Exchange credential UUID
orderId
string
Exchange order ID (from placement response)
market
string
Market symbol (required by some exchanges)
Response
{ "success": true }
cancelAllOrders

Cancel all open orders on a market.

Parameters
credentialId
string
Exchange credential UUID
market
string
Market symbol
Response
{ "success": true, "cancelledCount": 3 }
closeAllPositions

Close all open positions on a market with market orders.

Parameters
credentialId
string
Exchange credential UUID
market
string
Market symbol
Response
{ "success": true, "closedSize": "0.1", "avgClosePrice": "94180.00" }
setLeverage

Set cross-margin leverage for a market.

Parameters
credentialId
string
Exchange credential UUID
market
string
Market symbol
leverage
number
Leverage multiplier (e.g. 10)
Response
{ "success": true, "leverage": 10 }

Rate limits

Free
30 / min
5,000 / day
3 API keys
Builder
120 / min
50,000 / day
10 API keys
Pro
600 / min
500,000 / day
25 API keys

When a limit is exceeded, the gateway returns 429 with a Retry-After header (seconds to wait) and rate limit headers on every response:

X-RateLimit-Limit-Minute
Your per-minute limit
X-RateLimit-Remaining-Minute
Requests remaining this minute
X-RateLimit-Limit-Day
Your per-day limit
X-RateLimit-Remaining-Day
Requests remaining today
Retry-After
Seconds to wait (on 429 only)

Error codes

All errors follow the same envelope:

json
{
  "success": false,
  "code":    "RATE_LIMITED",
  "error":   "Rate limit exceeded. Retry after 2s."
}
MISSING_API_KEY
401 — No API key provided
INVALID_KEY_FORMAT
401 — Key does not start with mt_live_ or mt_test_
INVALID_API_KEY
401 — Key not found or hash mismatch
KEY_REVOKED
401 — Key has been manually revoked
KEY_EXPIRED
401 — Key has passed its expiry date
TIER_INSUFFICIENT
403 — Operation requires Builder or Pro tier
OPERATION_NOT_ALLOWED
403 — Operation not in this key's allowed_operations scope
CREDENTIAL_NOT_ALLOWED
403 — Credential not in this key's allowed_credentials scope
EXCHANGE_NOT_ALLOWED
403 — Exchange not in this key's allowed_exchanges scope
MISSING_OPERATION
400 — operation field missing from request body
INVALID_JSON
400 — Request body is not valid JSON
RATE_LIMITED
429 — Per-minute or per-day limit exceeded
EXCHANGE_ERROR
502 — Upstream exchange returned an error (details in error field)
INTERNAL_ERROR
500 — Gateway error (report if persistent)

Response headers

X-Request-Id
UUID for this specific request — include in bug reports
X-Request-Duration-Ms
Total gateway latency in milliseconds
X-RateLimit-Limit-Minute
Your per-minute limit
X-RateLimit-Remaining-Minute
Requests remaining this minute window
X-RateLimit-Limit-Day
Your per-day limit
X-RateLimit-Remaining-Day
Requests remaining today (resets at UTC midnight)

MCP setup

Mithril exposes all 20 operations as MCP tools via Streamable HTTP transport, letting Claude and other LLM agents trade directly. Available on Builder+.

Claude Desktop config

Add to ~/.claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

json
{
  "mcpServers": {
    "mithril": {
      "url": "https://pqnoeidcslevoexrduio.supabase.co/functions/v1/mithril-mcp-server",
      "headers": {
        "Authorization": "Bearer mt_live_YOUR_KEY"
      }
    }
  }
}

Claude Code config

json
// .claude/settings.json
{
  "mcpServers": {
    "mithril": {
      "url": "https://pqnoeidcslevoexrduio.supabase.co/functions/v1/mithril-mcp-server",
      "headers": {
        "Authorization": "Bearer mt_live_YOUR_KEY"
      }
    }
  }
}

How it works

The MCP server uses Streamable HTTP — stateless JSON-RPC over POST. Your API key is passed via the Authorization header. Tool names use snake_case (e.g. get_balance, place_limit_order) and are automatically converted to the gateway's camelCase operations.

Once connected, Claude can call tools like get_balance, place_limit_order, cancel_all_orders directly — 20 tools total covering all market data, account, order, and management operations.

Code examples

Python — place a limit order

python
import requests

API_KEY = "mt_live_YOUR_KEY"
BASE_URL = "https://api.mithril.money/v1"

def call(operation, **params):
    resp = requests.post(
        BASE_URL,
        headers={"X-API-Key": API_KEY, "Content-Type": "application/json"},
        json={"operation": operation, **params},
        timeout=10,
    )
    resp.raise_for_status()
    return resp.json()

# Get balance
balance = call("getBalance", credentialId="YOUR_CRED_ID")
print(f"Equity: {balance['equity']}")

# Place limit order
order = call(
    "placeLimitOrder",
    credentialId="YOUR_CRED_ID",
    market="BTC-USD-PERP",
    side="BUY",
    size="0.01",
    price="94000",
)
print(f"Order: {order['orderId']}")

TypeScript / Node.js

typescript
const API_KEY = "mt_live_YOUR_KEY";
const BASE = "https://api.mithril.money/v1";

async function call<T>(operation: string, params: Record<string, unknown> = {}): Promise<T> {
  const res = await fetch(BASE, {
    method: "POST",
    headers: { "Content-Type": "application/json", "X-API-Key": API_KEY },
    body: JSON.stringify({ operation, ...params }),
  });
  if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
  return res.json();
}

// Get all markets
const { markets } = await call<{ markets: any[] }>("getMarketsWithFunding", {
  credentialId: "YOUR_CRED_ID",
});

// Place a limit order
const order = await call("placeLimitOrder", {
  credentialId: "YOUR_CRED_ID",
  market: "ETH-USD-PERP",
  side: "BUY",
  size: "0.5",
  price: "3200",
  postOnly: true,
});
console.log("Order ID:", order.orderId);

Handling rate limits

python
import time, requests

def call_with_retry(operation, max_retries=3, **params):
    for attempt in range(max_retries):
        resp = requests.post(
            "https://api.mithril.money/v1",
            headers={"X-API-Key": API_KEY},
            json={"operation": operation, **params},
        )
        if resp.status_code == 429:
            wait = int(resp.headers.get("Retry-After", 60))
            print(f"Rate limited. Waiting {wait}s...")
            time.sleep(wait)
            continue
        resp.raise_for_status()
        return resp.json()
    raise Exception("Max retries exceeded")