What the market did, and where your price has drifted
Two streams, deliberately kept apart. A fact-stream of reference main-line changes, typed and timestamped — detected at a median of one minute. And warnings when the gap between your own price and fair is widening, which in an end-to-end day arrived a median of 15.8 minutes before the market caught up.
A fact-stream and a warning stream
Mixing observations with inferences is how alerting systems lose credibility. These arrive on separate endpoints and are never blended into one score.
1 · Main-line changes — facts
Every time the reference market moves its main line, you get a typed,
timestamped event carrying the market, the old line and the new line. Pull the ladder from
GET /v1/surface?event_id= when you need
it. Nothing here is predicted; it either happened or it did not.
2 · Gap warnings — inferences
Your posted price is compared with fair from the surface. When the gap is widening, a warning opens — labelled as an inference, with the gap in percentage points and the band around fair so you can see how solid the reading is.
A gap, a band and an action
- Direction-free by design. A defensive action does not need a direction — raise margin, trim the limit, or reprice toward fair. We tell you where the risk is; we do not claim to know where the market is going.
- The band travels with the alert — except on the match result, which has no
ladder and therefore no band. A 3 pp gap on a rung with a 1.4 pp band is a different object
from the same gap on a rung with a 3 pp band, and the payload says which one you are looking
at. On a moneyline alert
band_pp,line,main_line,limitandpriorityare null together: sort and plot code has to handle that rather than assume a number.
Every alert is resolved, including the ones that fail
An alert is not a notification that disappears into a chat window. It is an object with states, and all of its states are published.
active — the warning is open
The gap is present and widening. Readable from
GET /v1/alerts.confirmed — the market moved as the gap implied
The alert closes as confirmed, with the timestamps of both the warning
and the move, so the lead time is computed from the record and not from memory.withdrawn — the gap closed without a move
The alert closes as withdrawn and stays in the record. Withdrawn
alerts are what make a published hit-rate meaningful: we cannot quietly drop the ones that
did not work out.Three more states exist and are published the same way:
revised (you re-quoted and the gap changed), corrected (you re-quoted
into fair and the warning is retired as answered) and expired — an open alert that
nobody re-quoted inside the alert TTL. expired is the most common terminal state
in a live journal, not an edge case: it means the conversation stopped, not that the gap was
resolved. The full set is in
the alert reference.
The lifecycle is committed to the same cryptographic ledger as the prices, so the set of alerts we raised on a given day is fixed on that day.
Stream or poll
| Channel | Call | Use it for | Rate class |
|---|---|---|---|
| Live stream | GET /v1/stream (SSE) | Main-line changes as they are detected | stream · connect rate, plus a separate cap on how many streams you hold open at once (stream_concurrent_max on GET /v1/usage) |
| Catch-up poll | GET /v1/line-events?since= | Replaying anything missed while you were down | events · 12/min |
| Open alerts | GET /v1/alerts | Current gap warnings and their state | alerts · 60/min |
curl -N "https://api.foreline.io/v1/stream" \
-H "X-Foreline-Key: $FORELINE_KEY"
: foreline sse | data_cadence_s=30
id: 1787080944.7407787
event: line_change
data: {"ts": 1787080944.7407787, "event_id": 1633790365, "market": "spread",
"old_line": 0.0, "new_line": 0.25}
: ping
A frame carries exactly the row
GET /v1/line-events returns — same field
names, same types — so one parser serves the stream and the catch-up poll. The field names
above are not illustrative: the change is old_line / new_line,
the instant is ts as epoch seconds, a JSON number, and
event_id is our integer id, never a string. The opening
: foreline sse comment and the : ping keepalives are comments, not
events — skip any line beginning with :.
Delivery is pull, not push. Hold GET /v1/stream for latency
and replay GET /v1/line-events?since= for completeness — together they cover every
case a webhook would, and neither needs an endpoint on your side that we can reach. There is no
push delivery and no URL to register; if you need one, tell us before you build against it.
Values above are illustrative; see the
API reference.
The other three products
Fair-Price Surface
The whole ladder, de-vigged and banded — the fair price every gap is measured against.
See the product →Player Radar
Scores accounts on CLV against our close — including on lines the reference never quoted.
See the product →Parlay Protection
A coherent score matrix, so joint outcomes are priced consistently with the marginals.
See the product →Request a trial of Line Radar
Tell us how you would like alerts delivered and which markets you post. We reply with a trial key.
We usually reply within one business day. Pricing is quoted per scope — ask and we will send terms.
Foreline