How live is “live”
This is a deliberate, documented property, not a defect being hidden. When the model gains a push path the interval goes to zero andpoll_interval_s will
say so — your client needs no change.
Connect
Authentication
The token goes in anAuthorization: Bearer header, or as ?token= for
clients that cannot set headers — which includes every browser, because the
WebSocket and EventSource constructors take a URL and nothing else.
The header is checked first and always wins. Use it wherever you can.
Concurrent connection limits
Counted per credential, not per tier — the hundred-and-first Pro customer is never refused because of a stranger.
Over the limit, the connection is refused with 429 before the WebSocket
handshake completes, and the body says which limit you hit:
scope of "instance" rather than a tier name means the server is full,
not your plan. Retry; it is not a billing problem.
Filtering by chain
Set the initial filter in the query string, on either transport:subscribed event carrying the resolved filter.
Chain names not scored by this service are reported, not silently dropped:
SSE is one-way. Its filter is fixed for the life of the connection; reconnect
with a different
?chains= to change it, or use /ws/flags.Event types
Every event carriesseq, type and ts. Chain-scoped events also carry
chain.
rug_flag
first_seen: false means an existing flag was re-scored. A wallet moving
from 62 to 91 is usually the event you most want, so re-scores are published
rather than deduplicated away.
top_evidence is the single top factor, not the full attribution. For the
complete explanation read /api/token/{address}/rug-score, where it is
authoritative.
Resuming without a gap
Every event carries a monotonicseq. Reconnect with the last one you saw:
EventSource does this for you — it sends Last-Event-ID, which the server
treats identically.
The replay window is bounded. If you ask for a range that has already been
discarded, welcome carries a gap:
Backpressure
Each connection buffers 1,000 events. If you stop reading, the oldest are dropped — you get the current state of the world rather than a backlog you will never catch up on — and you are told exactly what you lost:lag event arrives before the event on the far side of the gap, so a
jump in seq is never ambiguous. One slow subscriber costs the others nothing.
Heartbeats
Every 30 seconds of silence you get aheartbeat event, and on WebSocket a
protocol-level ping as well. Both, because the JSON one is what your
application can act on and the ping is what stops an intermediary reaping an
idle connection.
If you see neither for 90 seconds, the connection is dead — reconnect with
?since=.
Why the feed may be empty
Reaching /ws/flags over plain HTTP
You get a 426 with the URL you actually wanted, rather than a bare error:
Known limitations
- Single instance. The event bus is in one process’s memory. The service runs one replica for this reason. Scaling out requires a shared log first — see ADR 0001 in the repository.
HEADanswers 501 on the deployed service — on every route, including/health. That handler defines no HEAD method and never has. UseGET.- The feed is only as populated as its source. Production currently serves
no_snapshoton the flag table because its database credentials are unset, so the stream connects, authenticates, heartbeats, and reportsunavailable_reasonrather than delivering flags. Reported honestly rather than as a quiet day. - No
permessage-deflate. Payloads are small JSON objects; the extension is opt-in and declining it is conformant.
Scienter publishes impersonal market signals of general and regular circulation. Nothing here is investment, legal, or tax advice, or a recommendation to buy, sell, or hold any asset. Scienter is not a registered investment adviser or broker-dealer. Trading digital assets can result in the total loss of your funds — see Disclaimers for the full text.