Build a GEX Dashboard with an API: Gamma Exposure Monitor for Developers | FlashAlpha

Build a GEX Dashboard with an API: Gamma Exposure Monitor for Developers

Step-by-step guide to building a gamma exposure (GEX) dashboard using the FlashAlpha API. Get per-strike GEX, gamma flip, call/put walls, dealer regime, and key levels in a single API call. Includes Python, JavaScript, and cURL examples with a complete dashboard layout.

T
Tomasz Dobrowolski Quant Engineer
Mar 29, 2026
45 min read
GEX GammaExposure API Dashboard Python DeveloperGuide OptionsAnalytics

If you're searching for a GEX API, a gamma exposure data source, or trying to figure out how to build a GEX dashboard without computing everything yourself, this is the guide. A handful of endpoints give you the full GEX picture: per-strike gamma exposure, gamma flip level, call/put walls, dealer regime, key levels, and higher-order Greek exposures (DEX, VEX, CHEX) that most dashboards ignore entirely.


What Is GEX and Why Does It Matter?

Gamma exposure (GEX) measures the aggregate gamma that market makers hold across all strikes and expirations for a given symbol. When dealers are long gamma (positive GEX), they hedge by buying dips and selling rips, which dampens volatility. When dealers are short gamma (negative GEX), they hedge in the same direction as the move, amplifying it. Understanding which regime the market is in tells you whether to expect mean reversion or trend continuation.

A GEX dashboard answers three questions:

  1. Where is the gamma flip? The price level where dealer positioning switches from long to short gamma. Below the flip, expect amplified moves. Above it, expect pinning.
  2. Where are the key levels? Call walls (resistance from dealer hedging), put walls (support), and the highest OI strikes that act as magnets.
  3. How is gamma distributed across strikes? The per-strike GEX profile shows you exactly where dealer hedging pressure concentrates.

For deeper background, see What Is Gamma Exposure (GEX) Explained and GEX Trading Guide for SPY and TSLA.

The API Approach: Multiple Endpoints, Complete Dashboard

The FlashAlpha exposure endpoints return everything you need for a production GEX dashboard. Here's what each endpoint provides:

EndpointTierWhat It Returns
/v1/exposure/gex/{symbol}FreePer-strike GEX with call/put breakdown, OI, volume, OI change
/v1/exposure/levels/{symbol}FreeGamma flip, call wall, put wall, max gamma strikes, highest OI, 0DTE magnet
/v1/exposure/summary/{symbol}Growth+Net GEX/DEX/VEX/CHEX, gamma regime, verbal interpretation, dealer hedging estimates
/v1/exposure/dex/{symbol}FreeDelta exposure by strike (same structure as GEX)
/v1/exposure/vex/{symbol}FreeVanna exposure by strike
/v1/exposure/chex/{symbol}FreeCharm exposure by strike

The GEX and levels endpoints are free tier. You can build a fully functional gamma exposure dashboard without spending anything. The summary endpoint on the Growth plan adds regime analysis, verbal interpretation, and dealer hedging estimates for more sophisticated dashboards.

Per-strike GEX, gamma flip, call/put walls, and dealer regime for 6,000+ symbols

Free tier available. No credit card required. 5 requests/day to start.

Get API Access

Quick Start: Your First GEX Call

from flashalpha import FlashAlpha

fa = FlashAlpha("YOUR_KEY")
gex = fa.gex("SPY")

print(f"Net GEX:      {gex['net_gex']:,.0f}")
print(f"Gamma flip:   {gex['gamma_flip']}")
print(f"Price:         {gex['underlying_price']}")
print(f"Strikes:       {len(gex['strikes'])} levels")

# Top 5 strikes by absolute net GEX
top = sorted(gex["strikes"], key=lambda s: abs(s["net_gex"]), reverse=True)[:5]
for s in top:
    print(f"  {s['strike']:>8.1f}  call={s['call_gex']:>12,.0f}  put={s['put_gex']:>12,.0f}  net={s['net_gex']:>12,.0f}")
import { FlashAlpha } from 'flashalpha';

const fa = new FlashAlpha('YOUR_KEY');
const gex = await fa.gex('SPY');

console.log(`Net GEX: ${gex.net_gex.toLocaleString()}`);
console.log(`Gamma flip: ${gex.gamma_flip}`);
console.log(`Strikes: ${gex.strikes.length} levels`);

// Top 5 by absolute net GEX
const top = [...gex.strikes]
  .sort((a, b) => Math.abs(b.net_gex) - Math.abs(a.net_gex))
  .slice(0, 5);
top.forEach(s => console.log(`  ${s.strike}: net=${s.net_gex}`));
using FlashAlpha;

var fa = new FlashAlphaClient("YOUR_KEY");
var gex = await fa.GexAsync("SPY");

Console.WriteLine($"Net GEX:    {gex.NetGex:N0}");
Console.WriteLine($"Gamma flip: {gex.GammaFlip}");
Console.WriteLine($"Strikes:    {gex.Strikes.Count} levels");
fa := flashalpha.NewClient("YOUR_KEY")
gex, _ := fa.Gex(ctx, "SPY")
fmt.Printf("Net GEX: %v\n", gex["net_gex"])
fmt.Printf("Gamma flip: %v\n", gex["gamma_flip"])
FlashAlphaClient fa = new FlashAlphaClient("YOUR_KEY");
JsonObject gex = fa.gex("SPY");
System.out.println("Net GEX: " + gex.get("net_gex"));
System.out.println("Gamma flip: " + gex.get("gamma_flip"));
curl -H "X-Api-Key: YOUR_KEY" \
  "https://lab.flashalpha.com/v1/exposure/gex/SPY"
$ pip install flashalpha  |  npm install flashalpha  |  dotnet add package FlashAlpha  |  go get github.com/FlashAlpha-lab/flashalpha-go

Full Response Walkthrough

Here's what the /v1/exposure/gex/SPY response looks like:

{
  "symbol": "SPY",
  "underlying_price": 587.23,
  "as_of": "2026-03-29T15:45:00Z",
  "gamma_flip": 585.0,
  "net_gex": 1847293000,
  "net_gex_label": "1.85B",
  "strikes": [
    {
      "strike": 580.0,
      "call_gex": 42891000,
      "put_gex": -187340000,
      "net_gex": -144449000,
      "call_oi": 28451,
      "put_oi": 54312,
      "call_volume": 3201,
      "put_volume": 8745,
      "call_oi_change": 1240,
      "put_oi_change": -3102
    },
    {
      "strike": 585.0,
      "call_gex": 312450000,
      "put_gex": -298100000,
      "net_gex": 14350000,
      "call_oi": 41230,
      "put_oi": 38920,
      "call_volume": 5120,
      "put_volume": 4890,
      "call_oi_change": 2100,
      "put_oi_change": 1800
    },
    {
      "strike": 590.0,
      "call_gex": 534210000,
      "put_gex": -112340000,
      "net_gex": 421870000,
      "call_oi": 62100,
      "put_oi": 21300,
      "call_volume": 7800,
      "put_volume": 2100,
      "call_oi_change": 4500,
      "put_oi_change": -800
    }
  ]
}

Key fields to understand:

  • gamma_flip — The strike where net GEX crosses from negative to positive. Price above this level = positive gamma (pinning). Below = negative gamma (amplification).
  • net_gex — Total net gamma exposure across all strikes. Positive means dealers are long gamma overall.
  • net_gex_label — Human-readable formatting of net GEX ("1.85B").
  • call_gex / put_gex — Per-strike gamma exposure from call and put options separately. Call GEX is positive; put GEX is negative.
  • call_oi_change / put_oi_change — Day-over-day open interest change. Shows where new positions are being opened or closed.

And here's the /v1/exposure/levels/SPY response:

{
  "gamma_flip": 585.0,
  "call_wall": 600.0,
  "put_wall": 570.0,
  "max_positive_gamma": 590.0,
  "max_negative_gamma": 575.0,
  "highest_oi_strike": 590.0,
  "zero_dte_magnet": 587.0
}

These are the key levels your dashboard should display prominently. The call wall is where call gamma concentrates most heavily (resistance). The put wall is where put gamma concentrates (support). The zero DTE magnet is the strike that 0DTE flows are gravitating toward during the current session.

Building the Dashboard: Section by Section

1. GEX by Strike Bar Chart

The centerpiece of any GEX dashboard is the per-strike bar chart. Each bar represents net gamma exposure at a strike, with positive bars (call-dominated) pointing up and negative bars (put-dominated) pointing down.

import matplotlib.pyplot as plt

fa = FlashAlpha("YOUR_KEY")
gex = fa.gex("SPY")

strikes = [s["strike"] for s in gex["strikes"]]
net_gex = [s["net_gex"] for s in gex["strikes"]]
colors = ["#16a34a" if g >= 0 else "#dc2626" for g in net_gex]

fig, ax = plt.subplots(figsize=(14, 6))
ax.bar(strikes, net_gex, color=colors, width=0.8)
ax.axvline(gex["underlying_price"], color="#2563eb", linestyle="--", label="Spot")
ax.axvline(gex["gamma_flip"], color="#f59e0b", linestyle="-", linewidth=2, label="Gamma Flip")
ax.set_xlabel("Strike")
ax.set_ylabel("Net GEX ($)")
ax.set_title(f"SPY Gamma Exposure by Strike | Net: {gex['net_gex_label']}")
ax.legend()
plt.tight_layout()
plt.savefig("spy_gex.png", dpi=150)

For a web dashboard, map the same data to a D3.js, Chart.js, or Lightweight Charts bar chart. The strikes array is already sorted by strike price.

2. Gamma Flip Level

The gamma flip is the single most important number on a GEX dashboard. Display it prominently with context:

levels = fa.exposure_levels("SPY")
price = gex["underlying_price"]
flip = levels["gamma_flip"]

regime = "POSITIVE GAMMA" if price > flip else "NEGATIVE GAMMA"
distance = ((price - flip) / price) * 100

print(f"Gamma Flip:  {flip}")
print(f"Spot Price:  {price}")
print(f"Regime:      {regime}")
print(f"Distance:    {distance:+.2f}%")

Show the gamma flip as a horizontal line on the price chart. Color the background green when price is above the flip (positive gamma, expect pinning) and red when below (negative gamma, expect amplification).

3. Call Wall / Put Wall / Key Levels

The levels endpoint gives you all the key price levels in one call. Display these as horizontal lines on your price chart or as a compact summary panel:

levels = fa.exposure_levels("SPY")

key_levels = [
    ("Call Wall (resistance)", levels["call_wall"]),
    ("Put Wall (support)", levels["put_wall"]),
    ("Max + Gamma", levels["max_positive_gamma"]),
    ("Max - Gamma", levels["max_negative_gamma"]),
    ("Highest OI", levels["highest_oi_strike"]),
    ("0DTE Magnet", levels["zero_dte_magnet"]),
    ("Gamma Flip", levels["gamma_flip"]),
]

for label, value in key_levels:
    print(f"  {label:<25s} {value:>8.1f}")

On a chart, the call wall and put wall define the likely range. The 0DTE magnet is where intraday price tends to gravitate as 0DTE options decay. These levels update throughout the trading day.

4. Dealer Regime Indicator

The summary endpoint (Growth plan) provides a verbal interpretation of the current gamma regime and dealer hedging estimates:

summary = fa.exposure_summary("SPY")

print(f"Gamma Regime:  {summary['gamma_regime']}")
print(f"Net GEX:       {summary['net_gex_label']}")
print(f"Net DEX:       {summary['net_dex_label']}")
print(f"Net VEX:       {summary['net_vex_label']}")
print(f"Net CHEX:      {summary['net_chex_label']}")
print(f"Interpretation: {summary['interpretation']}")
print(f"Dealer hedge on +1%: {summary['dealer_hedge_up_1pct']}")
print(f"Dealer hedge on -1%: {summary['dealer_hedge_down_1pct']}")

The regime indicator is perfect for a status badge at the top of your dashboard. Green for positive gamma (low vol expected), red for negative gamma (high vol expected), amber for neutral/transition.

5. Multi-Expiry View

The GEX endpoint accepts an optional expiration parameter to filter by specific expiration date. Use this to build a multi-expiry breakdown:

# Compare GEX profiles across expirations
expirations = ["2026-03-30", "2026-04-03", "2026-04-17"]

for exp in expirations:
    gex = fa.gex("SPY", expiration=exp)
    total = sum(s["net_gex"] for s in gex["strikes"])
    top_strike = max(gex["strikes"], key=lambda s: abs(s["net_gex"]))
    print(f"{exp}:  net_gex={total:>14,.0f}  top_strike={top_strike['strike']}")

This reveals whether GEX is concentrated in 0DTE/weekly options (short-term pinning) or monthly expirations (structural positioning). A dashboard that only shows aggregate GEX misses this distinction.

6. OI Change Tracking (Day-Over-Day)

Every strike in the GEX response includes call_oi_change and put_oi_change fields. These show where new positions are being opened or closed:

gex = fa.gex("SPY")

# Find strikes with largest OI increases
oi_movers = sorted(
    gex["strikes"],
    key=lambda s: abs(s["call_oi_change"]) + abs(s["put_oi_change"]),
    reverse=True
)[:10]

print("Top OI Movers:")
for s in oi_movers:
    print(f"  {s['strike']:>8.1f}  call_oi_change={s['call_oi_change']:>+7,d}  put_oi_change={s['put_oi_change']:>+7,d}")

Large OI increases at a specific strike tell you new positions are being established there, which strengthens that level as a magnet or wall. OI decreases mean positions are unwinding and the level is losing significance. Track this day-over-day to see how the GEX profile evolves.

Higher-Order Greek Exposures: DEX, VEX, CHEX

A GEX-only dashboard misses three critical forces that move markets. The FlashAlpha API provides all of them on the free tier:

  • DEX (Delta Exposure)/v1/exposure/dex/{symbol} — Shows directional dealer exposure. High DEX at a strike means dealer hedging will create strong directional flow there.
  • VEX (Vanna Exposure)/v1/exposure/vex/{symbol} — Shows how dealer hedging changes when IV moves. VEX drives the "vol-down, market-up" dynamic and explains why IV crushes coincide with rallies.
  • CHEX (Charm Exposure)/v1/exposure/chex/{symbol} — Shows time-decay-driven hedging flows. CHEX explains the predictable flows into the close as theta decay forces dealers to adjust.
# Full Greek exposure dashboard
fa = FlashAlpha("YOUR_KEY")

gex = fa.gex("SPY")
dex = fa.dex("SPY")
vex = fa.vex("SPY")
chex = fa.chex("SPY")

print(f"GEX  net: {gex['net_gex']:>14,.0f}  (gamma)")
print(f"DEX  net: {dex['net_dex']:>14,.0f}  (delta)")
print(f"VEX  net: {vex['net_vex']:>14,.0f}  (vanna)")
print(f"CHEX net: {chex['net_chex']:>14,.0f}  (charm)")

For deeper background on why GEX alone is insufficient, see Why GEX Isn't Enough: Vanna and Charm Exposure.

GEX + DEX + VEX + CHEX: all four Greek exposures on the free tier

Per-strike breakdowns with OI, volume, and day-over-day changes.

Get API Access

Multi-Symbol GEX Scanner

A single-symbol dashboard is useful. A scanner that monitors GEX across your watchlist is powerful. Here's a multi-symbol scanner that identifies regime changes and key level breaches:

from flashalpha import FlashAlpha

fa = FlashAlpha("YOUR_KEY")
symbols = ["SPY", "QQQ", "IWM", "TSLA", "AAPL", "NVDA", "AMD", "META", "AMZN", "MSFT"]

print(f"{'Symbol':<8} {'Price':>8} {'Flip':>8} {'Regime':<10} {'Net GEX':>14} {'Call Wall':>10} {'Put Wall':>10}")
print("-" * 78)

for sym in symbols:
    gex = fa.gex(sym)
    levels = fa.exposure_levels(sym)

    price = gex["underlying_price"]
    flip = gex["gamma_flip"]
    regime = "+" if price > flip else "-"

    print(f"{sym:<8} {price:>8.2f} {flip:>8.1f} {'POS GAMMA' if regime == '+' else 'NEG GAMMA':<10} "
          f"{gex['net_gex']:>14,.0f} {levels['call_wall']:>10.1f} {levels['put_wall']:>10.1f}")
import { FlashAlpha } from 'flashalpha';

const fa = new FlashAlpha('YOUR_KEY');
const symbols = ['SPY', 'QQQ', 'IWM', 'TSLA', 'AAPL', 'NVDA'];

const results = await Promise.all(
  symbols.map(async (sym) => {
    const gex = await fa.gex(sym);
    const levels = await fa.exposureLevels(sym);
    return {
      symbol: sym,
      price: gex.underlying_price,
      flip: gex.gamma_flip,
      regime: gex.underlying_price > gex.gamma_flip ? 'POS' : 'NEG',
      net_gex: gex.net_gex,
      call_wall: levels.call_wall,
      put_wall: levels.put_wall,
    };
  })
);

// Flag regime changes
results.forEach(r => {
  if (r.regime === 'NEG') {
    console.warn(`${r.symbol}: NEGATIVE GAMMA - price ${r.price} below flip ${r.flip}`);
  }
});
using FlashAlpha;

var fa = new FlashAlphaClient("YOUR_KEY");
var symbols = new[] { "SPY", "QQQ", "IWM", "TSLA", "AAPL", "NVDA" };

foreach (var sym in symbols)
{
    var gex = await fa.GexAsync(sym);
    var levels = await fa.ExposureLevelsAsync(sym);
    var regime = gex.UnderlyingPrice > gex.GammaFlip ? "POS" : "NEG";
    Console.WriteLine($"{sym,-8} {gex.UnderlyingPrice,8:F2} {gex.GammaFlip,8:F1} {regime,-10} {gex.NetGex,14:N0} {levels.CallWall,10:F1} {levels.PutWall,10:F1}");
}
fa := flashalpha.NewClient("YOUR_KEY")
symbols := []string{"SPY", "QQQ", "IWM", "TSLA", "AAPL", "NVDA"}

for _, sym := range symbols {
    gex, _ := fa.Gex(ctx, sym)
    levels, _ := fa.ExposureLevels(ctx, sym)
    regime := "POS"
    if gex["underlying_price"].(float64) < gex["gamma_flip"].(float64) {
        regime = "NEG"
    }
    fmt.Printf("%-8s %8.2f %8.1f %-10s %14.0f %10.1f %10.1f\n",
        sym, gex["underlying_price"], gex["gamma_flip"], regime,
        gex["net_gex"], levels["call_wall"], levels["put_wall"])
}
FlashAlphaClient fa = new FlashAlphaClient("YOUR_KEY");
String[] symbols = {"SPY", "QQQ", "IWM", "TSLA", "AAPL", "NVDA"};

for (String sym : symbols) {
    JsonObject gex = fa.gex(sym);
    JsonObject levels = fa.exposureLevels(sym);
    String regime = gex.getDouble("underlying_price") > gex.getDouble("gamma_flip") ? "POS" : "NEG";
    System.out.printf("%-8s %8.2f %8.1f %-10s %,14.0f %10.1f %10.1f%n",
        sym, gex.getDouble("underlying_price"), gex.getDouble("gamma_flip"), regime,
        gex.getDouble("net_gex"), levels.getDouble("call_wall"), levels.getDouble("put_wall"));
}

Add alerts when a symbol crosses its gamma flip level, approaches a wall, or when net GEX flips sign. These regime transitions are the highest-signal events for GEX-based trading.

AI Agents and MCP Integration

If you're building AI-powered trading tools, the FlashAlpha MCP server gives Claude, Cursor, Windsurf, and other AI assistants direct access to GEX data. No REST calls, no SDK — the agent calls the tools natively.

Add the MCP server to your claude_desktop_config.json or .cursor/mcp.json:

{
  "mcpServers": {
    "flashalpha": {
      "url": "https://lab.flashalpha.com/mcp",
      "headers": {
        "X-Api-Key": "YOUR_KEY"
      }
    }
  }
}

Once configured, you can ask the agent:

  • "What's the gamma flip for SPY right now?"
  • "Show me GEX by strike for TSLA and identify the key levels."
  • "Is NVDA in positive or negative gamma? Where are the walls?"
  • "Build me a multi-symbol GEX scanner for my watchlist."

The agent calls the GEX and levels endpoints directly, interprets the data, and presents actionable analysis. This is how you build an AI-powered GEX dashboard without writing any frontend code — the agent IS the dashboard.

Why Not Compute GEX Yourself?

You can. Here's what that requires:

  1. Options chain data source — Raw chains for all strikes and expirations, updated throughout the day. Budget $200-2,500/mo from providers like Intrinio, Polygon, or ThetaData.
  2. Open interest aggregation — Aggregate OI across all expirations for each strike. Handle weeklies, monthlies, quarterlies, LEAPs. Track day-over-day changes.
  3. Dealer model assumptions — Decide what fraction of OI is dealer-held. The standard assumption is that market makers are net short options, but the ratio varies by strike, expiry, and symbol.
  4. Gamma computation per strike — Calculate dollar gamma for each option at each strike using Black-Scholes or a more sophisticated model. Requires implied volatility, rates, dividends, and days to expiration.
  5. Gamma flip detection — Find the price level where cumulative net GEX crosses zero. This requires computing GEX at every strike and interpolating.
  6. Key level identification — Identify call walls, put walls, highest OI strikes, and 0DTE magnets from the aggregated data.
  7. Higher-order Greeks — Compute delta, vanna, and charm exposure using the same pipeline. Vanna and charm require second-order sensitivities.
  8. Infrastructure — Run all of this intraday for your universe. Handle data quality issues, missing strikes, corporate actions, and API failures from your data provider.

That's 3-6 months of engineering before you display a single bar chart. The FlashAlpha exposure endpoints exist specifically so you don't have to do any of this.

API Access and Pricing

The GEX, DEX, VEX, CHEX, and levels endpoints are all available on the free tier. The summary endpoint requires the Growth plan. Here's the full breakdown:

PlanPriceGEX/DEX/VEX/CHEXLevelsSummaryRate Limit
Free$0YesYesNo5 req/day
Basicfrom $63/moYesYesNo100 req/day
Growthfrom $239/moYesYesYes2,500 req/day
Alphafrom $1,199/moYesYesYesUnlimited

For a single-symbol GEX dashboard refreshing every 5 minutes during market hours (6.5 hours), you need ~78 calls/day for GEX + levels. The Basic plan covers this. For a multi-symbol scanner with 10 symbols, you need ~780 calls/day — the Growth plan covers this and unlocks the summary endpoint for regime analysis.

To evaluate before committing, the interactive API playground lets you test GEX calls in the browser with your API key. The per-stock dashboards visualize every metric the API returns, so you can see exactly what you're building against before writing code.

Frequently Asked Questions

All 6,000+ US equities and ETFs tracked by FlashAlpha. Check /v1/symbols for the full list. The most liquid symbols (SPY, QQQ, IWM, TSLA, AAPL, NVDA) have the deepest strike coverage. Index symbols (SPX, VIX, RUT) require Basic plan or higher.
GEX data is computed from live options chains and updated throughout the trading day. The as_of field in the response tells you the exact snapshot time. Higher-tier plans receive fresher data with lower cache times.
Yes. The /v1/exposure/gex/{symbol} endpoint accepts an optional expiration parameter (format: YYYY-MM-DD). Use this to isolate 0DTE GEX, weekly GEX, or monthly GEX. You can also use the min_oi parameter to filter out low-OI strikes that add noise to the profile.
The gamma flip is the price level where aggregate net gamma exposure crosses zero — it's computed across all expirations and represents the structural regime boundary. The zero DTE magnet is the strike where 0DTE option flows concentrate during the current session — it's a short-term intraday level. The gamma flip changes slowly (driven by monthly/quarterly OI); the 0DTE magnet changes every session.
Negative GEX at a strike means put gamma dominates there. Dealers holding those puts will hedge in the same direction as price moves: selling as price falls, buying as price rises. This amplifies moves around that strike. Conversely, positive GEX means call gamma dominates and dealers hedge against the move, dampening volatility.
The call_oi_change and put_oi_change fields show day-over-day changes. Large OI increases at a strike mean new positions are being established, strengthening that level as a wall or magnet. OI decreases mean positions are unwinding. Track OI changes over multiple days to see how the GEX profile evolves and anticipate when key levels will shift.

Live Market Pulse

Get tick-by-tick visibility into market shifts with full-chain analytics streaming in real time.

Intelligent Screening

Screen millions of option pairs per second using your custom EV rules, filters, and setups.

Execution-Ready

Instantly send structured orders to Interactive Brokers right from your scan results.

Join the Community

Discord

Engage in real time conversations with us!

Twitter / X

Follow us for real-time updates and insights!

GitHub

Explore our open-source SDK, examples, and analytics resources!