API Documentation

REST API for the FX Realized Volatility Forecast — 28 pairs, 11 horizons (15m-24h). Base URL: https://api.fxengineer.com

The API is currently v1 (beta). Breaking changes will be announced at least 14 days in advance via email to registered API users.

Authentication

All signal endpoints require authentication via API key or JWT Bearer token. Track record and model info endpoints are publicly accessible.

API Key (recommended for programmatic access)
curl -H "X-API-Key: fxe_your_key_here" \
  https://api.fxengineer.com/rv/signals/latest
JWT Bearer Token (for dashboard/browser sessions)
curl -H "Authorization: Bearer eyJhbG..." \
  https://api.fxengineer.com/rv/signals/latest

API keys are issued by your account administrator. Do not share keys or embed them in client-side code.

Access Tiers

TierPairsHorizonsRate LimitExport
Free--------
Trial6 majors1h, 4h10/minNo
ProAll 28All 11 (15m-24h)100/minYes

Free tier: track record and model info only (no live signals). Trial: EURUSD, USDJPY, GBPUSD, AUDUSD, USDCAD, USDCHF at 1h and 4h horizons. 14 days.

Endpoints

GET/rv/signals/latestTrial: 6 pairs, 1h+4h | Pro: all 28, all 11 (15m-24h)

Current predictions for all pairs and horizons. Returns the most recent signal for each pair/horizon combination, grouped by pair with full move distribution.

Auth: API key or JWT (Trial+)

GET/rv/signals/{pair}/{horizon}Example: /rv/signals/EURUSD/4

Signal history for a specific pair and horizon. Returns up to 1,000 historical predictions. Horizon parameter is an integer representing hours (e.g., 1, 2, 3, 4, 6, 8, 12, 24). Sub-hourly horizons use decimals: 0.25 (15m), 0.5 (30m), 0.75 (45m).

Auth: API key or JWT (Trial+)

GET/rv/track-record

Rolling AUC, Brier score, calibration stats, per-pair and per-horizon breakdowns, model metadata. This is the trust mechanism — available to all users.

Auth: Public (no auth required)

GET/rv/track-record/exportQuery param: ?format=json or ?format=csv

Full scored outcomes as JSON or CSV. Every prediction, every outcome, every timestamp. For independent verification — pull this data, check it against any public candle source.

Auth: API key or JWT (Trial+)

GET/rv/model

Current model version, training dates, test AUC per horizon, feature count, training row count. Available to all users.

Auth: Public (no auth required)

Response Format

All responses are JSON. Timestamps are ISO 8601 UTC. Numeric values are floats rounded to 6 decimal places.

GET /rv/signals/latest — response (truncated)
{
  "generated_at": "2026-03-18T20:00:00+00:00",
  "tier": "pro",
  "pairs": 28,
  "signals": [
    {
      "pair": "EURUSD",
      "horizons": {
        "1": {
          "signal_ts": "2026-03-18T20:00:00",
          "score": 0.142,
          "calibrated_prob": 0.138,
          "expected_move_pct": 0.00185,
          "median_move_pct": 0.00142,
          "p75_move_pct": 0.00258,
          "p90_move_pct": 0.00401,
          "threshold": 0.0012,
          "confidence_tier": "normal",
          "model_version": "lgbm_v2_20260318"
        },
        "4": { ... },
        "8": { ... },
        "24": { ... }
      }
    },
    ...
  ]
}
GET /rv/track-record — response (truncated)
{
  "generated_at": "2026-03-18T20:00:00+00:00",
  "total_signals": 5040,
  "total_scored": 3200,
  "pending": 1840,
  "overall": {
    "n_scored": 3200,
    "auc_alltime": 0.721,
    "brier_score": 0.0215,
    "base_rate": 0.175
  },
  "by_horizon": [
    { "horizon_hours": 0.25, "n_scored": 400, "auc": 0.693 },
    { "horizon_hours": 1, "n_scored": 400, "auc": 0.714 },
    // ... 9 more horizons (0.5, 0.75, 2, 3, 4, 6, 8, 12, 24)
  ],
  "by_pair": [
    { "pair": "EURUSD", "n_scored": 120, "auc": 0.708 },
    ...
  ],
  "model": {
    "version": "lgbm_v2_20260318",
    "trained_date": "2026-03-18",
    "live_since": "2026-03-18T17:00:00"
  }
}

Signal Fields

FieldTypeDescription
scorefloat 0-1Raw model probability of a significant move
calibrated_probfloat 0-1Isotonic-calibrated true probability. When it says 0.30, ~30% of such predictions historically produced large moves.
expected_move_pctfloatMean expected absolute return (percentage). E.g., 0.0025 = 0.25%.
median_move_pctfloat50th percentile of the expected move distribution.
p75_move_pctfloat75th percentile — moderate tail scenario.
p90_move_pctfloat90th percentile — upper tail / stress scenario.
thresholdfloatThe move threshold for this horizon (e.g., 0.0025 for 4h). Moves exceeding this are "significant."
confidence_tierstringScore classification: quiet (< 0.15), normal (0.15-0.30), elevated (0.30-0.50), high (> 0.50).

Quick Start (Python)

python
import requests

API_KEY = "fxe_your_key_here"
BASE = "https://api.fxengineer.com"

# Get latest signals for all pairs
resp = requests.get(
    f"{BASE}/rv/signals/latest",
    headers={"X-API-Key": API_KEY}
)
data = resp.json()

# Note: sub-hourly horizons are keyed as decimals in the response
# "0.25" = 15m, "0.5" = 30m, "0.75" = 45m, "1" = 1h, etc.
for pair_data in data["signals"]:
    pair = pair_data["pair"]
    for horizon, signal in pair_data["horizons"].items():
        score = signal["score"]
        move = signal["expected_move_pct"]
        h_label = {"0.25": "15m", "0.5": "30m", "0.75": "45m"}.get(horizon, f"{horizon}h")
        if score > 0.30:
            print(f"{pair} {h_label}: ELEVATED "
                  f"(score={score:.1%}, "
                  f"expected move={move:.4%})")
python — check track record
# Track record (no auth needed)
resp = requests.get(f"{BASE}/rv/track-record")
tr = resp.json()

print(f"Total scored: {tr['total_scored']}")
print(f"Overall AUC:  {tr['overall']['auc_alltime']}")

for h in tr["by_horizon"]:
    print(f"  {h['horizon_hours']}h: "
          f"AUC={h['auc']}, n={h['n_scored']}")

Error Codes

CodeMeaningAction
200SuccessParse response body
400Bad request — invalid pair, horizon, or parameterCheck request parameters
401Invalid or missing API key / JWTVerify your API key is correct and active
403Insufficient tier — endpoint requires higher accessUpgrade subscription or check tier limits
404Resource not found — no data for this pair/horizonVerify pair name and horizon value
429Rate limit exceededBack off and retry. Check X-RateLimit headers.
500Internal server errorRetry after a few seconds. Contact support if persistent.

Rate Limits

Rate limits are per API key. Limits are returned in response headers:

Response headers
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 97
X-RateLimit-Reset: 1711029600

If you exceed the limit, the API returns 429 Too Many Requests. Wait until the reset timestamp before retrying. Trial: 10 requests/minute. Pro: 100 requests/minute.

Polling guidance: Signals update hourly at :05 past. Sub-hourly signals update every 15 minutes. We recommend polling /rv/signals/latest no more than once per cycle.

Support

API questions: info@fxengineer.com