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.

Getting started? Free Options Data API walks through your first API call step by step. Also see: Python SDK Guide for SDK installation and examples.

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

pip install flashalpha
GitHub

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}`);
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Api-Key", "YOUR_API_KEY");
var baseUrl = "https://lab.flashalpha.com";

// 1. Check gamma regime
var summary = JsonDocument.Parse(
    await client.GetStringAsync($"{baseUrl}/v1/exposure/summary/SPY")
).RootElement;

Console.WriteLine($"Regime:     {summary.GetProperty("regime")}");
Console.WriteLine($"Net GEX:    {summary.GetProperty("exposures").GetProperty("net_gex")}");
Console.WriteLine($"Gamma Flip: {summary.GetProperty("gamma_flip")}");

// 2. Get key support/resistance levels
var levels = JsonDocument.Parse(
    await client.GetStringAsync($"{baseUrl}/v1/exposure/levels/SPY")
).RootElement.GetProperty("levels");

Console.WriteLine($"Call Wall:  {levels.GetProperty("call_wall")}");
HttpClient client = HttpClient.newHttpClient();
String BASE = "https://lab.flashalpha.com";
String API_KEY = "YOUR_API_KEY";

// 1. Check gamma regime
HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create(BASE + "/v1/exposure/summary/SPY"))
    .header("X-Api-Key", API_KEY).build();

JsonObject summary = JsonParser.parseString(
    client.send(req, BodyHandlers.ofString()).body()
).getAsJsonObject();

System.out.println("Regime:     " + summary.get("regime"));
System.out.println("Net GEX:    " + summary.getAsJsonObject("exposures").get("net_gex"));
System.out.println("Gamma Flip: " + summary.get("gamma_flip"));
base := "https://lab.flashalpha.com"
apiKey := "YOUR_API_KEY"

// 1. Check gamma regime
req, _ := http.NewRequest("GET", base+"/v1/exposure/summary/SPY", nil)
req.Header.Set("X-Api-Key", apiKey)
resp, _ := http.DefaultClient.Do(req)

var summary map[string]interface{}
json.NewDecoder(resp.Body).Decode(&summary)
resp.Body.Close()

fmt.Printf("Regime:     %v\n", summary["regime"])
exposures := summary["exposures"].(map[string]interface{})
fmt.Printf("Net GEX:    %v\n", exposures["net_gex"])
fmt.Printf("Gamma Flip: %v\n", summary["gamma_flip"])

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");
using System.Text.Json;

var apiKey = Environment.GetEnvironmentVariable("FLASHALPHA_API_KEY");
var baseUrl = "https://lab.flashalpha.com";
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Api-Key", apiKey);

async Task<JsonElement> FaGet(string path)
{
    var json = await client.GetStringAsync($"{baseUrl}{path}");
    return JsonDocument.Parse(json).RootElement;
}

// Usage
var summary = await FaGet("/v1/exposure/summary/SPY");
var quote   = await FaGet("/stockquote/SPY");
String API_KEY = System.getenv("FLASHALPHA_API_KEY");
String BASE = "https://lab.flashalpha.com";
HttpClient client = HttpClient.newHttpClient();

JsonObject faGet(String path) throws Exception {
    HttpRequest req = HttpRequest.newBuilder()
        .uri(URI.create(BASE + path))
        .header("X-Api-Key", API_KEY).build();
    String body = client.send(req, BodyHandlers.ofString()).body();
    return JsonParser.parseString(body).getAsJsonObject();
}

// Usage
JsonObject summary = faGet("/v1/exposure/summary/SPY");
JsonObject quote   = faGet("/stockquote/SPY");
apiKey := os.Getenv("FLASHALPHA_API_KEY")
base   := "https://lab.flashalpha.com"

func faGet(path string) (map[string]interface{}, error) {
    req, _ := http.NewRequest("GET", base+path, nil)
    req.Header.Set("X-Api-Key", apiKey)
    resp, err := http.DefaultClient.Do(req)
    if err != nil { return nil, err }
    defer resp.Body.Close()
    var result map[string]interface{}
    json.NewDecoder(resp.Body).Decode(&result)
    return result, nil
}

// Usage
summary, _ := faGet("/v1/exposure/summary/SPY")
quote, _   := faGet("/stockquote/SPY")
# Set your API key
export FLASHALPHA_API_KEY="YOUR_API_KEY"

# Reusable function
fa_get() {
  curl -s -H "X-Api-Key: $FLASHALPHA_API_KEY" \
    "https://lab.flashalpha.com$1"
}

# Usage
fa_get /v1/exposure/summary/SPY | jq .
fa_get /stockquote/SPY | jq .

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();
}
async Task<JsonElement> FaGetSafe(string path)
{
    var resp = await client.GetAsync($"{baseUrl}{path}");

    // Check remaining quota
    if (resp.Headers.TryGetValues("X-RateLimit-Remaining", out var vals))
    {
        var remaining = int.Parse(vals.First());
        if (remaining < 10)
            Console.WriteLine($"Warning: only {remaining} requests remaining today");
    }

    if (resp.StatusCode == (HttpStatusCode)429)
    {
        var retryAfter = int.Parse(
            resp.Headers.GetValues("Retry-After").FirstOrDefault() ?? "60");
        Console.WriteLine($"Rate limited. Retrying in {retryAfter}s...");
        await Task.Delay(retryAfter * 1000);
        return await FaGetSafe(path); // retry once
    }

    resp.EnsureSuccessStatusCode();
    var json = await resp.Content.ReadAsStringAsync();
    return JsonDocument.Parse(json).RootElement;
}
JsonObject faGetSafe(String path) throws Exception {
    HttpRequest req = HttpRequest.newBuilder()
        .uri(URI.create(BASE + path))
        .header("X-Api-Key", API_KEY).build();
    HttpResponse<String> resp = client.send(req, BodyHandlers.ofString());

    // Check remaining quota
    resp.headers().firstValue("X-RateLimit-Remaining").ifPresent(r -> {
        if (Integer.parseInt(r) < 10)
            System.out.println("Warning: only " + r + " requests remaining today");
    });

    if (resp.statusCode() == 429) {
        int retryAfter = Integer.parseInt(
            resp.headers().firstValue("Retry-After").orElse("60"));
        System.out.println("Rate limited. Retrying in " + retryAfter + "s...");
        Thread.sleep(retryAfter * 1000L);
        return faGetSafe(path); // retry once
    }

    return JsonParser.parseString(resp.body()).getAsJsonObject();
}
func faGetSafe(path string) (map[string]interface{}, error) {
    req, _ := http.NewRequest("GET", base+path, nil)
    req.Header.Set("X-Api-Key", apiKey)
    resp, err := http.DefaultClient.Do(req)
    if err != nil { return nil, err }
    defer resp.Body.Close()

    // Check remaining quota
    if r := resp.Header.Get("X-RateLimit-Remaining"); r != "" {
        if remaining, _ := strconv.Atoi(r); remaining < 10 {
            fmt.Printf("Warning: only %d requests remaining today\n", remaining)
        }
    }

    if resp.StatusCode == 429 {
        retryAfter, _ := strconv.Atoi(resp.Header.Get("Retry-After"))
        if retryAfter == 0 { retryAfter = 60 }
        fmt.Printf("Rate limited. Retrying in %ds...\n", retryAfter)
        time.Sleep(time.Duration(retryAfter) * time.Second)
        return faGetSafe(path) // retry once
    }

    var result map[string]interface{}
    json.NewDecoder(resp.Body).Decode(&result)
    return result, nil
}
# Check rate limit headers with verbose output
curl -v -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/exposure/gex/SPY" 2>&1 | \
  grep -i "x-ratelimit\|retry-after"

# Retry after rate limit (bash)
resp=$(curl -s -w "\n%{http_code}" -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/exposure/gex/SPY")
code=$(echo "$resp" | tail -1)
if [ "$code" = "429" ]; then
  echo "Rate limited  -  waiting 60s..."
  sleep 60
fi

API Plans, Pricing Tiers, and Request Quotas

Higher tiers unlock more daily requests and additional endpoints. Free covers single-expiry GEX, exposure levels, BSM Greeks, IV solver, stock quotes, tickers, options meta, symbols, and vol surface - for individual US equities only (e.g. AAPL, MSFT, TSLA). Basic adds ETFs (SPY, QQQ, IWM...) and index symbols (SPX, VIX, RUT...) on every endpoint, plus DEX/VEX/CHEX exposure by strike, max pain analysis, and access to the Market Overview page. Growth adds exposure summary, narrative, option quotes, volatility analytics, 0DTE analytics, Kelly sizing, full-chain GEX, and the live screener. Alpha adds advanced volatility (SVI surfaces), VRP analytics, historical exposure data, and unlimited requests.

Monthly Yearly Save 20%
Free
$0
forever free
  • 5 requests/day
  • Core endpoints
  • GEX & key levels (individual stocks only)
  • Community support
Get Started
Basic
$79/mo
  • 100 requests/day
  • ETFs & indexes (SPY, QQQ, SPX, VIX...)
  • DEX, VEX, CHEX & max pain
  • Market Overview page
  • Email support
Growth
$299/mo
  • 2,500 requests/day
  • Core endpoints
  • Exposure endpoints
  • Advanced endpoints
  • 0DTE analytics
  • Priority email support
Alpha
$1,499/mo
  • Unlimited requests
  • All endpoints incl. Raw + SVI
  • No 15s cache - real-time
  • Dedicated support + SLA

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 (Free for individual stocks; Basic+ for ETFs & indexes)
  • GET /v1/exposure/dex/{symbol} - Delta exposure by strike (Basic+)
  • GET /v1/exposure/vex/{symbol} - Vanna exposure by strike (Basic+)
  • GET /v1/exposure/chex/{symbol} - Charm exposure by strike (Basic+)
  • GET /v1/maxpain/{symbol} - Max pain analysis (pain curve, pin probability, dealer alignment) (Basic+)
  • GET /v1/exposure/summary/{symbol} - Full exposure summary (Growth+)
  • GET /v1/exposure/levels/{symbol} - Key support/resistance levels (Free for individual stocks; Basic+ for ETFs & indexes)
  • GET /v1/exposure/narrative/{symbol} - Verbal narrative analysis (Growth+)
  • GET /v1/exposure/history/{symbol} - Daily exposure history (Alpha+) coming soon

Volatility Analytics

  • GET /v1/volatility/{symbol} - Comprehensive volatility analysis (realized vol, skew, hedging scenarios, liquidity) (Growth+)
  • GET /v1/adv_volatility/{symbol} - Advanced: SVI parameters, variance surface, arbitrage detection, greeks surfaces, variance swap pricing (Alpha)

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}`);
}
var resp = await client.GetAsync($"{baseUrl}/v1/exposure/gex/INVALID");
var body = await resp.Content.ReadAsStringAsync();

switch ((int)resp.StatusCode)
{
    case 200:
        var data = JsonDocument.Parse(body).RootElement;
        break;
    case 401:
        Console.WriteLine("Bad API key  -  check your X-Api-Key header");
        break;
    case 403:
        var err403 = JsonDocument.Parse(body).RootElement;
        Console.WriteLine($"Tier restricted: {err403.GetProperty("message")}");
        Console.WriteLine($"Current: {err403.GetProperty("current_plan")}, required: {err403.GetProperty("required_plan")}");
        break;
    case 404:
        var err404 = JsonDocument.Parse(body).RootElement;
        Console.WriteLine($"Not found: {err404.GetProperty("message")}");
        break;
    case 429:
        var err429 = JsonDocument.Parse(body).RootElement;
        Console.WriteLine($"Quota exceeded on {err429.GetProperty("current_plan")} plan");
        Console.WriteLine($"Resets at: {err429.GetProperty("reset_at")}");
        break;
    default:
        Console.WriteLine($"Unexpected error: {(int)resp.StatusCode}");
        break;
}
HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create(BASE + "/v1/exposure/gex/INVALID"))
    .header("X-Api-Key", API_KEY).build();
HttpResponse<String> resp = client.send(req, BodyHandlers.ofString());

switch (resp.statusCode()) {
    case 200:
        JsonObject data = JsonParser.parseString(resp.body()).getAsJsonObject();
        break;
    case 401:
        System.out.println("Bad API key  -  check your X-Api-Key header");
        break;
    case 403:
        JsonObject err403 = JsonParser.parseString(resp.body()).getAsJsonObject();
        System.out.println("Tier restricted: " + err403.get("message"));
        break;
    case 404:
        JsonObject err404 = JsonParser.parseString(resp.body()).getAsJsonObject();
        System.out.println("Not found: " + err404.get("message"));
        break;
    case 429:
        JsonObject err429 = JsonParser.parseString(resp.body()).getAsJsonObject();
        System.out.println("Quota exceeded on " + err429.get("current_plan") + " plan");
        break;
    default:
        System.out.println("Unexpected error: " + resp.statusCode());
}
req, _ := http.NewRequest("GET", base+"/v1/exposure/gex/INVALID", nil)
req.Header.Set("X-Api-Key", apiKey)
resp, _ := http.DefaultClient.Do(req)
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()

switch resp.StatusCode {
case 200:
    var data map[string]interface{}
    json.Unmarshal(body, &data)
case 401:
    fmt.Println("Bad API key  -  check your X-Api-Key header")
case 403:
    var err map[string]interface{}
    json.Unmarshal(body, &err)
    fmt.Printf("Tier restricted: %v\n", err["message"])
case 404:
    var err map[string]interface{}
    json.Unmarshal(body, &err)
    fmt.Printf("Not found: %v\n", err["message"])
case 429:
    var err map[string]interface{}
    json.Unmarshal(body, &err)
    fmt.Printf("Quota exceeded on %v plan\n", err["current_plan"])
default:
    fmt.Printf("Unexpected error: %d\n", resp.StatusCode)
}
# Check HTTP status code with curl
resp=$(curl -s -o /dev/null -w "%{http_code}" \
  -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/exposure/gex/INVALID")

echo "Status: $resp"

# Get full error body
curl -s -H "X-Api-Key: YOUR_API_KEY" \
  "https://lab.flashalpha.com/v1/exposure/gex/INVALID" | jq .

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 derived from BSM inversion. The svi_vol field (SVI-smoothed IV) requires the Alpha plan - non-Alpha plans receive "REQUIRES_ALPHA_TIER".
  • Any US equity is supported on Free; ETFs (SPY, QQQ, IWM...) and index symbols (SPX, VIX, RUT...) require Basic+
  • Responses are cached for 15 seconds (Alpha plan bypasses cache)
  • Dealer position is the opposite of net exposure (dealers are counterparty)
  • OI changes are day-over-day deltas

Ready to build?

Get your free API key and start pulling live options data in 30 seconds.