HTTP API Reference
REST API for querying bot state and automating workflows
Overview
The Wealth trading bot exposes an HTTP API alongside the metrics server for querying live bot state. This enables:
- Automation scripts to monitor positions and balances
- Custom dashboards to display real-time data
- Alert integrations with external monitoring systems
- Programmatic position management workflows
Base URL
http://localhost:9090
The port is configurable via WEALTH__OBSERVABILITY__METRICS_PORT (default: 9090).
Data Freshness
| Endpoint | Data Source | API Quota Impact |
|---|---|---|
/api/positions | Live exchange fetch | Consumes quota |
/api/funding_rates | Cached repository | None (fast) |
/api/balances | Cached repository | None (may be stale) |
/api/arbitrage_positions | Cached repository | None (fast) |
Endpoints
GET /api/positions
Fetches current open positions directly from exchanges. This endpoint queries each configured exchange in parallel.
⚠️ Note: This endpoint consumes exchange API quota. Use
/api/arbitrage_positionsfor cached data without API impact.
Request:
curl http://localhost:9090/api/positions
Response:
{
"positions": [
{
"exchange": "BinanceFutures",
"position": {
"symbol": "BTCUSDT",
"side": "Long",
"quantity": "0.1",
"entry_price": "95000",
"mark_price": "96000",
"unrealized_pnl": "100",
"leverage": "10",
"liquidation_price": "86000"
}
},
{
"exchange": "Bybit",
"position": {
"symbol": "BTCUSDT",
"side": "Short",
"quantity": "0.1",
"entry_price": "95100",
"mark_price": "96000",
"unrealized_pnl": "-90",
"leverage": "10",
"liquidation_price": "104500"
}
}
],
"timestamp": "2024-12-27T10:30:00Z"
}
Response Fields:
| Field | Type | Description |
|---|---|---|
positions | array | List of positions with exchange info |
positions[].exchange | string | Exchange name (BinanceFutures, Bybit, HyperLiquid, Aster) |
positions[].position.symbol | string | Trading pair symbol |
positions[].position.side | string | Long or Short |
positions[].position.quantity | decimal | Position size in base asset |
positions[].position.entry_price | decimal | Average entry price |
positions[].position.mark_price | decimal | Current mark price |
positions[].position.unrealized_pnl | decimal | Unrealized P&L in quote asset |
positions[].position.leverage | decimal | Current leverage |
positions[].position.liquidation_price | decimal | Liquidation price (null if not available) |
timestamp | datetime | ISO 8601 timestamp of response |
Error Handling:
The endpoint returns partial results if some exchanges fail. Only returns an error if all exchanges fail:
{
"error": "Failed to fetch positions from all exchanges"
}
Check logs for details on individual exchange failures.
GET /api/funding_rates
Returns cached funding rates from the in-memory repository. Fast and does not consume API quota.
Request:
curl http://localhost:9090/api/funding_rates
Response:
{
"rates": [
{
"exchange": "BinanceFutures",
"symbol": "BTCUSDT",
"rate": "0.0001",
"next_funding_time": "2024-12-27T16:00:00Z",
"last_updated": "2024-12-27T10:25:00Z"
},
{
"exchange": "Bybit",
"symbol": "BTCUSDT",
"rate": "-0.00015",
"next_funding_time": "2024-12-27T16:00:00Z",
"last_updated": "2024-12-27T10:25:30Z"
},
{
"exchange": "HyperLiquid",
"symbol": "BTCUSDT",
"rate": "0.00008",
"next_funding_time": "2024-12-27T11:00:00Z",
"last_updated": "2024-12-27T10:29:00Z"
}
],
"timestamp": "2024-12-27T10:30:00Z"
}
Response Fields:
| Field | Type | Description |
|---|---|---|
rates | array | List of funding rate entries |
rates[].exchange | string | Exchange name |
rates[].symbol | string | Trading pair symbol |
rates[].rate | decimal | Current funding rate (e.g., 0.0001 = 0.01%) |
rates[].next_funding_time | datetime | Next funding settlement time |
rates[].last_updated | datetime | When this rate was last fetched |
timestamp | datetime | ISO 8601 timestamp of response |
Note: Funding rate values are in decimal form. Multiply by 100 to get percentage (e.g., 0.0001 = 0.01%).
GET /api/balances
Returns cached balances from the balance repository. Fast response but may be slightly stale.
Request:
curl http://localhost:9090/api/balances
Response:
{
"balances": [
{
"exchange": "BinanceFutures",
"asset": "USDT",
"free": "10000",
"total": "12000",
"wallet_balance": "11500",
"reserved": "2000"
},
{
"exchange": "Bybit",
"asset": "USDT",
"free": "8500",
"total": "8500",
"wallet_balance": "8200",
"reserved": "0"
},
{
"exchange": "HyperLiquid",
"asset": "USDC",
"free": "5000",
"total": "7500",
"wallet_balance": "7000",
"reserved": "2500"
}
],
"timestamp": "2024-12-27T10:30:00Z"
}
Response Fields:
| Field | Type | Description |
|---|---|---|
balances | array | List of balance entries |
balances[].exchange | string | Exchange name |
balances[].asset | string | Asset symbol (e.g., USDT, USDC) |
balances[].free | decimal | Available balance for trading (use for position sizing) |
balances[].total | decimal | Total balance including unrealized P&L |
balances[].wallet_balance | decimal | Deposited capital (stable, excludes unrealized P&L) |
balances[].reserved | decimal | Balance locked in orders/positions |
timestamp | datetime | ISO 8601 timestamp of response |
Balance Types:
freeis the correct balance for position sizing as it respects locked margin and automatically de-risks when positions are underwater.wallet_balanceprovides stable deposited capital for reporting/tracking that doesn't fluctuate with unrealized P&L.
GET /api/arbitrage_positions
Returns cached arbitrage positions with cumulative funding collected. This is the recommended endpoint for monitoring active arbitrage pairs.
Request:
curl http://localhost:9090/api/arbitrage_positions
Response:
{
"positions": [
{
"symbol": "BTCUSDT",
"long_exchange": "Bybit",
"short_exchange": "BinanceFutures",
"entry_spread": "0.00025",
"position_size": "0.1",
"entry_time": "2024-12-26T08:00:00Z",
"funding_collected": "12.50"
},
{
"symbol": "ETHUSDT",
"long_exchange": "HyperLiquid",
"short_exchange": "BinanceFutures",
"entry_spread": "0.0003",
"position_size": "1.5",
"entry_time": "2024-12-25T16:00:00Z",
"funding_collected": "28.75"
}
],
"total_funding_collected": "41.25",
"timestamp": "2024-12-27T10:30:00Z"
}
Response Fields:
| Field | Type | Description |
|---|---|---|
positions | array | List of active arbitrage positions |
positions[].symbol | string | Trading pair symbol |
positions[].long_exchange | string | Exchange holding long position |
positions[].short_exchange | string | Exchange holding short position |
positions[].entry_spread | decimal | Funding spread at entry |
positions[].position_size | decimal | Position size in base asset |
positions[].entry_time | datetime | When position was opened |
positions[].funding_collected | decimal | Cumulative funding collected (USD) |
total_funding_collected | decimal | Sum of all positions' funding |
timestamp | datetime | ISO 8601 timestamp of response |
Health Endpoints
In addition to the data API, the metrics server exposes health check endpoints for monitoring and container orchestration:
| Endpoint | Description | Use Case |
|---|---|---|
/health | Overall health status with details (JSON) | Human monitoring, dashboards |
/ready | Kubernetes readiness probe | K8s readiness gate |
/live | Kubernetes liveness probe | K8s container restart decisions |
/metrics | OpenTelemetry metrics info | OTLP configuration verification |
GET /health
Returns comprehensive health status including exchange connectivity and system metrics.
Request:
curl http://localhost:9090/health | jq
Response:
{
"status": "healthy",
"exchanges": {
"binance": "connected",
"bybit": "connected",
"hyperliquid": "connected"
},
"uptime_seconds": 3600
}
Response Fields:
| Field | Type | Description |
|---|---|---|
status | string | Overall status: healthy, degraded, or unhealthy |
exchanges | object | Per-exchange connection status |
uptime_seconds | integer | Bot uptime in seconds |
Status Meanings:
healthy- All exchanges connected, bot operating normallydegraded- Some exchanges unavailable, bot still operatingunhealthy- Critical failure, bot not functioning
GET /ready
Kubernetes-style readiness probe. Returns HTTP 200 when the bot is ready to accept traffic.
Request:
curl -s -o /dev/null -w "%{http_code}" http://localhost:9090/ready
Response:
200 OK- Bot is ready (connected to at least one exchange)503 Service Unavailable- Bot is not ready (still initializing or all exchanges failed)
Body (when ready):
{
"ready": true,
"timestamp": "2024-12-27T10:30:00Z"
}
GET /live
Kubernetes-style liveness probe. Returns HTTP 200 if the bot process is alive and responsive.
Request:
curl -s -o /dev/null -w "%{http_code}" http://localhost:9090/live
Response:
200 OK- Bot is alive503 Service Unavailable- Bot is unresponsive (should trigger container restart)
Body:
{
"alive": true,
"timestamp": "2024-12-27T10:30:00Z"
}
GET /metrics
Returns information about the OpenTelemetry metrics configuration (not Prometheus scrape format).
Request:
curl http://localhost:9090/metrics | jq
Response:
{
"status": "operational",
"backend": "OpenTelemetry OTLP",
"endpoint": "http://localhost:4317",
"protocol": "gRPC"
}
Note: For Prometheus-style metrics, configure OTLP export to your observability backend (Grafana Cloud, etc.) and query metrics there. See Monitoring Guide for details.
Kubernetes Deployment Example
apiVersion: apps/v1
kind: Deployment
metadata:
name: wealth-deployment
spec:
replicas: 1
selector:
matchLabels:
app: wealth
template:
metadata:
labels:
app: wealth
spec:
containers:
- name: wealth
image: ghcr.io/thiras/wealth:latest
ports:
- containerPort: 9090
livenessProbe:
httpGet:
path: /live
port: 9090
initialDelaySeconds: 10
periodSeconds: 30
readinessProbe:
httpGet:
path: /ready
port: 9090
initialDelaySeconds: 5
periodSeconds: 10
Usage Examples
Shell Scripts
Monitor total balance across exchanges:
#!/bin/bash
curl -s http://localhost:9090/api/balances | jq '
.balances
| group_by(.asset)
| map({asset: .[0].asset, total: (map(.total | tonumber) | add)})
'
Get current funding spread for a symbol:
#!/bin/bash
SYMBOL="BTCUSDT"
curl -s http://localhost:9090/api/funding_rates | jq --arg sym "$SYMBOL" '
.rates
| map(select(.symbol == $sym))
| sort_by(.rate)
| {min: .[0], max: .[-1], spread: ((.[-1].rate | tonumber) - (.[0].rate | tonumber))}
'
Python Integration
import requests
from datetime import datetime
BASE_URL = "http://localhost:9090"
def get_positions():
"""Fetch current positions from all exchanges."""
response = requests.get(f"{BASE_URL}/api/positions")
response.raise_for_status()
return response.json()
def get_arbitrage_summary():
"""Get arbitrage positions with funding collected."""
response = requests.get(f"{BASE_URL}/api/arbitrage_positions")
response.raise_for_status()
data = response.json()
print(f"Active positions: {len(data['positions'])}")
print(f"Total funding collected: ${data['total_funding_collected']}")
for pos in data['positions']:
print(f" {pos['symbol']}: {pos['long_exchange']} ↔ {pos['short_exchange']}, "
f"funding: ${pos['funding_collected']}")
def check_funding_opportunity(symbol: str, min_spread: float = 0.0004):
"""Check if funding spread exceeds threshold."""
response = requests.get(f"{BASE_URL}/api/funding_rates")
response.raise_for_status()
rates = [r for r in response.json()['rates'] if r['symbol'] == symbol]
if len(rates) < 2:
return None
rates_sorted = sorted(rates, key=lambda x: float(x['rate']))
spread = float(rates_sorted[-1]['rate']) - float(rates_sorted[0]['rate'])
if spread >= min_spread:
return {
'symbol': symbol,
'spread': spread,
'long_exchange': rates_sorted[0]['exchange'],
'short_exchange': rates_sorted[-1]['exchange']
}
return None
if __name__ == "__main__":
get_arbitrage_summary()
Prometheus/Alertmanager Integration
While the bot exports native Prometheus metrics, you can also create custom scrapers:
# prometheus.yml
scrape_configs:
- job_name: 'wealth-api'
metrics_path: /metrics
static_configs:
- targets: ['localhost:9090']
For custom alerts based on API data, use a script with Alertmanager webhook receiver.
Rate Limiting
The HTTP API itself has no rate limiting. However:
/api/positionstriggers exchange API calls (subject to exchange rate limits)- Other endpoints use cached data with no external API impact
Recommendation: Poll /api/positions no more than once per minute. Use /api/arbitrage_positions for frequent monitoring.
Error Responses
All endpoints return standard HTTP status codes:
| Code | Meaning |
|---|---|
200 | Success |
500 | Internal error (check logs) |
Error responses include a message:
{
"error": "Failed to fetch positions from all exchanges"
}
Configuration
Binding Address
By default, the API binds to all interfaces (0.0.0.0). For production, restrict to localhost:
[observability]
metrics_bind_address = "127.0.0.1"
metrics_port = 9090
Or via environment:
export WEALTH__OBSERVABILITY__METRICS_BIND_ADDRESS=127.0.0.1
Reverse Proxy
For external access, use a reverse proxy (nginx, Caddy) with authentication:
location /api/ {
auth_basic "Wealth API";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://127.0.0.1:9090;
}
See Also
- CLI Reference - Command-line interface for the same data
- Monitoring & Metrics - Prometheus metrics and Grafana dashboards
- Configuration Guide - Server configuration options