Unusual options·Developer / quant··8 min read

Why an unusual options activity webhook is not just another price alert

Broker and charting webhooks fire when a price crosses a line you drew. An unusual options activity webhook fires on a contract-level signal that has already been filtered, deduplicated and sized. Here is what that changes for anything you automate.

Most webhooks in trading are condition alerts. You draw a line on a chart or type a rule into a broker, and when price crosses it the platform posts a message to a URL. Useful, but the information content is exactly one bit: the thing you predicted happened. A webhook for unusual options activity is a different animal. It does not fire on what price did. It fires on what other participants did, in size, in a specific contract, and it hands you the contract.

That difference sounds philosophical until you try to automate on top of both. Then it becomes very practical, because the two kinds of event need different plumbing. This post walks through the properties of the OptionsBell webhook that make it behave unlike a price alert, and why each one was designed that way.

The unit is a contract, not a tick

A price alert re-fires. Price crosses 200, you get a message; it dips and crosses again, you get another. That is fine for a human and terrible for a script, which now needs its own memory to avoid acting twice. The OptionsBell webhook delivers each contract once. The key is option_symbol, a string like NVDA|20261016|215.00C that identifies the exact strike, expiry and side. When a print first crosses the unusual threshold on one of your tickers, you get it. If it stays unusual for the next four sessions, which large positions often do, you do not get it again.

The state that makes this work lives on our side, per webhook. The processor remembers which contracts you have been sent and diffs each five-minute scan against that memory. You still should de-duplicate on option_symbol if you retry deliveries yourself, but you never have to build the memory from scratch.

The filter runs before the POST

A raw options feed is a firehose: every trade, every contract, every second. Products that expose one leave the filtering to you, plus the reconnect logic, plus the buffering when your consumer falls behind. An OptionsBell event contains only contracts that already passed two gates. The first is the base floor that defines unusual on the platform: Vol/OI of at least 1.5, open interest of at least 100 and an estimated premium of at least $25,000. The second is whatever you added when you registered the webhook:

CriterionWhat it doesTypical use
typeCalls only or puts onlyA bullish-only screen, or a hedging monitor on longs
min_premiumFloor on estimated dollars spentCut everything below institutional size
min_voloiFloor on volume over open interestKeep only prints that are new positioning, not turnover
max_dteCap on days to expirationNear-dated conviction versus long-dated hedges
min_ivFloor on implied volatilityNames where somebody is paying up for movement
min_volume, min_oiAbsolute liquidity floorsAvoid prints you could not trade around

The practical consequence: an event is small. On a quiet day a webhook with 40 tickers and a $250k premium floor may fire twice. Your receiver can be a 20-line function that does something meaningful with every single contract, instead of a stream consumer that throws away 99.9 percent of what it reads.

Batching is a feature

Price alerts fire instantly and individually because each one is independent. Flow arrives in clusters. When a desk works an order across three strikes, you want those three contracts in one event, not three messages twelve seconds apart. The webhook has a cooldown, notify_interval_min, between successful deliveries: 10 minutes by default, selectable up to a day. Contracts that surface during the cooldown are not dropped, they are batched into the next delivery. Each event also caps at 10 contracts per ticker, highest Vol/OI first, so a day when a single name goes wild does not turn into a 400-row payload.

Set the cooldown to match what the receiver does. A script that writes to a database can take events every 10 minutes. A message to yourself that you will read on a phone is better at 60 or 240.

The silent first run

Register a price alert and nothing happens until the condition is met. Register a flow webhook naively and the first evaluation would find dozens of contracts that are already unusual today and send them all at once. OptionsBell does not do that. The first run after creation records a baseline and delivers nothing. From the second run on, only contracts that were not in the baseline are sent. Adding tickers to an existing webhook does not reset the baseline, so a new ticker's current unusual contracts arrive with the next evaluation, which is what you would expect when you ask to watch something new.

Every contract carries its own context

A price alert tells you a level broke. A flow event tells you who did what, and the fields are chosen so that the receiver can decide without a second API call:

FieldThe question it answers
symbol_typeCall or put: direction of the bet, or the hedge
premium_estimateHow much money is behind it
volume_oi_ratioIs this a new position or existing interest turning over
days_to_expirationHow soon the trade needs to be right
volatilityWhat the buyer paid for movement, in IV terms
strike_price, expiration_dateWhere and when, for anything you want to look up or chart

That is enough for a receiver to route a $14M near-dated call sweep to one place and a $30k long-dated put to another, or to score and rank contracts before you ever look at them. The call-sweep digest we wrote for the REST API works unchanged on webhook events, minus the fetching.

Signed, idempotent, retried

Condition alerts from charting tools are often plain unsigned POSTs. If you automate anything that costs money on top of one, that is a problem, because a fake event is one curl away. OptionsBell signs every delivery with HMAC-SHA256 over the timestamp and the raw body using a per-webhook secret, and gives every delivery an id that stays constant across retries. Verify the signature, key your idempotency on the delivery id, and a spoofed or duplicated event has no effect. Failed deliveries are retried three times, then resent with the next five-minute run, and after 20 consecutive failures the webhook pauses itself rather than flooding a dead endpoint.

What it is not

Honesty about scope keeps automations out of trouble. The webhook is not tick-level and not an execution feed. The underlying data refreshes every 5 minutes during the session and deliveries typically land one to six minutes after a contract appears in our data. It fires only during the active window you set, in New York time, so nothing arrives overnight. It does not replay history; for a backfill after downtime or an audit trail, query the options flow API with the since parameter. And a contract in an event is information about what somebody did, not a recommendation about what you should do. Read the sweep piece before treating any single print as a trade.

Push and pull, side by side

The cleanest setups use both channels. The webhook is the push: it wakes your code up when a new contract appears and hands it the contract. The API is the pull: it answers questions the event raises, like what the stock has been printing for the last ten sessions or what the whole sector looked like today. Same data, same $24.99 plan, same key. The only thing the webhook changes is that you no longer have to ask on a timer to find out that something happened.

For the plain-English version of what a webhook is and how to receive one, see this explainer. For concrete receivers you can copy, see seven things to build on an options flow webhook. The full contract, including the signature scheme and every management endpoint, is in the API docs.