API Reference
8 endpoints. Stable v1 contract. All requests require Authorization: Bearer <key>.
Authentication
Every API call must include your key in the Authorization header:
Authorization: Bearer axt_live_<your-key>
On missing or invalid key:
{
"error": "unauthorized",
"message": "Missing or invalid API key. Include header: Authorization: Bearer axt_live_<your-key>",
"docs": "https://axistruth.com/docs/api#authentication"
}
Rate limits
Limits are monthly (calendar month, UTC). On breach:
{
"error": "rate_limit_exceeded",
"message": "Rate limit exceeded. Free tier: 100 calls/mo. Pro: 50,000 calls/mo.",
"used": 100,
"limit": 100,
"resetAt": 1748822400
}
Response headers on every call:
| Header | Description |
|---|---|
X-RateLimit-Limit | Monthly call ceiling for your tier |
X-RateLimit-Remaining | Calls remaining this month |
X-RateLimit-Reset | Unix timestamp of next quota reset |
Retry-After | Present on 429 only — seconds until quota resets |
Error codes
| HTTP status | error field | When |
|---|---|---|
| 400 | bad_request | Schema validation failure or malformed JSON |
| 401 | unauthorized | Missing or invalid API key |
| 422 | validation_failed | Field-level constraint violation (with field pointer) |
| 429 | rate_limit_exceeded | Monthly quota exhausted |
| 500 | internal_server_error | Unexpected server error |
| 503 | service_unavailable | Billing service not configured (non-production environments) |
POST /v1/optimize-portfolio
POST /v1/optimize-portfolio
Allocates portfolio weights using Hierarchical Risk Parity (HRP). Requires at least 2 assets and 2 time-period rows. All auth + rate-limit rules apply.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
returns | number[][] | Yes | Return series. Outer array = time periods (min 2). Inner array = assets (min 1, must match assetNames length). |
assetNames | string[] | Yes | Asset labels in the same order as the inner return arrays. |
useShrinkage | boolean | No | Apply Ledoit-Wolf shrinkage to the covariance matrix. Default: false. Recommended when n_periods < 3 * n_assets. |
Response (200)
{
"weights": { "SPY": 0.38, "AGG": 0.62 },
"method": "hrp",
"riskContributions": { "SPY": 0.5, "AGG": 0.5 }
}
Example (curl)
curl -X POST https://api.axistruth.com/v1/optimize-portfolio \
-H "Authorization: Bearer axt_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"returns": [
[0.01, -0.005, 0.002],
[0.02, 0.010, -0.003],
[-0.01, 0.005, 0.004],
[0.005, -0.002, 0.001],
[0.015, 0.008, -0.002]
],
"assetNames": ["SPY", "AGG", "GLD"]
}'
Example (Python)
import requests
resp = requests.post(
"https://api.axistruth.com/v1/optimize-portfolio",
headers={"Authorization": "Bearer axt_live_YOUR_KEY_HERE"},
json={
"returns": [[0.01, -0.005], [0.02, 0.010], [-0.01, 0.005],
[0.005, -0.002], [0.015, 0.008]],
"assetNames": ["SPY", "AGG"],
},
)
data = resp.json()
print(data["weights"]) # {'SPY': 0.38, 'AGG': 0.62}
POST /v1/size-position
POST /v1/size-position
Computes Kelly-criterion position size. Two modes: continuous (variance-based) and discrete (win-probability-based). Applies a configurable cap (default 25%) to prevent over-sizing.
Request body — continuous mode
| Field | Type | Required | Description |
|---|---|---|---|
expectedReturn | number | Yes | Annualized expected excess return (e.g., 0.12 for 12%). |
variance | number | Yes | Annualized variance (e.g., 0.04 for 20% vol). |
riskFreeRate | number | No | Risk-free rate. Default: 0. |
fraction | number | No | Fractional Kelly multiplier (0, 1]. Default: 0.25. |
maxDrawdownFraction | number | No | Hard cap on recommended fraction. Default: 0.25. |
Request body — discrete mode
| Field | Type | Required | Description |
|---|---|---|---|
mode | "discrete" | Yes | Selects discrete Kelly formula. |
winProbability | number | Yes | Probability of a winning trade [0, 1]. |
winLossRatio | number | Yes | Ratio of average win to average loss (e.g., 2.0 = win twice as much as you lose). |
fraction | number | No | Fractional Kelly multiplier. Default: 0.25. |
maxDrawdownFraction | number | No | Hard cap. Default: 0.25. |
Response (200)
{
"kellyOptimal": 3.0,
"recommended": 0.75,
"clamped": true,
"cap": 0.25
}
kellyOptimal is the raw f* before cap. recommended is min(kellyOptimal * fraction, maxDrawdownFraction). clamped: true means the cap was binding.
Example (curl — continuous)
curl -X POST https://api.axistruth.com/v1/size-position \
-H "Authorization: Bearer axt_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{"expectedReturn": 0.12, "variance": 0.04, "fraction": 0.5}'
Example (curl — discrete)
curl -X POST https://api.axistruth.com/v1/size-position \
-H "Authorization: Bearer axt_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{"mode": "discrete", "winProbability": 0.6, "winLossRatio": 1.0, "fraction": 1.0}'
POST /v1/factor-score
POST /v1/factor-score
Scores assets on up to 4 factors: momentum, low-volatility, quality, value. Returns raw scores and cross-sectional ranks.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
assets | Asset[] | Yes | Array of assets. Each asset must have ticker and priceHistory. See Asset schema below. |
factors | string[] | No | Subset of ["momentum", "lowVol", "quality", "value"]. Default: all four. |
lookback | integer | No | Lookback window in trading days for momentum. Default: 252. |
Asset object
| Field | Type | Required | Description |
|---|---|---|---|
ticker | string | Yes | Identifier for the asset (used as key in response). |
priceHistory | number[] | Yes | Daily close prices, oldest first. Min length: lookback + 1. |
roe | number | No | Return on equity (for quality factor). |
debtToEquity | number | No | Debt-to-equity ratio (for quality factor). |
bookValuePerShare | number | No | Book value per share (for value factor). |
price | number | No | Current price (for value factor — used with bookValuePerShare to compute P/B). |
Response (200)
{
"scores": {
"SPY": { "momentum": 0.82, "lowVol": 0.41, "quality": 0.67, "value": 0.55 },
"GLD": { "momentum": 0.63, "lowVol": 0.78, "quality": null, "value": null }
},
"ranks": {
"SPY": { "momentum": 1, "lowVol": 2, "quality": 1, "value": 1 },
"GLD": { "momentum": 2, "lowVol": 1, "quality": null, "value": null }
}
}
null values indicate the factor could not be computed for that asset (insufficient data or missing fields).
Example (curl)
curl -X POST https://api.axistruth.com/v1/factor-score \
-H "Authorization: Bearer axt_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"assets": [
{
"ticker": "AAPL",
"priceHistory": [<253 daily prices, oldest first>],
"roe": 0.15,
"debtToEquity": 0.3,
"bookValuePerShare": 4.0,
"price": 180.0
}
],
"factors": ["momentum", "lowVol"]
}'
POST /v1/stress-test
POST /v1/stress-test
Monte Carlo stress test across up to 3 historical regime scenarios (GFC 2008, COVID 2020, Dotcom 2000). Returns max drawdown, VaR 95, and CVaR 95 per scenario.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
weights | Record<string, number> | Yes | Asset weights. Must sum to 1.0 (±1e-6 tolerance). |
expectedReturns | Record<string, number> | Yes | Daily expected returns per asset. |
dailyVols | Record<string, number> | Yes | Daily volatilities per asset (annualize by multiplying by √252). |
correlationMatrix | number[][] | Yes | N×N correlation matrix. Must be positive semi-definite. |
assetOrder | string[] | Yes | Asset names defining row/column order of correlationMatrix. |
scenarios | string[] | No | Subset of ["GFC_2008", "COVID_2020", "DOTCOM_2000"]. Default: all three. |
simulations | integer | No | Number of Monte Carlo paths. Default: 1000. Min: 50. |
Response (200)
{
"scenarios": [
{
"name": "GFC_2008",
"durationDays": 127,
"maxDrawdown": -0.312,
"var95": -0.187,
"cvar95": -0.243
},
{
"name": "COVID_2020",
"durationDays": 23,
"maxDrawdown": -0.198,
"var95": -0.124,
"cvar95": -0.156
}
]
}
All return values are in decimal form (e.g., -0.312 = -31.2% drawdown). CVaR is the expected loss in the worst-5% of simulations; it is always ≤ VaR 95.
Example (curl)
curl -X POST https://api.axistruth.com/v1/stress-test \
-H "Authorization: Bearer axt_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"weights": { "SPY": 0.6, "AGG": 0.4 },
"expectedReturns": { "SPY": 0.0003, "AGG": 0.0002 },
"dailyVols": { "SPY": 0.01, "AGG": 0.008 },
"correlationMatrix": [[1.0, 0.4], [0.4, 1.0]],
"assetOrder": ["SPY", "AGG"],
"scenarios": ["GFC_2008", "COVID_2020"],
"simulations": 500
}'
GET /v1/usage
GET /v1/usage
Returns current-month call count vs tier limit. Use this to build quota tracking into your application.
Response (200)
{
"used": 47,
"limit": 100,
"tier": "free",
"resetAt": 1748822400
}
Example (curl)
curl https://api.axistruth.com/v1/usage \
-H "Authorization: Bearer axt_live_YOUR_KEY_HERE"
Billing endpoints
The three billing endpoints below are for server-to-server use. They do not require an API Bearer token but have their own validation requirements.
POST /v1/billing/checkout
POST /v1/billing/checkout
Creates a Stripe Checkout session for upgrading a tenant to a paid tier.
| Field | Type | Required | Description |
|---|---|---|---|
tenantId | string | Yes | Your internal tenant identifier. |
tier | "pro" | "quant" | "enterprise" | Yes | Target tier for the checkout session. |
{
"url": "https://checkout.stripe.com/pay/cs_test_..."
}
POST /v1/billing/webhook
POST /v1/billing/webhook
Stripe webhook receiver. This endpoint is for Stripe to call, not your application. Requires the stripe-signature header with a valid HMAC. Configure your Stripe webhook to point here.
Handled events:
customer.subscription.created— upgrades tenant to paid tiercustomer.subscription.deleted— downgrades tenant to free
GET /health
GET /health
Uptime probe. No authentication required. Returns 200 when the server is operational.
{ "status": "ok", "version": "0.2.0" }
Returns 503 if a critical dependency is unavailable.