VolStrata

A free feed of today's key gamma levels

Every trading day we work out four levels from the options chain of a handful of major index and ETF symbols — the call wall, the put wall, the gamma flip and max pain. This page documents the endpoint that hands them to anyone who asks. It is free, it needs no key and no account, and it is open to browsers from any domain, so you can call it from a blog post, a notebook, a dashboard or your own tool.

What the numbers are

Four levels, plus the spot price they were measured against.

LevelWhat it is
Call WallThe largest positive-gamma strike above spot — dealers sell into rallies here, so it often caps upside (resistance).
Put WallThe largest positive-gamma strike below spot — dealer buying tends to cushion selloffs here (support).
Gamma FlipThe gamma-flip / zero-gamma level: above it dealers dampen moves (mean-reversion), below it they amplify them (trend).
Max PainThe strike where the most option value expires worthless — a weak magnet into monthly/weekly expiry.
SpotThe current underlying price the levels are measured against.

The endpoint

GET https://volstrata.com/embed/levels.json?ticker=SPX

Returns one JSON object. No key, no account, no sign-up. It answers with Access-Control-Allow-Origin: *, so you can call it straight from a browser on any domain. Responses carry Cache-Control: public, max-age=30.

Send a User-Agent that names your project. A bare default library user-agent can be refused at the edge with a 403 — if a script gets one and a browser does not, that is why. The Python example below shows it.

Parameters

NameRequiredWhat it does
tickernoThe symbol you want. Currently published: SPX, NDX, QQQ, SPY. Defaults to SPX. Anything else is answered with SPX and clamped: true rather than an error — this endpoint does not return a 4xx for a symbol it does not carry, so do not use the status code to test whether a symbol exists. Read clamped.

A real response

This is not a mock. It is an actual response for SPX, captured as this page was rendered (Sep 15, 2026 · 12:06 PM ET), pretty-printed (the wire response is a single line).

{
  "ticker": "SPX",
  "requested_ticker": "SPX",
  "clamped": false,
  "spot": 7583.58,
  "call_wall": 7675.0,
  "put_wall": 7580.0,
  "flip": 7592.97,
  "max_pain": 7650.0,
  "regime": null,
  "net_gex_bn": null,
  "as_of_epoch": 1789488387,
  "as_of_label": "Sep 15, 2026 · 12:06 PM ET",
  "delayed": false,
  "delay_secs": 0,
  "stale": false,
  "has_data": true,
  "source": "live",
  "status": "ok",
  "reason": null
}

Every field

Read straight off the response above, so this table cannot describe a field the endpoint no longer sends or miss one it has started sending. Anything marked null now was null in that exact response.

FieldTypeWhat it is
tickerstringThe symbol these levels belong to. This is the symbol we answered with, which is not always the one you asked for — see clamped.
requested_tickerstring or nullExactly what you passed in ?ticker=, echoed back. Null when you passed nothing at all.
clampedbooleanTrue when you asked for a symbol this feed does not publish and we answered with SPX instead of an error.
spotnumber or nullThe price of the underlying that the levels were measured against.
call_wallnumber or nullThe call wall strike — the level that most often acts as resistance.
put_wallnumber or nullThe put wall strike — the level that most often acts as support.
flipnumber or nullThe gamma flip (zero-gamma) level. Above it moves tend to be dampened, below it they tend to be amplified.
max_painnumber or nullThe max pain strike — where the most option value expires worthless.
regimenull nowstring or nullA one-word summary of the gamma regime. Frequently null on this feed; always check before you rely on it.
net_gex_bnnull nownumber or nullNet gamma exposure in billions. Frequently null on this feed; always check before you rely on it.
as_of_epochinteger or nullUnix time, in seconds, of the market data these levels were computed from. This is the age of the NUMBERS, not the time you called us — subtract it from your own clock to find out how old they are.
as_of_labelstringThe same instant already formatted in US Eastern time, for display. The string “—” when we do not have a timestamp.
delayedbooleanTrue when a deliberate delay was applied to this symbol before serving it to you. See How fresh is it, really.
delay_secsintegerThe size of that delay in seconds, or 0. It is a FLOOR, not a promise of precision — read the note under the freshness table.
stalebooleanTrue when we could not build a level set at the point you were entitled to and are showing you the most recent one we do have instead. as_of_epoch still tells you its real age.
has_databooleanFalse before the first level set of the session exists. Every price field is null when this is false, and the response is still a 200.
sourcestringHow this record was arrived at: “live”, “delayed”, “last-known”, or “none” when there is nothing to serve yet.
statusstringWhat kind of answer this is: “ok” when the levels below are a real reading, “no_data” when the session has genuinely not published any yet (nothing is wrong — it is pre-open, or a sparsely-polled symbol), and “unavailable” when WE failed and this record is standing in for a reading we could not take. Branch on this rather than inferring health from the absence of numbers: the last two look identical otherwise.
reasonnull nowstring or nullA short machine-readable tag naming the fault when status is “unavailable”, and null otherwise. Log it; do not parse it for meaning — the set of tags is not part of this contract.

Any price field can be null. Before the first level set of a session exists, has_data is false, every price is null, and the response is still a 200 — handle that case rather than assuming a number is always there.

How fresh is it, really

There is no single answer, so here are the actual numbers, read off the feed as this page rendered (Sep 15, 2026 · 12:06 PM ET). Some symbols are served with no delay applied and others are held back; do not assume either.

SymbolDelay applieddelayed delay_secsAge of the data right now
SPXNo delay applied — served as soon as it exists.false no clamp027 s
NDXNo delay applied — served as soon as it exists.false no clamp03 min 12 s
QQQHeld back by at least 30 minutes before it reaches you.true180031 min 22 s
SPYHeld back by at least 30 minutes before it reaches you.true180032 min 6 s

Do not take this page's word for it. Every response carries delayed, delay_secs and as_of_epoch. Those three are the authoritative answer for that request; the table above is the same three values read at one moment. If we ever change what we hold back, your response changes with it and this table changes with it. The fields cannot go out of date; a sentence can.

Using it — what we ask

Use the feed for anything you like, including commercial work. There is one condition and a few requests.

The condition: a visible credit link

  1. Wherever the numbers are shown, show a visible, clickable link back to volstrata.com. A credit your readers cannot see or cannot click does not count.
  2. Paste-ready:
    <a href="https://volstrata.com/">Gamma levels by VolStrata</a>
  3. Show the age. If a reader can see the numbers, they should be able to see how old they are — pass as_of_label through, or render as_of_epoch yourself.
  4. Do not present it as your own market data, and do not imply we endorse or are affiliated with what you built.

Fair use: what is actually enforced

No rate limit is enforced on this endpoint today, and there is no key to ask for. We would rather you used it than wrote to us for permission. So this is a request, not a gate:

If a limit ever becomes necessary, it will be written on this page before it is enforced.

Copy and paste

Command line

curl -s 'https://volstrata.com/embed/levels.json?ticker=SPX'

In a browser

// Runs in a browser from any origin — no key, no proxy.
const r = await fetch('https://volstrata.com/embed/levels.json?ticker=SPX');
const levels = await r.json();
console.log(levels.call_wall, levels.put_wall, levels.as_of_label);
if (levels.delayed) console.log('delayed by', levels.delay_secs, 's');

Python

import json, time, urllib.request

url = "https://volstrata.com/embed/levels.json?ticker=SPX"
req = urllib.request.Request(url, headers={
    # Name your project. A bare default library user-agent
    # can be refused at the edge with a 403.
    "User-Agent": "my-project/1.0 (+https://example.com)",
})
lv = json.load(urllib.request.urlopen(req))

print(lv["ticker"], lv["call_wall"], lv["put_wall"],
      lv["flip"], lv["max_pain"])

if lv["as_of_epoch"]:                      # can be null — check it
    age = int(time.time() - lv["as_of_epoch"])
    print(f"data is {age}s old ({lv['as_of_label']})")
if lv["delayed"]:
    print(f"held back by at least {lv['delay_secs']}s")

Other ways to use the same levels