FlashAlpha Lab API Documentation: Options Exposure Analytics REST API
Lab API Overview & Auth

FlashAlpha Lab API: Real-Time Options Exposure Analytics

Real-time options exposure analytics. Live gamma (GEX), delta (DEX), vanna (VEX), and charm (CHEX) exposure data, key levels, dealer hedging estimates, and verbal narrative analysis.

Options Exposure REST API Overview

The FlashAlpha Lab API provides programmatic access to real-time options exposure analytics derived from live options flow. Build trading tools, dashboards, and research platforms on top of production-grade exposure data.

Base URL: https://lab.flashalpha.com

What you can build:

  • Real-time gamma exposure dashboards with support/resistance levels
  • Automated trading signals based on dealer positioning shifts
  • Options flow analytics platforms with narrative interpretation
  • Historical exposure trend analysis and regime detection

Quick Start: Make Your First Options API Call

Make your first API call in under a minute. Create a free account to get your API key (no credit card required), then run:

# Fetch SPY exposure summary
curl -H "X-Api-Key: YOUR_API_KEY" \
  https://lab.flashalpha.com/v1/exposure/summary/SPY
import requests

API_KEY = "YOUR_API_KEY"
BASE    = "https://lab.flashalpha.com"

# 1. Check gamma regime
summary = requests.get(
    f"{BASE}/v1/exposure/summary/SPY",
    headers={"X-Api-Key": API_KEY}
).json()

print(f"Regime:     {summary['regime']}")
print(f"Net GEX:    ${summary['exposures']['net_gex']:,.0f}")
print(f"Gamma Flip: {summary['gamma_flip']}")

# 2. Get key support/resistance levels
levels = requests.get(
    f"{BASE}/v1/exposure/levels/SPY",
    headers={"X-Api-Key": API_KEY}
).json()["levels"]

print(f"Call Wall:  {levels['call_wall']}")
print(f"Put Wall:   {levels['put_wall']}")
print(f"0DTE Magnet: {levels['zero_dte_magnet']}")
const API_KEY = "YOUR_API_KEY";
const BASE    = "https://lab.flashalpha.com";

const headers = { "X-Api-Key": API_KEY };

// 1. Check gamma regime
const summary = await fetch(`${BASE}/v1/exposure/summary/SPY`, { headers })
  .then(r => r.json());

console.log(`Regime:     ${summary.regime}`);
console.log(`Net GEX:    $${summary.exposures.net_gex.toLocaleString()}`);
console.log(`Gamma Flip: ${summary.gamma_flip}`);

// 2. Get key support/resistance levels
const { levels } = await fetch(`${BASE}/v1/exposure/levels/SPY`, { headers })
  .then(r => r.json());

console.log(`Call Wall:  ${levels.call_wall}`);
console.log(`Put Wall:   ${levels.put_wall}`);

That's it. If you see JSON output with regime, net_gex, and gamma_flip fields, your API key is working. Explore the Exposure Analytics endpoints next.

API Key Authentication via X-Api-Key Header

Step 1: Create a free account. Sign up here — no credit card required. Your API key can be found on your profile page and will appear automatically in the examples below once you're logged in.

All endpoints (except /health) require an API key. Pass it in the X-Api-Key header (recommended):

curl -H "X-Api-Key: YOUR_API_KEY" https://lab.flashalpha.com/v1/symbols

Or as a query parameter (useful for quick browser testing):

https://lab.flashalpha.com/v1/symbols?apiKey=YOUR_API_KEY

Security tip: Always prefer the X-Api-Key header in production. Query parameters may be logged in server access logs, browser history, and proxy caches.

Here's a reusable setup for your projects:

import requests, os

API_KEY = os.environ["FLASHALPHA_API_KEY"]
BASE    = "https://lab.flashalpha.com"
HEADERS = {"X-Api-Key": API_KEY}

def fa_get(path, params=None):
    """Reusable helper for FlashAlpha API calls."""
    resp = requests.get(f"{BASE}{path}", headers=HEADERS, params=params)
    resp.raise_for_status()
    return resp.json()

# Usage
summary = fa_get("/v1/exposure/summary/SPY")
quote   = fa_get("/stockquote/SPY")
const API_KEY = process.env.FLASHALPHA_API_KEY;
const BASE    = "https://lab.flashalpha.com";
const HEADERS = { "X-Api-Key": API_KEY };

async function faGet(path, params = {}) {
  const url = new URL(`${BASE}${path}`);
  Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));
  const resp = await fetch(url, { headers: HEADERS });
  if (!resp.ok) throw new Error(`${resp.status}: ${await resp.text()}`);
  return resp.json();
}

// Usage
const summary = await faGet("/v1/exposure/summary/SPY");
const quote   = await faGet("/stockquote/SPY");

Need higher limits? The free tier includes 5 requests/day. Upgrade your plan for higher daily quotas and additional endpoints, or contact sales for enterprise access.

API Rate Limits and Throttling Policy

Every response includes rate limit headers:

Header Description
X-RateLimit-LimitMax requests per day (or unlimited)
X-RateLimit-RemainingRequests remaining today
X-RateLimit-ResetUnix timestamp when quota resets
Retry-AfterSeconds to wait (only on 429)

When you hit the daily quota, the API returns 429 with a Retry-After header. Here's how to handle it gracefully:

import time, requests

def fa_get_safe(path, params=None):
    resp = requests.get(f"{BASE}{path}", headers=HEADERS, params=params)

    # Check remaining quota
    remaining = resp.headers.get("X-RateLimit-Remaining")
    if remaining and int(remaining) < 10:
        print(f"Warning: only {remaining} requests remaining today")

    if resp.status_code == 429:
        retry_after = int(resp.headers.get("Retry-After", 60))
        print(f"Rate limited. Retrying in {retry_after}s...")
        time.sleep(retry_after)
        return fa_get_safe(path, params)  # retry once

    resp.raise_for_status()
    return resp.json()
async function faGetSafe(path, params = {}) {
  const url = new URL(`${BASE}${path}`);
  Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));
  const resp = await fetch(url, { headers: HEADERS });

  // Check remaining quota
  const remaining = resp.headers.get("X-RateLimit-Remaining");
  if (remaining && parseInt(remaining) < 10) {
    console.warn(`Warning: only ${remaining} requests remaining today`);
  }

  if (resp.status === 429) {
    const retryAfter = parseInt(resp.headers.get("Retry-After") || "60");
    console.log(`Rate limited. Retrying in ${retryAfter}s...`);
    await new Promise(r => setTimeout(r, retryAfter * 1000));
    return faGetSafe(path, params); // retry once
  }

  if (!resp.ok) throw new Error(`${resp.status}: ${await resp.text()}`);
  return resp.json();
}

API Plans, Pricing Tiers, and Request Quotas

Higher tiers unlock more daily requests and additional endpoints. Free and Basic plans cover stock quotes, exposure endpoints (GEX, DEX, VEX, CHEX, levels), tickers, options meta, symbols, and vol surface. Growth adds summary, narrative, history, option quotes, and full-chain GEX (no expiry filter).

Monthly Yearly Save 20%
Free
$0
forever free
  • 5 requests/day
  • Stock quotes, exposure (GEX, DEX, VEX, CHEX, levels), tickers, options meta, symbols, surface
  • Community support
Get Started
Basic
$49/mo
or $470/yr (save 20%)
14-day free trial
  • 100 requests/day
  • Everything in Free
  • Community support
Most Popular
Growth
$299/mo
or $2,870/yr (save 20%)
14-day free trial
  • 1,000 requests/day
  • + Summary, narrative, history, option quotes, full-chain GEX
  • FlashMind AI included
  • Priority support
Pro
$1,499/mo
or $14,390/yr (save 20%)
14-day free trial
  • Unlimited requests
  • Full access to all endpoints
  • Dedicated support & SLA
Enterprise
Custom
tailored pricing
  • Unlimited requests
  • Full access to all endpoints
  • White-glove onboarding
Contact Sales

Billing: Payments are processed securely via Stripe. You can upgrade, downgrade, or cancel anytime from your account page. Check your current usage with the GET /v1/account endpoint.

Available REST API Endpoints for Options Data

Market Data

  • GET /stockquote/{ticker} — Live stock quote
  • GET /optionquote/{ticker} — Option quotes with greeks (Growth+)
  • GET /v1/options/{ticker} — Option chain metadata (expirations + strikes)
  • GET /v1/surface/{symbol} — Vol surface grid (public, no auth)

Exposure Analytics

  • GET /v1/exposure/gex/{symbol} — Gamma exposure by strike
  • GET /v1/exposure/dex/{symbol} — Delta exposure by strike
  • GET /v1/exposure/vex/{symbol} — Vanna exposure by strike
  • GET /v1/exposure/chex/{symbol} — Charm exposure by strike
  • GET /v1/exposure/summary/{symbol} — Full exposure summary (Growth+)
  • GET /v1/exposure/levels/{symbol} — Key support/resistance levels
  • GET /v1/exposure/narrative/{symbol} — Verbal narrative analysis (Growth+)
  • GET /v1/exposure/history/{symbol} — Daily exposure history (Growth+)

Account & System

  • GET /v1/account — Account info
  • GET /v1/symbols — Tracked symbols
  • GET /v1/tickers — Available tickers
  • GET /health — Health check (public, no auth)

HTTP Error Codes and Error Response Format

The API returns consistent JSON error responses. Errors include a machine-readable error field and a human-readable message or detail.

401 Unauthorized

{
  "title": "Unauthorized",
  "status": 401,
  "detail": "Invalid API key."
}

404 Not Found

{
  "error": "symbol_not_found",
  "message": "No data for XYZ."
}

403 Forbidden (Tier Restricted)

{
  "status": "ERROR",
  "error": "tier_restricted",
  "message": "This endpoint requires a Growth plan or higher.",
  "current_plan": "Free",
  "required_plan": "Growth"
}

429 Too Many Requests

{
  "status": "ERROR",
  "error": "Quota exceeded",
  "message": "You have exceeded your daily API quota of 5 requests on the Free plan.",
  "current_plan": "Free",
  "limit": 5,
  "upgrade_to": "Basic",
  "reset_at": "2026-03-06T00:00:00Z"
}

Handling Errors in Code

resp = requests.get(f"{BASE}/v1/exposure/gex/INVALID", headers=HEADERS)

if resp.status_code == 200:
    data = resp.json()
elif resp.status_code == 401:
    print("Bad API key — check your X-Api-Key header")
elif resp.status_code == 403:
    err = resp.json()
    print(f"Tier restricted: {err['message']}")
    print(f"Current plan: {err['current_plan']}, required: {err['required_plan']}")
elif resp.status_code == 404:
    err = resp.json()
    print(f"Not found: {err['message']}")  # "No data for INVALID."
elif resp.status_code == 429:
    err = resp.json()
    print(f"Quota exceeded on {err['current_plan']} plan")
    print(f"Resets at: {err['reset_at']}")
else:
    print(f"Unexpected error: {resp.status_code}")
const resp = await fetch(`${BASE}/v1/exposure/gex/INVALID`, { headers: HEADERS });

switch (resp.status) {
  case 200:
    const data = await resp.json();
    break;
  case 401:
    console.error("Bad API key — check your X-Api-Key header");
    break;
  case 403:
    const err403 = await resp.json();
    console.error(`Tier restricted: ${err403.message}`);
    console.error(`Current: ${err403.current_plan}, required: ${err403.required_plan}`);
    break;
  case 404:
    const err404 = await resp.json();
    console.error(`Not found: ${err404.message}`);
    break;
  case 429:
    const err429 = await resp.json();
    console.error(`Quota exceeded on ${err429.current_plan} plan`);
    console.error(`Resets at: ${err429.reset_at}`);
    break;
  default:
    console.error(`Unexpected error: ${resp.status}`);
}

API Conventions: JSON Format, Dates, and Pagination

  • All timestamps are UTC in ISO 8601 format
  • All exposure values are in USD notional
  • GEX formula: gamma × OI × 100 × spot² × 0.01
  • Greeks are calculated via Black-Scholes-Merton (BSM) — not sourced from vendor
  • Implied volatility is SVI-smoothed across the surface
  • Any US equity or ETF is supported
  • Responses are cached for 15 seconds
  • Dealer position is the opposite of net exposure (dealers are counterparty)
  • OI changes are day-over-day deltas
FlashAlpha
© FlashAlpha. All rights reserved.