Lavender API¶
Your Greeks are only as good as your model. The Lavender native API gives you a second, independently computed set — dividend-aware, early-exercise-aware, with calibrated forward prices — that you can run as your primary source or alongside an existing provider. It's the richest way to access Gateway's Greeks: extended Greeks through third order, quality metadata, per-expiry chain data, and term structure support, in a clean, flat response format.
Whether you're starting fresh or migrating, this is the canonical protocol. The vendor compatibility layers let you swap a vendor's host with a one-line change and adopt the Lavender API at your own pace.
Want to verify these numbers yourself?
Every Greek returned by this endpoint is reproducible from first principles — see Verify the Greeks for the Black-76 derivation and a drop-in Python/R/Excel reference implementation.
Try It in Your Browser¶
Once Gateway is running, paste this into any browser:
The at-the-forward call and put for every SPY expiry, rendered as an HTML table. Change the symbol or drop the center=atf filter to widen the view. No API key, no code, no setup.
Sample Response¶
The values shown below are illustrative -- a snapshot from the moment this page was written. A live request returns whatever the market is doing right now, so und_price, the Greeks, and theo will be different. The shape and field set are stable.
GET /l1/greeks?root=SPY&right=call&min_strike=570&max_strike=570&min_exp=2026-12-18&max_exp=2026-12-18&format=json
[
{
"underlying": "SPY",
"root": "SPY",
"expiry": "2026-12-18",
"strike": 570,
"right": "call",
"osym": "SPY 261218C00570000",
"und_price": 632.16,
"delta": 0.7335,
"gamma": 0.0022,
"vega": 1.7270,
"theta": -0.1147,
"decay": -0.1495,
"rho": 2.6532,
"theo": 95.94,
"vol": 0.2659,
"greeks": "core",
"ts": "2026-05-12T13:04:02.595"
}
]
The core field group (default) includes delta, gamma, vega, theta, rho, plus Lavender-specific fields: decay (next-trading-day P&L), theo (model price), and vol (calibrated implied volatility). Add greeks=all for extended Greeks through third order.
Endpoint¶
Primary endpoint. No authentication required. Runs on localhost:2112.
Returns per-contract Greeks for all options on an underlying, with optional filtering, field selection, and term structure modes.
A slimmer companion endpoint, /l1/chains, returns just the per-expiry chain metadata (forward, rates, dividends, borrow, settlement) with one row per (root, expiry). Use it when you need term-structure inputs but not strikes.
Output Formats¶
Every request accepts a format parameter:
| Format | Value | Content-Type | Notes |
|---|---|---|---|
| JSON | format=json |
application/json |
Array of objects. Default. |
| CSV | format=csv |
text/csv |
Header row + data rows. Ideal for pandas / R. |
| NDJSON | format=ndjson |
application/x-ndjson |
One JSON object per line. Good for streaming. |
| HTML | format=html |
text/html |
Rendered table. Paste the URL in a browser. |
Browser-first onboarding
Append ?format=html (or &format=html) to any request to see the output as a rendered table — no code needed. Great for quick inspection, spot-checking Greeks, or sharing a link with a colleague.
Identity Fields¶
Every response row includes identity fields that describe which contract the row belongs to.
Every response row includes both structural fields and the OSI symbol:
| Field | Example | Description |
|---|---|---|
underlying |
SPY |
Underlying symbol |
root |
SPY |
Option root |
expiry |
2026-06-19 |
Expiration date |
strike |
550 |
Strike price |
right |
call |
call or put |
osym |
SPY 260619C00550000 |
Standard OSI contract symbol (21 chars, space-padded root) |
Field Groups¶
Control which fields appear in the response with the greeks parameter. Default is core. Use greeks=all for everything, or combine groups with commas (e.g., greeks=core,chain).
Every response row carries two metadata columns appended after the real data: greeks (echo of which field groups are active) and ts (the wall-clock reprice timestamp). ts is always rightmost — pinned in a known position regardless of which field groups you select — so a client can pick it off without knowing which Greeks columns came back. See Response metadata in the field reference for details.
Which group do I need?
Most traders need core — delta, gamma, vega, theta, rho, and the model price. This is the default.
Add extra if you trade vol (vanna, volga, charm, veta) or want bid/ask IV.
Add exotic for third-order Greeks (speed, zomma, color, ultima) — rarely needed outside market-making.
Add chain for per-expiry data (forward price, rates, dividends, settlement type).
core — Essential per-strike Greeks¶
| Field | Description |
|---|---|
und_price |
Current underlying price |
delta |
\(\partial V / \partial S\) — per $1 spot move |
gamma |
\(\partial^2 V / \partial S^2\) — per $1 spot move |
vega |
\(\partial V / \partial \sigma\) — per 1% vol move |
theta |
\(\partial V / \partial t\) — per calendar day |
decay |
Expected price change to same time on next trading day |
rho |
\(\partial V / \partial r\) — per 1% rate move |
theo |
Model theoretical value |
vol |
Surface-fitted implied volatility (decimal: 0.25 = 25%) |
extra — Cross-Greeks and vol data¶
| Field | Description |
|---|---|
vanna |
\(\partial^2 V / \partial S\,\partial \sigma\) — delta sensitivity to vol |
volga |
\(\partial^2 V / \partial \sigma^2\) — vega sensitivity to vol (also known as vomma) |
charm |
\(\partial^2 V / \partial S\,\partial t\) — delta decay over time (annualized) |
veta |
\(\partial^2 V / \partial \sigma\,\partial t\) — vega decay over time (annualized) |
bid_vol |
Implied volatility at bid price |
ask_vol |
Implied volatility at ask price |
prev_vol |
Prior session's implied volatility |
confidence |
Vol surface quality metric |
exotic — Niche and third-order Greeks¶
| Field | Description |
|---|---|
speed |
\(\partial^3 V / \partial S^3\) — gamma sensitivity to spot |
zomma |
\(\partial^3 V / \partial S^2\,\partial \sigma\) — gamma sensitivity to vol |
color |
\(\partial^3 V / \partial S^2\,\partial t\) — gamma decay over time (annualized) |
ultima |
\(\partial^3 V / \partial \sigma^3\) — volga sensitivity to vol |
vera |
\(\partial^2 V / \partial \sigma\,\partial r\) — cross-sensitivity of vol and rates |
lambda |
\(\Delta \times S \,/\, V\) — leverage ratio (% option move per % spot move). Also known as omega (\(\Omega\)). |
epsilon |
\(\partial V / \partial q\) — per 1% dividend yield move |
chain — Per-expiry constants¶
| Field | Description |
|---|---|
forward |
Calibrated forward price for this expiry |
ex_style |
american or european |
settle |
am or pm settlement |
days |
Fractional calendar days to settlement (e.g., 31.83 means 31 days and ~20 hours). Accounts for AM vs PM settlement. |
t |
Variance-weighted time to expiry (years). Use for σ√t in Black-76 d1/d2. |
t_disc |
Calendar time to expiry (years). Use for discounting: DF = exp(−r × t_disc). |
rate |
Risk-free rate (annualized, continuously compounded) |
borrow |
Implied stock borrow rate (annualized, continuously compounded) |
divs |
Cumulative dividends to expiry (dollars) |
Query Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
underlying |
string | — | All roots for an underlying (e.g., SPX returns both SPX and SPXW). Use this for typical queries. |
root |
string | — | A specific option root (e.g., SPXW for just SPX weeklies). Use when you need to filter to one root within a multi-root underlying. One of underlying or root is required. Alias: symbol. |
format |
string | json |
json, csv, ndjson, or html |
exp |
date or * |
* |
Exact expiration, comma-separated list, or * for all. Accepts yyyy-MM-dd, yyyyMMdd, or yyMMdd. |
right |
string | both |
call, put, or both. Alias: type. |
strike |
decimal | — | Exact strike match. Comma-separated for multi-strike (e.g., strike=570,580,590). |
min_strike |
decimal | — | Strike floor |
max_strike |
decimal | — | Strike ceiling |
min_exp |
date | — | Expiration floor |
max_exp |
date | — | Expiration ceiling |
dte |
int | — | Days-to-expiry (nearest match). For each value, picks the available expiry whose calendar distance to today + N is smallest. Comma-separated for multi-DTE (e.g., dte=7,30,60). Per-root: with multi-root queries, each root contributes its own nearest expiry. |
min_dte |
int | — | Minimum days to settlement |
max_dte |
int | — | Maximum days to settlement |
min_delta |
decimal | — | Lower bound on signed delta (−1 to 1). Calls are positive, puts are negative. Use min_delta=0.3&max_delta=0.6 for ITM-ish calls, min_delta=-0.6&max_delta=-0.3 for ITM-ish puts. |
max_delta |
decimal | — | Upper bound on signed delta (−1 to 1). |
moneyness |
string | — | itm, otm, or atm. See moneyness section below. |
greeks |
string | core |
Comma-separated list of field groups (core, extra, exotic, chain, all) and/or individual field names. See "Field selection" below. |
center |
string | — | atm (at-the-money) or atf (at-the-forward) — anchors per expiry |
strike_count |
int | 1 |
Number of closest strikes to center, per expiry |
sort |
string | — | Sort key, ascending. One of: delta, call_delta, gamma, vega, theta, vol, strike, expiry. Overrides the default sort order. |
sortdesc |
string | — | Same as sort but descending |
limit |
int | — | Cap the number of returned rows. Applied after sorting. |
osym |
string | — | Request specific contracts by OSI symbol (comma-separated). Accepts padded (AAPL 260321C00400000) or compact (AAPL260321C00400000) forms. When set, root/underlying are optional. |
Parameter details
underlyingandrootaccept comma-separated lists (e.g.,underlying=SPX,NDXorroot=SPX,SPXW).underlyingexpands to all roots for each underlying —underlying=SPXreturns both SPX and SPXW options.rootreturns only the exact root(s) specified.centermust beatmoratf— unrecognized values return 400.sort/sortdescoverride the default underlying → expiry → strike ordering.call_deltanormalizes put delta to1 + deltaso calls and puts sort on the same scale.- Unknown parameters return a 400 error with a descriptive message. This catches typos like
?righ=call. - All numeric values are rounded to 6 decimal places.
Field selection (greeks=)¶
Mix group names and individual field names freely. Output column order is fixed (core → extra → exotic → chain) regardless of the order tokens appear in the request.
| Example | Returns |
|---|---|
greeks=core |
All core fields (default) |
greeks=delta,vega |
Only those two |
greeks=core,vanna,volga |
Core plus those two extras |
greeks=all |
Every field across all groups |
greeks=core,chain |
Core fields plus chain pricing inputs |
Unknown field names return a 400 error (e.g., Unknown field 'vomma' in greeks=) — this catches typos and reminds you of canonical names. Identity fields (underlying, root, expiry, strike, right, osym, greeks) are always included.
Moneyness (moneyness=)¶
| Value | Definition |
|---|---|
itm |
Strikes where the option has intrinsic value relative to the forward. Calls: K ≤ F. Puts: K ≥ F. The single ATM strike falls into itm. |
otm |
The other side. Calls: K > F. Puts: K < F. |
atm |
The single strike closest to the forward, returned per (root, expiry) pair. With right=both (the default) you get both the call and the put at that strike — so two rows per (root, expiry). With right=call or right=put you get one row per (root, expiry). |
itm and otm partition the chain at the forward. atm is independent and always returns exactly one strike per (root, expiry) — even on multi-underlying or multi-root queries.
Sort Order¶
Results are sorted deterministically:
- Underlying — in the order specified by the caller
- Expiration — ascending
- Settlement — AM before PM
- Root — alphabetical
- Strike — ascending
- Right — call before put
Term Structure¶
Use center=atm or center=atf to select the N closest strikes to an anchor per expiry. Combined with strike_count=1 (the default) and right=call, this produces one row per expiration — a term structure view.
http://localhost:2112/l1/greeks?root=AAPL¢er=atf&right=call&format=html
http://localhost:2112/l1/greeks?root=AAPL¢er=atm&right=call&format=html
http://localhost:2112/l1/greeks?root=AAPL¢er=atf&strike_count=5&format=html
atmanchors to the nearest strike to the current underlying priceatfanchors to the nearest strike to the calibrated forward price for each expiry- Numeric values (e.g.,
center=25) anchor to the nearest strike by call delta — see Example Requests
All Greeks are per-share
Multiply by 100 (or the contract multiplier) to get per-contract values. This applies to all endpoints — native and vendor compatibility.
List available expirations
Use center=atm&strike_count=1&right=call to get one row per expiration — a lightweight expiry discovery query:
Example Requests¶
Every example is a complete URL — paste it in a browser or curl it.
http://localhost:2112/l1/greeks?root=AAPL : All AAPL Greeks — core fields, all expirations
http://localhost:2112/l1/greeks?root=AAPL&right=call&max_dte=30 : Just calls, near-term
http://localhost:2112/l1/greeks?root=AAPL&right=call&dte=7,30,90 : Calls at the expiries nearest to 7, 30, and 90 days from today (one expiry per requested DTE)
http://localhost:2112/l1/greeks?root=AAPL&strike=170,175,180&right=call : Specific strikes only
http://localhost:2112/l1/greeks?root=AAPL&moneyness=otm&right=call : Out-of-the-money calls only
http://localhost:2112/l1/greeks?root=AAPL&sortdesc=vega&limit=10 : Top 10 contracts by vega
http://localhost:2112/l1/greeks?root=AAPL¢er=atf&right=call : Term structure — one row per expiry at the forward
http://localhost:2112/l1/greeks?root=AAPL¢er=25&right=call : 25-delta call skew across the term
http://localhost:2112/l1/greeks?root=AAPL¢er=atf&right=call&greeks=chain : Term structure with carry data (forward, rates, divs)
http://localhost:2112/l1/greeks?underlying=SPX
: SPX + SPXW options together — underlying= returns every root for that underlying
http://localhost:2112/l1/greeks?root=SPX
: Just the PM-settled SPX monthlies — root= is the more specific selector
http://localhost:2112/l1/greeks?root=SPXW : Just the SPX weeklies
http://localhost:2112/l1/greeks?root=SPX,SPXW
: Same as underlying=SPX, listed explicitly
http://localhost:2112/l1/greeks?root=AAPL&greeks=all : Every field — core, extra, exotic, chain
http://localhost:2112/l1/greeks?root=AAPL&format=html : Paste in browser — instant Greeks table
Code Examples¶
import pandas as pd
url = "http://localhost:2112/l1/greeks"
df = pd.read_csv(f"{url}?root=SPY¢er=atf&format=csv")
# Near-term ATM calls
near = df[(df["right"] == "call") & (df["delta"].abs() > 0.4) & (df["delta"].abs() < 0.6)]
print(near[["expiry", "strike", "delta", "vega", "theta", "decay", "vol"]])
using var client = new HttpClient();
var json = await client.GetStringAsync(
"http://localhost:2112/l1/greeks?root=SPY¢er=atf&format=json");
using var doc = JsonDocument.Parse(json);
foreach (var row in doc.RootElement.EnumerateArray().Take(5))
{
Console.WriteLine($"{row.GetProperty("expiry")} " +
$"{row.GetProperty("strike")} {row.GetProperty("right")}: " +
$"delta={row.GetProperty("delta")}");
}
#include <cpr/cpr.h>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
auto r = cpr::Get(cpr::Url{
"http://localhost:2112/l1/greeks?root=SPY¢er=atf&format=json"});
auto data = json::parse(r.text);
for (auto& row : data | std::views::take(5))
std::cout << row["expiry"] << " " << row["strike"]
<< " delta=" << row["delta"] << "\n";
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
.uri(URI.create(
"http://localhost:2112/l1/greeks?root=SPY¢er=atf&format=json"))
.build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
var array = new JSONArray(response.body());
for (int i = 0; i < Math.min(5, array.length()); i++) {
var row = array.getJSONObject(i);
System.out.printf("%s %.0f %s: delta=%.3f%n",
row.getString("expiry"), row.getDouble("strike"),
row.getString("right"), row.getDouble("delta"));
}
using CSV, DataFrames, HTTP
resp = HTTP.get(
"http://localhost:2112/l1/greeks?root=SPY¢er=atf&format=csv")
df = CSV.read(IOBuffer(resp.body), DataFrame)
# Near-term ATM calls
filter(r -> r.right == "call" && abs(r.delta) > 0.4, df) |>
x -> select(x, :expiry, :strike, :delta, :vega, :theta, :decay, :vol) |>
x -> first(x, 10)
How It Differs from Vendor Compatibility Layers¶
| Lavender API | Vendor Compatibility | |
|---|---|---|
| Field names | Lavender native (vol, und_price, decay, …) |
Vendor-specific (implied_vol, underlying_price, …) |
| Field selection | greeks= groups (core, extra, exotic, chain) |
Fixed per vendor |
| Filters | Strike/DTE/delta/moneyness + sort/limit + term structure | Vendor-specific subset |
| Term structure | center=atm/atf + strike_count |
Not available |
| Market data fields | Not included — model outputs only | Zeroed for wire compatibility |
| Response shape | Flat array of objects | Vendor-specific envelope |
| Greek units | Per-1% vega/rho/epsilon, per-calendar-day theta | Converted to each vendor's convention |
The compatibility layers are for migrating existing code with zero changes. The Lavender API is for new integrations and for accessing the full feature set.
Error Handling¶
| Status | Condition | Example |
|---|---|---|
200 |
Success. Empty array [] means no contracts matched your filters — the root exists but no options match. |
?root=AAPL&exp=2026-07-04 (holiday, no expiry) |
400 |
Invalid or missing parameters. The response body describes the error. | Missing root or underlying, Invalid right, Unknown parameter 'righ' |
502 |
Upstream data unavailable -- the gateway could not fetch Greeks from the data engine. | See When you see a 502 below. |
Unknown parameters return 400 with the parameter name — this catches typos like ?righ=call before they silently produce wrong results.
When you see a 502¶
A 502 means Gateway received your request but couldn't deliver a Greeks response. The three common causes:
| Cause | How long it lasts | Signal |
|---|---|---|
| Housekeeping-window restart | A few seconds, nightly 03:00-04:00 ET (details) | Daily cadence; outside that hour Gateway runs without interruption |
| Upstream feed transient | Seconds to a minute | Persists across retries within a short window; recovers on its own |
| Entitlement / subscription check failed | Until the subscription state is resolved | Gateway console logs the entitlement message (details) |
Recommended client behavior:
- Retry 502s with exponential backoff and jitter -- e.g. 250ms, 500ms, 1s, 2s, capped at 30s, max 5 attempts.
- Do not retry 4xx responses. They are not transient; the client request is wrong.
- A 502 that persists for more than ~1 minute outside the nightly 03:00-04:00 ET housekeeping window almost always indicates an entitlement issue -- check Gateway's console output rather than retrying further.
Compat layers (Polygon.io, ThetaData, Alpaca, etc.) emit 502 for the same conditions; the response body matches the upstream vendor's error envelope.
Rate Limits¶
There are no rate limits. Gateway runs on localhost and serves requests as fast as your machine can handle them. Typical latency is 5–50ms for a single root, 100–200ms for large chains (SPY, SPX).
Coverage¶
Lavender covers all OPRA-listed US options — equities, ETFs, and index options including SPX, NDX, RUT, VIX, and their weekly variants. Approximately 6,000 underlying names with all listed expirations and strikes.
Data Freshness¶
Greeks are repriced continuously against the market during market hours (9:30 AM – 4:00 PM ET). Outside market hours, Greeks reflect the last computed values from the prior session.
Excel Integration¶
Bulk fetch + VLOOKUP
Don't make one HTTP call per cell — Excel will freeze. Instead, use Power Query to import the full chain as a table, then VLOOKUP into it:
- Data → From Web → paste
http://localhost:2112/l1/greeks?root=AAPL¢er=atf&format=csv - Power Query imports all Greeks as a table
- Use
VLOOKUPorINDEX/MATCHagainst the table to pull individual values into your spreadsheet - Data → Refresh All to update Greeks
For multiple underlyings, make one query per underlying (not per contract). Each query returns the full chain in ~50ms.
Per-expiry chain metadata: /l1/chains¶
Returns the per-expiry pricing inputs (underlying price, forward, rate, borrow, dividends, variance time, settlement type) as one row per (root, expiry) — no strikes, no Greeks. Same fields as the chain field group on /l1/greeks, just flattened so you don't have to dedupe across strikes — plus a chain-only num_strikes column (the count of distinct listed strikes for that (root, expiry)).
Use it when you need term-structure inputs — building a forward curve, calibrating a model, or staging discounting factors — without paying for per-contract Greeks you'll throw away.
Parameters¶
| Parameter | Type | Required | Description |
|---|---|---|---|
root |
list<string> | one of {root, underlying} |
OCC root(s). Comma-separated for multiple. |
underlying |
list<string> | one of {root, underlying} |
Underlying ticker(s); expands to all matching roots (e.g. underlying=SPX returns SPX and SPXW). |
format |
string | no | json (default), csv, ndjson, or html. |
Output columns¶
underlying, root, expiry, und_price, forward, ex_style, settle, days, num_strikes, t, t_disc, rate, borrow, divs, ts
Same units and semantics as the chain field group — see the Field Reference. ts is the reprice wall-clock stamp (Response metadata), pinned rightmost.
Examples¶
Sample response¶
[
{
"underlying": "SPY",
"root": "SPY",
"expiry": "2026-06-19",
"und_price": 632.16,
"forward": 631.38,
"ex_style": "american",
"settle": "pm",
"days": 36.0,
"num_strikes": 287,
"t": 0.098630,
"t_disc": 0.098630,
"rate": 0.0432,
"borrow": 0.0151,
"divs": 1.49,
"ts": "2026-05-12T13:04:02.595"
}
]
Service liveness: /l1/health¶
A blended view of both layers in one round trip -- the Gateway running on your machine and the Lavender cloud it talks to. Use it as the canonical monitoring / liveness probe: startup readiness, retry-loop sanity, dashboard healthchecks.
Parameters¶
| Parameter | Type | Required | Description |
|---|---|---|---|
format |
string | no | json (default), csv, ndjson, or html. |
Output columns¶
Fields are emitted in the order shown below. time is always last.
| Field | Type | Description |
|---|---|---|
status |
string | Roll-up: "ok" iff both gw_status and lav_status are "ok", otherwise "degraded". |
gw_status |
string | Gateway status. Always "ok" when this endpoint returns 200 (a Gateway that can't respond is its own unhealthy signal). |
lav_status |
string | Lavender cloud status. "ok", "unreachable", or an error string. |
gw_version |
string | Gateway version (e.g. 26.5.13.2). |
lav_version |
string | Lavender cloud version. "unknown" when lav_status is not "ok". |
gw_uptime |
string | Gateway uptime, e.g. 5h 2m. |
lav_uptime |
string | Lavender cloud uptime. "unknown" when lav_status is not "ok". |
gw_memory_mb |
int | Gateway memory footprint, MB. |
lav_memory_mb |
int | Lavender cloud memory footprint, MB. 0 when lav_status is not "ok". |
time |
string | Current ET wall clock, yyyy-MM-dd HH:mm:ss. |
Sample response -- healthy¶
{
"status": "ok",
"gw_status": "ok",
"lav_status": "ok",
"gw_version": "26.5.13.2",
"lav_version": "26.5.13.2",
"gw_uptime": "5h 2m",
"lav_uptime": "12h 18m",
"gw_memory_mb": 142,
"lav_memory_mb": 1837,
"time": "2026-05-14 10:03:11"
}
Sample response -- degraded (cloud unreachable)¶
{
"status": "degraded",
"gw_status": "ok",
"lav_status": "unreachable",
"gw_version": "26.5.13.2",
"lav_version": "unknown",
"gw_uptime": "5h 2m",
"lav_uptime": "unknown",
"gw_memory_mb": 142,
"lav_memory_mb": 0,
"time": "2026-05-14 10:03:11"
}
A 200 with top-level status: "ok" is the green-light contract. A 200 with status: "degraded" means the Gateway is up but something on the Lavender cloud side needs attention -- inspect lav_status for the specific cause. Anything else (connection refused, 5xx) means the Gateway is not currently serving -- typically a brief mid-restart during the nightly 03:00-04:00 ET housekeeping window; retry after a few seconds.
See also¶
- Field Reference — every L1 field with units, source, and example values
- Greek Conventions — sign conventions, units, and the full extended Greeks catalog
- Verify the Greeks — derive each Greek from first principles and check against the API