Diffusal

API Reference

REST API endpoint specifications

The API server provides REST endpoints for reading protocol data. All write operations go directly to smart contracts.

Note: In the current implementation, the quote currency is always USDC. All trading pairs are denominated against USDC (e.g., BTC-USDC, ETH-USDC) because USDC is used as the collateral currency. Settlement is always in USDC.


Health Check

GET /health

Check if the API server is running.

Response:

Prop

Type

Example:

curl "http://localhost:8080/health"
{
  "status": "ok",
  "timestamp": 1736409000000
}

Base URL

EnvironmentURL
Localhttp://localhost:8080
Productionhttps://api.diffusal.xyz

OpenAPI Documentation

The API provides an interactive OpenAPI (Swagger) interface for exploring endpoints:

EnvironmentOpenAPI URL
Localhttp://localhost:8080/openapi
Productionhttps://api.diffusal.xyz/openapi

Additional formats:

  • JSON spec: /openapi/json
  • YAML spec: /openapi/yaml
  • LLM-friendly: /llms.txt (markdown format optimized for AI consumption)

Authentication Endpoints

Authentication uses SIWE (Sign-In with Ethereum) via Better Auth. See Authentication for the full flow.

Better Auth endpoints are mounted at /api/auth/*. Custom wallet endpoints are at /auth/api/*.

Authentication Flow

CLIENT                      WALLET                  SERVER
  │                           │                        │
  ├── GET /api/auth/siwe/nonce?walletAddress=0x... ───►│
  │                           │                        │
  │◄── { nonce: "random32chars" }  ────────────────────┤
  │                           │                        │
  │  Construct SIWE message   │                        │
  │  (EIP-4361)               │                        │
  │                           │                        │
  ├── Request signature ──────►│                       │
  │                           │                        │
  │◄── Signature (0x...) ─────┤                        │
  │                           │                        │
  ├── POST /api/auth/siwe/verify ─────────────────────►│
  │    { message, signature }                          │
  │                           │  Verify signature      │
  │                           │  Create session        │
  │◄── { session, user } + Set-Cookie  ────────────────┤
  │                           │                        │
  │  ─────────────────────────────────────────────────┐│
  │  Subsequent requests include session cookie       ││
  │  ─────────────────────────────────────────────────┘│
  │                           │                        │
  ├── GET /account/me (with cookie) ──────────────────►│
  │                           │  Validate session      │
  │◄── User profile data  ─────────────────────────────┤

GET /api/auth/siwe/nonce

Request a nonce for SIWE message signing.

ParameterTypeDescription
walletAddressqueryUser's Ethereum address

Response:

FieldTypeDescription
noncestringRandom 32-character nonce

Example:

curl "http://localhost:8080/api/auth/siwe/nonce?walletAddress=0x742d35Cc..."
{
  "nonce": "abc123xyz789def456ghi012jkl345mno"
}

POST /api/auth/siwe/verify

Verify a signed SIWE message and create a session.

Request Body:

FieldTypeDescription
messagestringSIWE message (EIP-4361 format)
signaturestringWallet signature

Response:

FieldTypeDescription
sessionobjectSession details
userobjectUser profile

Note: Session cookie is set automatically.

Example:

curl -X POST "http://localhost:8080/api/auth/siwe/verify" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "localhost:3000 wants you to sign in with your Ethereum account:\n0x742d35Cc...\n\nSign in to Diffusal\n\nURI: http://localhost:3000\nVersion: 1\nChain ID: 10143\nNonce: abc123xyz789def456ghi012jkl345mno\nIssued At: 2024-01-09T12:00:00.000Z",
    "signature": "0x..."
  }'
{
  "session": {
    "id": "session_abc123",
    "expiresAt": "2024-01-16T12:00:00.000Z"
  },
  "user": {
    "id": "user_xyz789",
    "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f1F321"
  }
}

POST /api/auth/sign-out

Sign out and invalidate the current session.

Example:

curl -X POST "http://localhost:8080/api/auth/sign-out" \
  -H "Cookie: session=..."
{
  "success": true
}

GET /api/auth/session

Get current session information.

Response:

FieldTypeDescription
userobjectUser profile (or null)
sessionobjectSession details (or null)

Example:

curl "http://localhost:8080/api/auth/session" \
  -H "Cookie: session=..."
{
  "user": {
    "id": "user_xyz789",
    "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f1F321"
  },
  "session": {
    "id": "session_abc123",
    "expiresAt": "2024-01-16T12:00:00.000Z"
  }
}

GET /auth/api/wallets

List all wallets linked to the current user (requires auth).

Response:

Prop

Type

Wallet Object:

Prop

Type

Example:

curl "http://localhost:8080/auth/api/wallets" \
  -H "Cookie: session=..."
{
  "wallets": [
    {
      "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f1F321",
      "isPrimary": true,
      "linkedAt": "2024-01-09T12:00:00.000Z"
    }
  ],
  "total": 1
}

GET /auth/api/wallets/primary

Get the primary wallet address for the current user (requires auth).

Response:

Prop

Type

Example:

curl "http://localhost:8080/auth/api/wallets/primary" \
  -H "Cookie: session=..."
{
  "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f1F321",
  "userId": "user_abc123"
}

POST /auth/api/wallets/verify

Verify wallet ownership using SIWE signature.

Request Body:

Prop

Type

Response:

Prop

Type

Example:

curl -X POST "http://localhost:8080/auth/api/wallets/verify" \
  -H "Content-Type: application/json" \
  -H "Cookie: session=..." \
  -d '{
    "message": "localhost:3000 wants you to sign in...",
    "signature": "0x...",
    "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f1F321"
  }'
{
  "success": true,
  "message": "Wallet verified",
  "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f1F321"
}

Public Market Data

No authentication required for these endpoints.

GET /markets

List all active options markets (series).

Query Parameters:

Prop

Type

Pagination: Use cursor from nextCursor in the response to fetch the next page. Returns up to limit results (default 20, max 100).

Response:

Prop

Type

Market Object:

Prop

Type

Example:

curl "http://localhost:8080/markets?limit=10&pairId=0x..."
{
  "markets": [
    {
      "seriesId": "0x1a2b3c4d...",
      "symbol": "BTC-82000-C-1736409600",
      "pairId": "0xabcd1234...",
      "pairSymbol": "BTC-USDC",
      "strike": "82000000000000000000000",
      "expiry": 1736409600,
      "isCall": true,
      "isSettled": false
    }
  ],
  "total": 1,
  "hasMore": false,
  "nextCursor": null
}

GET /markets/pairs

List all registered trading pairs.

Response:

Prop

Type

Pair Object:

Prop

Type

Example:

curl "http://localhost:8080/markets/pairs"
{
  "pairs": [
    {
      "pairId": "0xabcd1234...",
      "symbol": "BTC-USDC",
      "baseCurrency": "BTC",
      "quoteCurrency": "USDC"
    },
    {
      "pairId": "0xef567890...",
      "symbol": "ETH-USDC",
      "baseCurrency": "ETH",
      "quoteCurrency": "USDC"
    }
  ]
}

GET /markets/orderbook/:symbol

Get aggregated order book for a series.

Path Parameters:

Prop

Type

Query Parameters:

Prop

Type

Response:

Prop

Type

Level Object:

Prop

Type

Example:

curl "http://localhost:8080/markets/orderbook/BTC-82000-C-1736409600?depth=5"
{
  "symbol": "BTC-82000-C-1736409600",
  "seriesId": "0x1a2b3c4d...",
  "bids": [
    {
      "price": "1500000000000000000",
      "size": "5000000000000000000",
      "orderCount": 3
    },
    {
      "price": "1400000000000000000",
      "size": "10000000000000000000",
      "orderCount": 5
    }
  ],
  "asks": [
    {
      "price": "1600000000000000000",
      "size": "3000000000000000000",
      "orderCount": 2
    },
    {
      "price": "1700000000000000000",
      "size": "8000000000000000000",
      "orderCount": 4
    }
  ],
  "timestamp": 1736409500000
}

GET /markets/ticker/:symbol

Get 24-hour statistics and Greeks for a series.

Path Parameters:

Prop

Type

Response:

Prop

Type

Example:

curl "http://localhost:8080/markets/ticker/BTC-82000-C-1736409600"
{
  "symbol": "BTC-82000-C-1736409600",
  "seriesId": "0x1a2b3c4d...",
  "markPrice": "1500000000000000000",
  "indexPrice": "81500000000000000000000",
  "bestBid": "1450000000000000000",
  "bestAsk": "1550000000000000000",
  "delta": "550000000000000000",
  "gamma": "12000000000000000",
  "vega": "8500000000000000000",
  "theta": "-250000000000000000",
  "rho": "45000000000000000",
  "iv": "650000000000000000",
  "volume24h": "125000000000",
  "trades24h": 47,
  "priceChange24h": "5200000000000000000",
  "high24h": "1600000000000000000",
  "low24h": "1350000000000000000",
  "openInterest": "50000000000000000000",
  "lastTradeAt": 1736408900
}

GET /markets/trades/:symbol

Get recent trades for a series.

Path Parameters:

Prop

Type

Query Parameters:

Prop

Type

Pagination: Use before with the timestamp of the last trade to fetch older trades. Returns up to limit results (default 20, max 100).

Response:

Prop

Type

Trade Object:

Prop

Type

Example:

curl "http://localhost:8080/markets/trades/BTC-82000-C-1736409600?limit=5"
{
  "trades": [
    {
      "id": "trade_001",
      "price": "1500000000000000000",
      "size": "1000000000000000000",
      "side": "buy",
      "timestamp": 1736408900
    },
    {
      "id": "trade_002",
      "price": "1480000000000000000",
      "size": "2500000000000000000",
      "side": "sell",
      "timestamp": 1736408800
    }
  ],
  "nextCursor": "1736408800"
}

GET /markets/oracle/:pairId

Get oracle data for an underlying pair.

Path Parameters:

Prop

Type

Response:

Prop

Type

Example:

curl "http://localhost:8080/markets/oracle/0xabcd1234..."
{
  "pairId": "0xabcd1234...",
  "symbol": "BTC-USDC",
  "spotPrice": "81500000000000000000000",
  "volatility": "650000000000000000",
  "riskFreeRate": "50000000000000000",
  "lastUpdate": 1736409000
}

GET /markets/contracts

Get deployed contract addresses and ABIs.

Response:

Prop

Type

Example:

curl "http://localhost:8080/markets/contracts"
{
  "chainId": 10143,
  "contracts": {
    "DiffusalOracle": "0xa0d9da3a88e467e3a6a5a5666979a5784eb8d942",
    "DiffusalOptionsQuoter": "0xd18b41918f5f5d943c26e58bd2bc5ede1ae03630",
    "TestnetUSDC": "0xbdeaba226442621c7a9841a689284cfb1c4178e8",
    "DiffusalOptionsPositionManager": "0xaacdabcc225b330f9f0d4b671138286b5d792288",
    "DiffusalOptionsSeriesRegistry": "0xd880b541cf4d21635955d77cdb96498b4d937864",
    "DiffusalCollateralVault": "0xc6fb31b62480eca5412bbe127d61aeb3c386fc25",
    "DiffusalOptionsOrderBook": "0xcc064226992dd20de821200be8d3e612bf39d7d4",
    "DiffusalOptionsRFQ": "0x560fcf29c95e775574ad44b5d50577f5a5c3d8a6"
  }
}

Use these addresses with the contract ABIs to interact with the protocol. See On-Chain Guide for contract interaction patterns.

GET /markets/open-interest

List open interest and caps for all trading pairs.

Response:

Prop

Type

Open Interest Object:

Prop

Type

Example:

curl "http://localhost:8080/markets/open-interest"
{
  "pairs": [
    {
      "pairId": "0xabcd1234...",
      "symbol": "BTC-USDC",
      "callOI": "50000000000000000000000",
      "putOI": "35000000000000000000000",
      "maxCallOI": "100000000000000000000000",
      "maxPutOI": "100000000000000000000000",
      "callsRemaining": "50000000000000000000000",
      "putsRemaining": "65000000000000000000000"
    },
    {
      "pairId": "0xef567890...",
      "symbol": "ETH-USDC",
      "callOI": "75000000000000000000000",
      "putOI": "60000000000000000000000",
      "maxCallOI": "200000000000000000000000",
      "maxPutOI": "200000000000000000000000",
      "callsRemaining": "125000000000000000000000",
      "putsRemaining": "140000000000000000000000"
    }
  ]
}

Field Descriptions:

FieldDescription
callOITotal open interest for call options (WAD format, 18 decimals)
putOITotal open interest for put options (WAD format, 18 decimals)
maxCallOIMaximum allowed call OI set by MMM (0 = unlimited)
maxPutOIMaximum allowed put OI set by MMM (0 = unlimited)
callsRemainingRemaining capacity for new call positions
putsRemainingRemaining capacity for new put positions

Note: Open interest is tracked at the pair level, aggregating all series (strikes and expiries) for that pair. This prevents users from bypassing caps by creating positions across different strikes.

GET /markets/open-interest/:pairId

Get open interest and caps for a specific trading pair.

Path Parameters:

Prop

Type

Response:

Prop

Type

Example:

curl "http://localhost:8080/markets/open-interest/0xabcd1234..."
{
  "data": {
    "pairId": "0xabcd1234...",
    "symbol": "BTC-USDC",
    "callOI": "50000000000000000000000",
    "putOI": "35000000000000000000000",
    "maxCallOI": "100000000000000000000000",
    "maxPutOI": "100000000000000000000000",
    "callsRemaining": "50000000000000000000000",
    "putsRemaining": "65000000000000000000000"
  }
}

Account Data (Auth Required)

These endpoints require authentication. Include the session cookie or JWT token.

GET /account/me

Get current user profile and aggregate metrics.

Response:

Prop

Type

Example:

curl "http://localhost:8080/account/me" \
  -H "Cookie: session=..."
{
  "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f1F321",
  "portfolioCount": 2,
  "totalEquity": "125000000000",
  "totalDeposit": "100000000000",
  "totalUnrealizedPnl": "25000000000",
  "isHealthy": true,
  "positionCount": 5
}

GET /account/portfolios

List all user portfolios with summary data.

Response:

Prop

Type

Portfolio Summary:

Prop

Type

Example:

curl "http://localhost:8080/account/portfolios" \
  -H "Cookie: session=..."
{
  "portfolios": [
    {
      "id": 1,
      "positionCount": 3,
      "equity": "75000000000",
      "deposit": "60000000000",
      "isHealthy": true,
      "createdAt": 1736000000
    },
    {
      "id": 2,
      "positionCount": 2,
      "equity": "50000000000",
      "deposit": "40000000000",
      "isHealthy": true,
      "createdAt": 1736100000
    }
  ]
}

GET /account/portfolios/:id

Get detailed portfolio information with margin calculations.

Path Parameters:

Prop

Type

Response:

Prop

Type

Example:

curl "http://localhost:8080/account/portfolios/1" \
  -H "Cookie: session=..."
{
  "id": 1,
  "deposit": "60000000000",
  "equity": "75000000000",
  "unrealizedPnl": "15000000000",
  "initialMargin": "20000000000",
  "maintenanceMargin": "16000000000",
  "maxWithdraw": "55000000000",
  "isHealthy": true,
  "isLiquidatable": false,
  "healthRatio": "4687500000000000000",
  "positionCount": 3
}

GET /account/portfolios/:id/positions

Get all positions in a portfolio.

Path Parameters:

Prop

Type

Response:

Prop

Type

Position Object:

Prop

Type

Example:

curl "http://localhost:8080/account/portfolios/1/positions" \
  -H "Cookie: session=..."
{
  "positions": [
    {
      "seriesId": "0x1a2b3c4d...",
      "symbol": "BTC-82000-C-1736409600",
      "optionBalance": "5000000000000000000",
      "premiumBalance": "7500000000000000000",
      "entryPrice": "1500000000000000000",
      "realizedPnl": "0",
      "markPrice": "1650000000000000000",
      "notional": "407500000000",
      "unrealizedPnl": "7500000000",
      "delta": "2750000000000000000",
      "gamma": "60000000000000000",
      "vega": "42500000000000000000",
      "theta": "-1250000000000000000"
    }
  ]
}

GET /account/portfolios/:id/collateral

Get collateral details for a portfolio.

Path Parameters:

Prop

Type

Response:

Prop

Type

Example:

curl "http://localhost:8080/account/portfolios/1/collateral" \
  -H "Cookie: session=..."
{
  "deposit": "60000000000",
  "maxWithdraw": "55000000000",
  "locked": "5000000000"
}

GET /account/orders

Get user's open orders across all portfolios.

Query Parameters:

Prop

Type

Response:

Prop

Type

Order Object:

Prop

Type

Example:

curl "http://localhost:8080/account/orders?portfolioId=1" \
  -H "Cookie: session=..."
{
  "orders": [
    {
      "orderId": "0xorder123...",
      "seriesId": "0x1a2b3c4d...",
      "symbol": "BTC-82000-C-1736409600",
      "portfolioId": 1,
      "side": "buy",
      "price": "1500000000000000000",
      "size": "2000000000000000000",
      "filled": "500000000000000000",
      "remaining": "1500000000000000000",
      "status": "partial",
      "createdAt": 1736400000
    }
  ]
}

GET /account/trades

Get user's trade history.

Query Parameters:

Prop

Type

Pagination: Use before with the trade ID from nextCursor to fetch older trades. Returns up to limit results (default 20, max 100).

Response:

Prop

Type

User Trade Object:

Prop

Type

Example:

curl "http://localhost:8080/account/trades?portfolioId=1&limit=5" \
  -H "Cookie: session=..."
{
  "trades": [
    {
      "id": "trade_abc123",
      "seriesId": "0x1a2b3c4d...",
      "symbol": "BTC-82000-C-1736409600",
      "portfolioId": 1,
      "side": "buy",
      "price": "1500000000000000000",
      "size": "1000000000000000000",
      "fee": "150000",
      "timestamp": 1736408900
    }
  ],
  "nextCursor": "trade_abc122"
}

GET /account/fees

Get user's fee summary.

Response:

Prop

Type

Example:

curl "http://localhost:8080/account/fees" \
  -H "Cookie: session=..."
{
  "totalFeesPaid": "1250000000",
  "fees24h": "150000000",
  "makerFees": "500000000",
  "takerFees": "750000000",
  "rebatesReceived": "100000000"
}

Transaction Helpers

These endpoints help prepare transaction parameters. No authentication required.

GET /helpers/series-id

Compute series ID from parameters.

Query Parameters:

Prop

Type

Response:

Prop

Type

Example:

curl "http://localhost:8080/helpers/series-id?pairId=0xabcd...&strike=82000000000000000000000&expiry=1736409600&isCall=true"
{
  "seriesId": "0x1a2b3c4d5e6f..."
}

GET /helpers/pair-id

Compute pair ID from pair name.

Query Parameters:

Prop

Type

Response:

Prop

Type

Example:

curl "http://localhost:8080/helpers/pair-id?name=BTC-USDC"
{
  "pairId": "0xabcd1234..."
}

GET /helpers/tick-decimals/:pairId

Get current tick decimals for a pair.

Path Parameters:

Prop

Type

Response:

Prop

Type

Example:

curl "http://localhost:8080/helpers/tick-decimals/0xabcd1234..."
{
  "tickDecimals": 4,
  "spotPrice": "81500000000000000000000"
}

GET /helpers/tick-to-price

Convert tick to price.

Query Parameters:

Prop

Type

Response:

Prop

Type

Example:

curl "http://localhost:8080/helpers/tick-to-price?tick=15000&tickDecimals=4"
{
  "price": "1500000000000000000"
}

GET /helpers/price-to-tick

Convert price to tick.

Query Parameters:

Prop

Type

Response:

Prop

Type

Example:

curl "http://localhost:8080/helpers/price-to-tick?price=1500000000000000000&tickDecimals=4&roundUp=false"
{
  "tick": "15000"
}

POST /helpers/symbol/translate

Translate a symbol between Diffusal and user-facing formats.

Symbol Formats:

FormatPatternExample
DiffusalUNDERLYING-STRIKE-TYPE-EXPIRYBTC-82000-C-1736409600
User-facingUNDERLYING-DDMMMYY-STRIKE-TYPEBTC-09JAN26-82000-C

Strikes support decimals using underscore (_) as separator:

  • ETH-3550_5-C-... = strike $3,550.50
  • SHIB-0_00001234-C-... = strike $0.00001234

See Reference for complete examples covering all price ranges and edge cases.

Request Body:

Prop

Type

Response:

Prop

Type

Example - User-facing to Diffusal:

curl -X POST http://localhost:8080/helpers/symbol/translate \
  -H "Content-Type: application/json" \
  -d '{"symbol": "BTC-09JAN26-82000-C"}'

Response:

{
  "input": "BTC-09JAN26-82000-C",
  "inputFormat": "external",
  "output": "BTC-82000-C-1736409600",
  "outputFormat": "diffusal"
}

Example - Diffusal to User-facing:

curl -X POST http://localhost:8080/helpers/symbol/translate \
  -H "Content-Type: application/json" \
  -d '{"symbol": "BTC-82000-C-1736409600-SHORT"}'

Response:

{
  "input": "BTC-82000-C-1736409600-SHORT",
  "inputFormat": "diffusal",
  "output": "BTC-09JAN26-82000-C",
  "outputFormat": "external",
  "isShort": true
}

Example - Decimal Strike:

curl -X POST http://localhost:8080/helpers/symbol/translate \
  -H "Content-Type: application/json" \
  -d '{"symbol": "ETH-3550_5-C-1736409600"}'

Response:

{
  "input": "ETH-3550_5-C-1736409600",
  "inputFormat": "diffusal",
  "output": "ETH-09JAN26-3550_5-C",
  "outputFormat": "external"
}

POST /helpers/symbol/translate/batch

Batch translate multiple symbols between formats.

Request Body:

Prop

Type

Response:

Prop

Type

Example:

curl -X POST http://localhost:8080/helpers/symbol/translate/batch \
  -H "Content-Type: application/json" \
  -d '{"symbols": ["BTC-09JAN26-82000-C", "ETH-82000-P-1736409600"]}'

Response:

{
  "results": [
    {
      "input": "BTC-09JAN26-82000-C",
      "inputFormat": "external",
      "output": "BTC-82000-C-1736409600",
      "outputFormat": "diffusal"
    },
    {
      "input": "ETH-82000-P-1736409600",
      "inputFormat": "diffusal",
      "output": "ETH-09JAN26-82000-P",
      "outputFormat": "external"
    }
  ],
  "errors": []
}

Error Responses

All endpoints return consistent error responses:

HTTP StatusDescription
400Bad request (invalid parameters)
401Unauthorized (auth required)
403Forbidden (insufficient permissions)
404Not found
429Rate limited
500Internal server error

Error Response Format:

FieldTypeDescription
errorstringError code
messagestringHuman-readable message
detailsobjectAdditional error details (optional)

Rate Limits

Rate limits vary based on authentication status. Authenticated users (with valid session) receive higher limits.

Unauthenticated Users (by IP)

Endpoint CategoryRate Limit
Public market data60 req/min
Helpers100 req/min
Auth10 req/min

Authenticated Users (by wallet address)

Endpoint CategoryRate Limit
Public market data300 req/min
Helpers300 req/min
Account data120 req/min
Auth20 req/min

Rate limit headers are exposed for future use. Currently, rate limit information is only available in 429 error responses.


Data Freshness

Understanding data update frequencies helps build responsive integrations:

Data TypeSourceUpdate FrequencyTypical Latency
OrderbookIndexed eventsReal-time~1 block
Ticker / GreeksComputed on requestPer requestReal-time
Oracle pricesOn-chain oracle~30 seconds5-30s
Markets listIndexed series data~60 seconds background refresh~1 min
Positions / BalancesIndexed eventsReal-time~1-2 blocks
TradesIndexed eventsReal-time~1 block

WebSocket vs REST

ChannelRecommendation
orderbook:*Use WebSocket for live updates; REST for initial load
ticker:*Use WebSocket for price changes; REST for one-off queries
trades:*Use WebSocket for real-time fills; REST for history
positions, collateralWebSocket recommended for active trading

On this page