API Reference

Pre-trade risk permissions for BTC/USD and ETH/USD. Spot, perpetual futures (perps), and DeFi borrowing aware.

USD-denominated: All scoring is based on BTC/USD and ETH/USD price action, derivatives, and macro conditions. If you trade non-USD pairs (e.g. BTC/EUR, ETH/BTC), additional cross-rate risk is not covered by this API.

Endpoint

POST https://riskstate.netlify.app/v1/risk-state

Authentication

All requests require a Bearer token in the Authorization header.

Authorization: Bearer <your_api_key>

Key types

TypeFormatRate limitAccess
OwnerRISKSTATE_API_KEY env varUnlimitedAll endpoints
Externalrs_live_ + 64 hex chars60 req/min/v1/risk-state + read-only endpoints

Getting an API key

Request API access from the home page — only an email is required. You'll receive an rs_live_ key via email within minutes. Free during beta.

The endpoint fails closed: if the server secret is not configured, all requests are denied (401). Rate-limited requests return 429 with retry_after_seconds: 60.

Request

Headers

HeaderRequiredValue
AuthorizationYesBearer <token>
Content-TypeYesapplication/json

Body (JSON)

FieldTypeDefaultDescription
assetstring"BTC"Asset to evaluate. "BTC" or "ETH".
walletstringnullEthereum wallet address (0x...) for DeFi position data. Optional.
protocolstring"spark"DeFi lending protocol. "spark" or "aave". Only used when wallet is provided.
include_detailsbooleanfalseInclude expanded scoring details in response.
reference_timenumbernowUnix seconds. Pins daysSinceHalving and the policy hash to a single timestamp, enabling bit-exact reproducibility. Must be in [halving, now+1d].
allow_degradedbooleanfalseIf false (default), the endpoint returns 503 Core data unavailable when any of price, rsi, funding are missing upstream. Set to true to receive a degraded policy (with data_integrity capped).

Example requests

Minimal (BTC):

curl -X POST https://riskstate.netlify.app/v1/risk-state \
  -H "Authorization: Bearer $RISKSTATE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"asset": "BTC"}'

Detailed (with scoring breakdown):

curl -X POST https://riskstate.netlify.app/v1/risk-state \
  -H "Authorization: Bearer $RISKSTATE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"asset": "BTC", "include_details": true}'

DeFi monitoring (with wallet + Aave):

curl -X POST https://riskstate.netlify.app/v1/risk-state \
  -H "Authorization: Bearer $RISKSTATE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"asset": "ETH", "wallet": "0xYOUR_WALLET_ADDRESS", "protocol": "aave", "include_details": true}'

Note: All parameters go inside the -d JSON string. The \ at the end of each line is a shell line continuation — the entire command is one curl call.

Validation

  • asset must be "BTC" or "ETH" (case-insensitive) → 400 otherwise
  • wallet must match ^0x[a-fA-F0-9]{40}$ if provided → 400 otherwise
  • protocol must be "spark" or "aave" (case-insensitive) → 400 otherwise
  • reference_time must be a finite number in [new Date('2024-04-20').getTime()/1000, now+86400] if provided → 400 otherwise
  • Invalid JSON body → 400
  • Core data missing (and allow_degraded not set) → 503 with Retry-After: 30 and body { error, missing, sources, retry_after_seconds, hint }

Determinism contract (v1.2.0+)

The policy hash now includes api_version, scoring_version, ts (from reference_time), prices, indicators, positioning (funding percentile, OI z-score, squeeze direction), macro (regime, coupling, DXY), volatility (regime + score), cycle (phase, MVRV percentile, boost flag), composite, and policy binding. Given identical inputs and the same reference_time, the hash is bit-for-bit identical across requests.

Response — Minimal (default)

Three blocks: Permissioning, Classification, Auditability.

Permissioning

FieldTypeDescription
exposure_policy.max_size_fractionfloat (0–1)Maximum position size as fraction of portfolio
exposure_policy.leverage_allowedbooleanWhether leverage is permitted
exposure_policy.max_leveragestringMaximum leverage for DeFi borrowing ("0x", "1x", "1.5x", "2x"). For perps, use max_size_fraction as notional cap instead.
exposure_policy.direction_biasstring"LONG_PREFERRED", "SHORT_PREFERRED", or "NEUTRAL"
exposure_policy.reduce_recommendedbooleanAgent should reduce exposure
exposure_policy.allowed_actionsstring[]Actions the agent MAY take (enum tokens, see reference below)
exposure_policy.blocked_actionsstring[]Actions the agent MUST NOT take (enum tokens, see reference below)

Classification

FieldTypeRangeDescription
tactical_statestringBULLISH, LEAN BULL, NEUTRAL, LEAN BEAR, BEARISH24-72h directional tilt from composite
structural_statestringCycle phaseBTC: BOTTOM/EARLY/MID/LATE/EUPHORIA/CORRECTION/POST-PEAK. ETH: DEPRESSED/VALUE_ZONE/RECOVERY/EXTENDED/DISTRIBUTION
macro_statestringRISK-ON, NEUTRAL, RISK-OFFMacro regime from FRED data
market_regimestringPANIC, EUPHORIA, SQUEEZE, TREND, RANGE5-state unified market regime
volatility_regimestringLOW, NORMAL, HIGH, EXTREMEVolatility classification
policy_levelint1–5Informational classification. The exposure_policy fields are the binding constraints. 1=BLOCK Survival, 2=BLOCK Defensive, 3=CAUTIOUS, 4=GREEN Selective, 5=GREEN Expansion
confidence_scorefloat (0–1)Signal agreement × data qualityMeasures subscore agreement and data integrity. NOT a probability of market prediction accuracy. Higher = signals agree more and data is fresher.
data_quality_scoreint (0–100)% of data sources livePercentage of data sources reporting live data. Different scale from confidence_score (0–1 factor). <70 = degraded, <50 = unreliable

Constraints & Flags

FieldTypeDescription
binding_constraint.sourcestringWhich cap is limiting: "RULES", "DEFI", "MACRO", "CYCLE"
binding_constraint.reasonstringHuman-readable explanation (e.g. "RISK-OFF × NORMAL")
binding_constraint.reason_codesstring[]Machine-parseable reason tokens (e.g. ["MACRO_RISK_OFF", "COUPLING_NORMAL"])
binding_constraint.cap_valuefloatThe binding cap's value (0–1)
risk_flags.structural_blockersstring[]Hard blockers — agent MUST pause new entries
risk_flags.context_risksstring[]Soft risks — agent should reduce conviction
defiobject|nullDeFi position data if wallet provided, else null. See fields below.
defi.health_factorfloatCurrent health factor (>1 = safe, <1.1 = danger)
defi.ltvfloatCurrent loan-to-value ratio % (debt / collateral × 100). E.g. 35.4 means 35.4% utilized.
defi.max_ltvfloatProtocol's maximum LTV threshold % (e.g. 82.99). Borrowing above this is blocked.
defi.liquidation_thresholdfloatLiquidation threshold % (e.g. 82.5). Position liquidatable above this.
defi.protocolstring"spark" or "aave"

Auditability

FieldTypeDescription
policy_hashstringSHA-256 hash of policy inputs for non-repudiation
scoring_versionstring"score_v2" — scoring algorithm version
versionstringAPI version
timestampstringISO 8601 timestamp
assetstringAsset evaluated
cachedbooleanWhether response was served from cache
ttl_secondsintCache TTL in seconds (60). Agent should re-request after this interval for fresh data.
key_typestring"owner" or "external" — identifies which auth tier was used
stale_fieldsstring[]Core signals that are missing or stale

Response — Detailed (include_details=true)

All minimal fields plus:

FieldTypeDescription
caps.rulesfloatRules cap (0–1)
caps.defifloatDeFi health cap (0–1)
caps.macrofloatMacro regime cap (0–1)
caps.cyclefloatCycle phase cap (0–1)
caps.qualityfloatQuality factor (conflict × integrity)
caps.data_integrityfloatData freshness score
positioning.squeeze_directionstringUPSIDE, DOWNSIDE, TWO-SIDED, NONE
positioning.squeeze_confidenceint0–100
positioning.ls_crowdingstringLONG_CROWDED, SHORT_CROWDED, BALANCED, etc.
positioning.ls_crowding_scoreint0–100
positioning.funding_percentileintFunding rate percentile vs 30d (0–100)
positioning.oi_zscorefloatOI z-score vs 30d
positioning.basis_pctfloatPerp-spot basis %
volatility.regimestringLOW, NORMAL, HIGH, EXTREME
volatility.scoreint0–100
whale_pressure.scoreint0–100 (9 proxy signals)
whale_pressure.directionstringSTRONG_BUY, BUY, NEUTRAL, SELL, STRONG_SELL
trend_strength.scoreint0–100
trend_strength.directionstringSTRONG_TREND, TREND, NEUTRAL, COUNTER_TREND, STRONG_COUNTER
trend_strength.componentsobject{ ma_cluster, expansion, flow_alignment } (each 0–100)
composite.overallint0–100 weighted composite score
composite.subscoresarray[{ name, score, weight }] — 7-8 subscores
extreme_scores.panicint0–100 panic percentile
extreme_scores.euphoriaint0–100 euphoria percentile
eth_structuralobject|nullETH structural score (ETH only): { overall, label, dataQuality, subfamilies: { network, supply, relative, demand } }
eth_downgradeobject|nullETH structural downgrade (ETH only): { active, count, hardCount, softCount, severity, signals, capReduction }
macro_detailobjectFull macro data for diagnostics: { regime, coupling, realRate10y, liquidityRegime, yield10y, yield10yChg, spxChangePct, fedBsDelta3m, spread10y2y, riskOffSignals, riskOnSignals, dxy }
data_sourcesobjectPer-field source status (LIVE/MOCK/CG_FALLBACK/CC_FALLBACK/DEFAULT)
core_missingstring[]Missing core signals

Error Responses

StatusBodyCause
400{ "error": "Invalid JSON body" }Malformed JSON
400{ "error": "Invalid asset. Must be BTC or ETH." }Unknown asset
400{ "error": "Invalid wallet address format." }Bad wallet format
401{ "error": "Unauthorized" }Missing or invalid Bearer token
429{ "error": "Rate limit exceeded", "retry_after_seconds": 60 }External key exceeded 60 req/min
500{ "error": "Internal server error" }Server-side failure

Data Sources & Fallback Chain

The endpoint fetches from 15+ external APIs in parallel waves. Binance returns HTTP 451 from Netlify servers, so all Binance data has fallbacks:

DataPrimaryFallbackLast Resort
RSI (4h)Binance klinesCryptoCompare histohourDefault 50
Funding rateBinance fundingRateOKX V5 → Bybit V5 → CoinGlassDefault 0
Daily klines (200d)Binance klinesCryptoCompare histodaynull (trend strength unavailable)
MVRVblockchain.infoCoinGlass /indicator/market/mvrvDefault 1.8
Real RateFRED T10YIE (breakeven)null
Liquidity RegimeFRED WALCL (13-week delta)NEUTRAL
GoldYahoo Financenull
Macro regime + coupling/api/macro (single source of truth)NEUTRAL / NORMAL
ETH StructuralLido + Ultrasound + DefiLlama (6 APIs)Score defaults to 50
SPXYahoo Finance (^GSPC) via macro.jsFRED SP500 (T-1 lag)null
Staking APRLido /apr/lastLido /apr/smaDefault 2.8%

The data_sources field (in detailed response) shows per-field source: LIVE, CC_FALLBACK (CryptoCompare), CG_FALLBACK (CoinGlass), ESTIMATED (price-based), DEFAULT_ZERO, DEFAULT, or MOCK.

Known Data Limitations

Binance returns HTTP 451 from Netlify servers. Current CoinGlass tier lacks certain endpoints. These cause permanent fallback states for some fields:

FieldServer StatusImpact
FundingOKX or BYBIT fallbackLive data via cascading fallback chain. Neutral default (0) only if all fallbacks fail
Open InterestOKX or BYBIT fallbackLive data via cascading fallback chain. Affects squeeze detection and OI z-score
MVRVESTIMATED (~price/$36K)Accurate to ±5%. Affects cycle phase near thresholds
DXYLIVE (Frankfurter EUR/USD proxy)Same formula as dashboard. Affects detectRegime + macro scoring

Caching Behavior

  • 60-second TTL via Netlify Blobs
  • First call: 5–12 seconds (fetches from 15+ external APIs in 5 parallel waves)
  • Subsequent calls within 60s: <1 second
  • cached: true in response indicates cache hit
  • Wallet parameter bypasses cache (DeFi data is personalized)

Action Enums Reference

allowed_actions values

TokenMeaningPolicy Levels
REDUCEReduce existing exposure1
ADD_COLLATERALAdd collateral to DeFi position1
HEDGEHedge existing positions1, 2
WAITWait for better conditions2, 3
REDUCE_LEVERAGEReduce leverage on existing positions2
SCALP_SMALLSmall scalp trades only2
RR_GT_2Trades with R:R > 2:1 only2, 3
DCADollar-cost averaging3, 4
LIGHT_ACCUMULATIONLight spot accumulation3
LONG_SHORT_CONFIRMEDLong or short with confirmation signals4
LEVERAGE_MODERATEModerate leverage (up to 1.5x)4
TREND_FOLLOWTrend following strategies5
ADD_ON_PULLBACKAdd to winners on pullbacks5
LEVERAGE_2XLeverage up to 2x5
AGGRESSIVE_ACCUMULATIONAggressive spot accumulation5

blocked_actions values

TokenMeaningPolicy Levels
NEW_TRADESAll new position entries blocked1
LEVERAGEAny leverage blocked1, 3
INCREASE_POSITIONIncreasing existing positions blocked1
LEVERAGE_GT_1XLeverage above 1x blocked2
AGGRESSIVE_LONGAggressive long entries blocked2, 3
FOMO_ENTRYFOMO-driven entries blocked2
ALL_INFull portfolio allocation blocked3, 4
LEVERAGE_GT_2XLeverage above 2x blocked4
COUNTER_TREND_SHORTShorting against confirmed trend blocked5

binding_constraint.reason_codes values

SourcePossible codesExample
RULESRULES_CRITICAL_{n}, RULES_WARNING_{n}["RULES_CRITICAL_2", "RULES_WARNING_5"]
DEFIDEFI_HF_LOW["DEFI_HF_LOW"]
MACROMACRO_{regime}, COUPLING_{level}["MACRO_RISK_OFF", "COUPLING_HIGH_COUPLING"]
CYCLECYCLE_{phase}["CYCLE_MID"], ["CYCLE_EUPHORIA"]

Risk Flags Reference

Structural Blockers (agent MUST pause)

FlagMeaning
DEFI_LIQUIDATION_RISKHealth Factor critically low
SQUEEZE_RISKHigh-confidence directional squeeze
MACRO_CONTAGIONSPX/QQQ selloff >1.5% with normal or high coupling
MACRO_RISK_OFFMacro regime risk-off with multiple signals
ONCHAIN_EUPHORIAMVRV + NUPL at historical extremes
EXTREME_FEARF&G ≤ 10 (panic conditions)
FUNDING_EXTREMEFunding + RSI both extreme
LIQUIDATION_CASCADE>$100M liquidations with asymmetry

Context Risks (agent should reduce conviction)

FlagMeaning
HIGH_FUNDINGFunding rate elevated
RSI_OVERBOUGHTRSI > 70
RSI_OVERSOLDRSI < 25
DXY_HEADWINDDXY > 104
YIELD_PRESSURE10Y yield elevated or spiking
HIGH_OIOI z-score > 2.0 (30d)
BASIS_EXTREMEPerp-spot basis > 0.15%
WHALE_ACTIVITYWhale score ≥ 50
ETH_STRUCTURAL_WEAKETH structural downgrade active
ETH_SUPPLY_INFLATIONARYETH supply strongly inflationary
TREND_NOT_CONFIRMEDTrend strength ≤ 45 with directional tilt
NO_TRENDTrend strength ≤ 20
HIGH_COUPLINGHigh macro correlation
SIGNAL_CONFLICTSubscore dispersion high
LS_CROWDEDL/S ratio extreme
YIELD_SPIKE10Y yield change > 0.08% in session
CURVE_INVERTED10Y-2Y spread < -0.2%
REAL_RATE_HEADWINDReal rate > 1.5%
LIQUIDITY_TIGHTENINGFed balance sheet shrinking (3m delta < -1%)
STAKING_HEADWINDReal rate > ETH staking APR (ETH only)
LIQUIDATION_ASYMMETRY>75% of liquidations on one side

How to Use by Context

The API returns the same response regardless of how you trade. The market conditions assessment (composite score, regime, policy level) is identical. What changes is how you interpret the risk permissions for your specific context.

Spot Trading (BTC/USD or ETH/USD)

You are buying or selling the asset on a spot exchange (Coinbase, Binance Spot, Kraken, Uniswap, CoW Swap).

Key fields:

  • max_size_fraction% of portfolio to deploy. If 0.33, allocate up to 33% of your portfolio to this position.
  • direction_biasLONG_PREFERRED means "buy signal." SHORT_PREFERRED means "don't buy / consider selling." NEUTRAL means "no directional edge."
  • structural_blockers → If non-empty, do not buy.
  • allowed_actions / blocked_actions → Filter for relevant actions (DCA, WAIT, LIGHT_ACCUMULATION).

Ignore for spot: max_leverage, leverage_allowed, and leverage-related blocked actions (LEVERAGE_GT_1X, LEVERAGE_GT_2X). These apply to leveraged markets and DeFi borrowing.

Pre-trade workflow:

  1. Call the API with {"asset": "BTC"}
  2. Check structural_blockers — if non-empty, do not enter
  3. Read max_size_fraction — this is your max allocation
  4. Check direction_bias — respect the directional guidance
  5. Proceed to your exchange and place the spot order

Perpetual Futures (Perps)

You are trading BTC/USDT or ETH/USDT perpetuals on Binance Futures, Hyperliquid, dYdX, Bybit, or similar venues.

Key fields:

  • max_size_fractionMax notional exposure as % of portfolio. If 0.33 and your portfolio is $100K, your max notional is $33K. At 10x leverage, that means max $3.3K margin.
  • direction_bias → Directly actionable: LONG_PREFERRED favors longs, SHORT_PREFERRED favors shorts.
  • structural_blockers → If SQUEEZE_RISK is present, do not open leveraged positions — liquidation risk is elevated.

Especially relevant for perps (in detailed response):

  • positioning.funding_percentile → How extreme the current funding rate is vs. 30 days. P>85 = paying heavy carry on longs. P<15 = shorts are crowded.
  • positioning.basis_pct → Perp-spot premium. Positive = longs dominant, negative = discount.
  • positioning.squeeze_direction → UPSIDE (short squeeze probable) or DOWNSIDE (long squeeze probable).
  • positioning.oi_zscore → OI z-score vs. 30 days. >2.0 = excessive leverage in the market.

Margin guide (from max_size_fraction):

Your leverageMax margin (% of portfolio)Example ($100K portfolio, max_size=0.33)
3xmax_size / 3 = 11.0%$11,000 margin
5xmax_size / 5 = 6.6%$6,600 margin
10xmax_size / 10 = 3.3%$3,300 margin
25xmax_size / 25 = 1.3%$1,300 margin

Note: RiskState does not impose a leverage cap for perpetual futures. The max_leverage field reflects DeFi borrowing constraints (see below). For perps, the binding constraint is max_size_fraction as max notional exposure — you choose your own leverage within that cap.

Pre-trade workflow (e.g. Hyperliquid):

  1. Call the API with {"asset": "BTC", "include_details": true}
  2. Check structural_blockers — if SQUEEZE_RISK present, do not open leveraged positions
  3. Read max_size_fraction — this is your max notional as % of portfolio
  4. Divide by your intended leverage to get max margin
  5. Check positioning.funding_percentile — if P>85, longs are paying heavy carry
  6. Check positioning.squeeze_direction — if DOWNSIDE, longs are at risk
  7. Respect direction_bias for trade direction
  8. Place your order on the venue

DeFi Borrowing (Aave, Spark, Morpho)

You borrow stablecoins against BTC or ETH collateral on-chain.

Key fields:

  • max_size_fraction% of collateral to deploy. Accounts for your current health factor.
  • max_leverage → Borrowing ratio cap: "0x" (don't borrow), "1x", "1.5x", "2x".
  • defi.health_factor → Current health factor (>1 safe, <1.1 danger).
  • defi.ltv → Current loan-to-value ratio.
  • binding_constraint.source → If "DEFI", your on-chain position is the limiting factor.

Pre-trade workflow:

  1. Call the API with {"asset": "ETH", "wallet": "0xYOUR_WALLET", "protocol": "aave"}
  2. Check defi.health_factor — if <1.5, prioritize adding collateral
  3. If binding_constraint.source === "DEFI", do not increase debt
  4. Respect max_leverage for borrowing ratio
  5. If structural_blockers contains DEFI_LIQUIDATION_RISK, reduce position immediately

AI Trading Agent Integration

You are building an autonomous trading agent (Hermes, ElizaOS, OpenClaw, AgentKit, custom) that calls the API programmatically.

Key principle: Call the API between decision and execution. Your agent decides what to trade (intelligence layer). RiskState tells it how much it can risk (governance layer). Then the agent executes within those bounds.

Binding precedence (evaluation order):

  1. risk_flags.structural_blockers — if non-empty, abort new entries
  2. exposure_policy.blocked_actions contains NEW_TRADES — abort new entries
  3. exposure_policy.reduce_recommended — reduce exposure (not necessarily close all)
  4. Size position: max_size_fraction × agent_conviction
  5. Respect max_leverage and direction_bias
  6. Filter trades through allowed_actions
  7. If stale_fields non-empty or data_quality_score < 70 → halve size or abstain
  8. policy_level is summary only — do not use for enforcement

Interpretation Guide

Position Sizing

Spot:

position_size = portfolio_value × max_size_fraction × conviction

Perps:

max_notional = portfolio_value × max_size_fraction × conviction
margin = max_notional / leverage

Where conviction is your own confidence factor (0–1), whether human or algorithmic.

Re-consultation Frequency

Use caseRecommended interval
Active trading (scalps, day trades)Every 5 minutes
Swing trading (24h–72h holds)Every 15 minutes
Holding / DCAEvery 4 hours
After significant move (>3% 1h)Immediate

When to Force-Check

  • Before any new position entry
  • When policy_level was previously ≤ 2 (check if conditions improved)
  • After external events (ETF announcements, Fed meetings, major hacks)