API reference
Signal feed
Coverage tiers, response fields, and signal history.
Every row is one indicator changing its mind about one asset on one closed candle. The compute job runs hourly at :03 UTC — 1h readings refresh every hour, 4h on 4-hour boundaries, 1d at 00:00 UTC — so a universe-tier change is logged within ~3-5 minutes of the close that produced it. Rows are append-only: never edited, never selectively removed, they only age out (universe tier 365 days, alert tier 90). The history you evaluate against is the history that actually fired.
Two tiers share one schema, so you never branch on the source. Universe covers all 251 assets × 3 timeframes and logs changes for 6 indicators; its current state also carries PerpKit’s trend reading. Alerts is the narrower set our subscribers receive — the full strategy engines, including PerpKit, with explicit entry and exit actions. Pass tier=universe|alerts|all.
| Field | Type | Meaning |
|---|---|---|
id | string | Stable identifier — safe to dedupe on |
tier | enum | "universe" · "alerts" — which pipeline produced the row |
ticker | string | Asset symbol, e.g. "BTC" |
asset_class | enum | "crypto" · "stock" · "metal" |
indicator | string | Stable id, e.g. "trend_oracle" |
indicator_name | string | Human-readable name, e.g. "Trend Oracle" |
timeframe | enum | "1h" · "4h" · "1d" |
direction | enum | "bullish" · "bearish" · "neutral" · "exit" — normalized across both tiers |
transition | object | null | {from, to} on the universe tier — the state change itself |
change_type | enum | null | "reversal" (bullish ↔ bearish) · "activation" (neutral → a direction) · "deactivation" (a direction → neutral) on the universe tier; null on the alerts tier |
action | string | null | LONG · SHORT · CLOSE on the alerts tier. CLOSE does not say which side it closes; BUY · SELL are legacy values |
price | number | null | Asset price when the signal fired |
created_at | ISO 8601 | When Eclipse wrote the row, not when the signal fired (see timing below) |
Timing and known limitations
created_at is when Eclipse wrote the row, not when the signal fired. Universe-tier rows are written by the hourly job a few minutes after the candle that produced them closes; if an asset’s candles had not updated in time, its change is written at that timeframe’s next run instead. Alerts-tier rows are written when the alert job publishes them, and alerts published in the same run carry created_at values milliseconds apart even when they fired on different candles.
Alerts tier only: the same action for the same ticker, indicator and timeframe is published at most once per 20 candles of that timeframe (80 hours on 4h, 20 days on 1d). An alert held back by this rule is published later, when its window ends, or never if another alert for that ticker, indicator and timeframe is published first. The universe tier has no such rule and logs every change.
CLOSE ends the strategy’s open position but does not say whether that position was long or short. Because of the alerts-tier cooldown, the entry it closes may be missing from the feed or published late. BUY and SELL are legacy values; the current alert engines publish LONG, SHORT and CLOSE.
Every change to the API is listed, dated, in the changelog field of the manifest.
# The whole board in one call — every indicator, every asset, every timeframe
curl https://www.eclipse.trade/api/v1/state \
-H "Authorization: Bearer ek_YOUR_KEY"
# Only what changed since your cursor (poll this every 15 min)
curl "https://www.eclipse.trade/api/v1/signals?since=2026-08-01T12:00:00Z" \
-H "Authorization: Bearer ek_YOUR_KEY"
# Where 4+ different indicators currently agree, bullish, 4h
curl "https://www.eclipse.trade/api/v1/confluence?timeframe=4h&min_agree=4" \
-H "Authorization: Bearer ek_YOUR_KEY"{
"signals": [
{
"id": "…",
"tier": "universe",
"ticker": "LINK",
"asset_class": "crypto",
"indicator": "trend_oracle",
"indicator_name": "Trend Oracle",
"timeframe": "4h",
"direction": "bullish",
"transition": { "from": "neutral", "to": "bullish" },
"change_type": "activation",
"price": 11.42,
"created_at": "2026-08-01T12:03:11.412Z"
}
],
"pagination": { "limit": 50, "offset": 0, "returned": 1,
"has_more": false, "next_cursor": "2026-08-01T12:03:11.412Z" }
}Pagination and polling
Results are newest first. next_cursor is the newest timestamp on the page, not a link to the next page of older events. Advancing since immediately can skip unread events.
For a fixed backlog, keep since and all filters unchanged while increasing offset by limit. Consume every page before saving the newest timestamp from the first page as your next polling watermark. Deduplicate stored events by tier and id.
Offset pagination is not a frozen snapshot: new inserts can shift pages, and an exclusive timestamp can miss late-arriving events at the same time. Use overlapping polling windows and deduplication, and reconcile current readings with /api/v1/state. This interface does not guarantee lossless event replay.