1BullBear Data API

Programmatic access to 1BullBear's market, macro & analytics data — REST endpoints plus real-time SSE streams, for building your own dashboards and models.

Base URL  https://api.1bb.me/v1   Auth  API key   Format  JSON   Realtime  SSE

Quick start

Pass your key in the x-api-key header (or Authorization: Bearer <key>):

REST — one shot

curl -s https://api.1bb.me/v1/gex/SPX/current \
  -H "x-api-key: YOUR_API_KEY"

SSE — live stream

curl -N https://api.1bb.me/v1/gex/stream \
  -H "x-api-key: YOUR_API_KEY"
Your API key is issued by the 1BullBear team and is permanent until revoked. Keep it secret — it identifies your account and counts against your rate limit. Treat it like a password; don't embed it in client-side/browser code.

Authentication

Every request must include a valid key. Send it as either:

HeaderValue
x-api-keyYOUR_API_KEY
AuthorizationBearer YOUR_API_KEY

A missing or invalid key returns 401. Your key is scoped to the data endpoints below; anything outside that scope returns 403.

Streams only: /v1/gex/stream and /v1/calendar/stream also accept the key as a query parameter — ?key=YOUR_API_KEY — because the browser EventSource API can't set custom headers. Prefer the headers everywhere else: query strings tend to end up in access logs and browser history. And never ship a key in client-side code you distribute to end users.

Rate limits

120 requests per minute per key. Every response carries:

HeaderMeaning
x-ratelimit-limitRequests allowed per minute (120).
x-ratelimit-remainingRequests left in the current minute.

Exceeding the limit returns 429. Cache responses where you can — most products update on a schedule (minutes to hours), not per-request.

An SSE stream counts as one request when the connection opens — a long-lived stream costs nothing more while it stays up, but each automatic EventSource reconnect counts again. Keep at most 2 concurrent stream connections per key.

Real-time streams (SSE)

Two Server-Sent Events streams push updates the moment the pipeline produces them — no polling. Standard SSE wire format:

retry: 5000

: ping                                  <- comment heartbeat, ~every 25s (ignore)

data: {"kind":"basis","instrument":"SPX","data":{...}}   <- one JSON event per data: line
SSE/v1/gex/stream

Live market-structure feed. Two event kinds fan out from the compute workers: kind="basis" — a 1-minute spot/basis tick per instrument (every 60s); kind="snapshot" — a full recomputed GEX snapshot (same payload as /v1/gex/{instrument}/current), session-aware: every 1 min 9:30–10:00 ET, every 5 min during RTH, every 15 min overnight/weekend.

Params — instrument = SPX | NDX (optional; omit = events for all instruments); key = your API key as a query param (streams only) — or send the x-api-key header

Example
Response (truncated)
data: {"kind":"basis","instrument":"SPX","data":{"instrument":"SPX","spot":7552.75,
  "compute_spot":7495.06,"basis":-57.69,"basis_source":"frozen","basis_age_sec":33060,
  "futures_contract":"CME_MINI:ES1!","staleness_warning":null,
  "snapped_at":"2026-07-02T05:10:09.623Z"}}

(one line on the wire, wrapped here for readability; kind="snapshot" events carry
 the full /v1/gex/{instrument}/current payload in place of the basis object)
SSE/v1/calendar/stream

Live economic-calendar feed. A database trigger fires on event INSERT and on any material UPDATE — a change to actual, actual_display, forecast, forecast_display, previous, revised, impact, ce_impact, release_at, or ce_enriched_at (no-op rewrites are suppressed). Each fire pushes the full updated row, so expect bursts at release times (actuals landing) plus occasional forecast revisions and enrichment updates.

Params — key = your API key as a query param (streams only) — or send the x-api-key header. No filters; filter client-side.

Example
Response (truncated)
data: {"cbf_uid":"6489d5b59a2596ec5c56dc98:400676","release_at":"2026-07-02T01:30:00.000Z",
  "country":"AU","currency":"AUD","event_name":"Balance of Trade","impact":"HIGH",
  "is_holiday":false,"actual":-3.018,"forecast":2.2,"previous":1.383,"revised":1.791,
  "actual_display":"A$-3.018B","forecast_display":"A$2.2B",
  "surprise_score":0.321,"surprise_basis":2.614,
  "ec_code":"AUBALG=ECI","mapping_method":"llm","mapping_confidence":78,
  "ce_min_threshold":1000,"ce_max_threshold":2500,"ce_notes":{"...":"..."},
  "ce_enriched_at":"2026-06-28T18:43:29.997Z"}

(one line on the wire, wrapped here; identical columns to a REST /v1/calendar row,
 so you can overlay pushed rows onto a polled set by cbf_uid)

Connecting

curl

curl -N "https://api.1bb.me/v1/gex/stream?instrument=SPX" \
  -H "x-api-key: YOUR_API_KEY"

Node

// npm install eventsource   (Node 18+, native fetch)
import { EventSource } from 'eventsource';

const es = new EventSource('https://api.1bb.me/v1/gex/stream?instrument=SPX', {
  fetch: (url, init) => fetch(url, {
    ...init,
    headers: { ...init.headers, 'x-api-key': process.env.API_KEY },
  }),
});

es.onmessage = (e) => {
  const msg = JSON.parse(e.data);
  if (msg.kind === 'basis')    console.log(msg.instrument, 'spot', msg.data.spot);
  if (msg.kind === 'snapshot') console.log(msg.instrument, 'new snapshot');
};

Browser

// Browser EventSource can't set headers — use the ?key= query param (streams only).
// Never ship a key in client-side code you distribute to end users; keep it behind
// your own server or proxy.
const es = new EventSource('https://api.1bb.me/v1/gex/stream?key=YOUR_API_KEY');
es.onmessage = (e) => render(JSON.parse(e.data));
Reconnects: EventSource reconnects automatically (the stream sends retry: 5000, so ~5s back-off). Events are not replayed — on every connect and reconnect, hydrate current state from the matching REST endpoint (/v1/gex/{instrument}/current or /v1/calendar), then apply stream events on top. The : ping comment every ~25s just keeps proxies from idling the connection; SSE clients ignore it natively.

Endpoints

All endpoints are GET and return JSON (the research PDF returns binary). Paths with {…} take a path parameter. Expand Example on any card for a real request + truncated response.

Market Structure (GEX)

GET/v1/gex/{instrument}/current

Full latest GEX snapshot for one index: spot, gamma/vol regime, all key levels (zero-gamma, walls, max pain, charm magnet, vol & expected-move bands), per-tenor expected moves, direction model, setup grade and playbook. Served from the hot cache, falling back to the newest DB snapshot.

Params — instrument (path) = SPX | NDX (case-insensitive; these are the only computed instruments — futures tickers ES/NQ are NOT aliased here and return 404)

Session-aware refresh: every 1 min in the 9:30–10:00 ET opening burst, 5 min during RTH, 15 min overnight/weekend. All prices are in FUTURES space (ES/NQ points) — GEX is computed on SPX/NDX index options then shifted by the live basis.

Example
Request
curl -s https://api.1bb.me/v1/gex/SPX/current -H "x-api-key: YOUR_API_KEY"
Response (truncated)
{
  "snapshot_id": "1626b86a-831f-4b96-aeea-c8c5d577aa23",
  "instrument": "SPX",
  "as_of": "2026-07-02T04:44:17.649Z",
  "computed_at": "2026-07-02T04:59:10.565Z",
  "spot": 7549.25,
  "regime": { "gamma": "POSITIVE", "vol": "EXPANDED", "cell": "POSITIVE_EXPANDED" },
  "grade": "A",
  "grade_score": 71.95,
  "direction": { "direction": "DRIFT_UP_MILD", "strength": 0.398, "reason": "spot 0.66×ATR above HVL", "signals": { "gexSkew": 0.265, "locationAtr": 0.656, "…": "…" } },
  "session": { "session": "ASIA", "label": "Asia", "cadenceMin": 15, "isWeekend": false },
  "levels": {
    "zgl": 7508.29, "net_gex_at_spot": 11565916756.49,
    "maxPain": 7512.69, "charm_magnet": 7607.69,
    "vol_band_high": 7635.29, "vol_band_low": 7460.09,
    "em_high": 7579.05, "em_low": 7491.45,
    "callWalls": [ { "strike": 7557.69, "gex": 3396673681.84, "oi": 12659, "hold_pct": 95, "break_pct": 5, "distance_pts": 8.44, "render_intensity": "full" }, "…" ],
    "putWalls": [ { "strike": 7507.69, "gex": 1907951407.32, "oi": 7428, "hold_pct": 76, "break_pct": 24, "…": "…" }, "…" ],
    "gammaFlips": [ "…" ]
  },
  "em": { "tenors": [ { "expiration": "2026-07-02", "em": 43.8, "em_high": 7593.05, "em_low": 7505.45, "straddle": 43.8, "atmStrike": 7547.69 }, "…" ] },
  "playbook": "…", "overlays": "…", "paths": "…", "wall_events": [ "…" ], "upcoming_events": [ "…" ], "diagnostics": "…"
}
GET/v1/gex/{instrument}/levels

Flattened, ranked level table from the single freshest snapshot — one row per level (call/put walls, gamma flips, zero-gamma, max pain, EM/vol band edges, charm). Ideal for plotting horizontal lines on a chart.

Params — instrument (path) = SPX | NDX. No query params. level_type values: CW (call wall), PW (put wall), GL (gamma flip), ZG (zero-gamma), MP (max pain), EH/EL (expected-move high/low), VH/VL (vol band high/low), CHARM (charm magnet). hold_pct/break_pct populated on walls only.

Pinned to one snapshot_id (the engine writes several snapshots per chain timestamp; this avoids duplicate stacked levels). NUMERIC columns (strike, distance_pts) serialize as strings. Strikes are futures-space prices.

Example
Request
curl -s https://api.1bb.me/v1/gex/SPX/levels -H "x-api-key: YOUR_API_KEY"
Response (truncated)
{
  "instrument": "SPX",
  "levels": [
    { "level_type": "CHARM", "rank": null, "strike": "7607.6900", "magnitude": 526555.68, "raw_magnitude": null, "mag_unit": "usd_millions", "oi": null, "distance_pts": null, "distance_pct": null, "render_intensity": null, "role": null, "side": null, "is_dominant": null, "k_low": null, "k_high": null, "hold_pct": null, "break_pct": null },
    { "level_type": "CW", "rank": 1, "strike": "7557.6900", "magnitude": 3396673681.84, "raw_magnitude": 339667368183.87, "oi": 12659, "distance_pts": "8.4400", "distance_pct": 0.00113, "render_intensity": "full", "role": "RESISTANCE", "side": "above", "hold_pct": 95, "break_pct": 5 },
    { "level_type": "CW", "rank": 2, "strike": "7607.6900", "magnitude": 2233429525.05, "oi": 9857, "role": "RESISTANCE", "hold_pct": 77, "break_pct": 23, "…": "…" },
    "…"
  ]
}
GET/v1/gex/{instrument}/history

Time series of headline snapshot values (spot, zero-gamma, net GEX at spot, max pain, charm, vol/EM bands, prior-day high/low, regime cells) — one point per compute timestamp, returned oldest-first.

Params — instrument (path) = SPX | NDX; limit = int, default 500, max 2000; from = ISO timestamp (as_of >=); to = ISO timestamp (as_of <=)

Collapses multiple recomputes per chain timestamp to the freshest one, so the series has exactly one point per timestamp. Array is ascending (oldest → newest); limit selects the N most recent points before reversing.

Example
Request
curl -s "https://api.1bb.me/v1/gex/SPX/history?limit=100&from=2026-07-01T00:00:00Z" -H "x-api-key: YOUR_API_KEY"
Response (truncated)
{
  "instrument": "SPX",
  "snapshots": [
    "…",
    { "as_of": "2026-07-02T04:29:16.151Z", "spot": "7542.5000", "zgl": "7507.6200", "net_gex_at_spot": 9447997570.85, "max_pain": "7512.6900", "charm_magnet": "7607.6900", "vol_band_high": "7633.9900", "vol_band_low": "7451.3900", "em_high": "7580.9000", "em_low": "7489.6000", "pdh": "7579.0000", "pdl": "7507.2500", "gamma_regime": "POSITIVE", "vol_regime": "NEUTRAL", "regime_cell": "POSITIVE_NEUTRAL" },
    { "as_of": "2026-07-02T04:44:17.649Z", "spot": "7549.2500", "zgl": "7508.2900", "net_gex_at_spot": 11565916756.49, "gamma_regime": "POSITIVE", "vol_regime": "EXPANDED", "regime_cell": "POSITIVE_EXPANDED", "…": "…" }
  ]
}
Notes & caveats — Market Structure (GEX)

Analytics

GET/v1/analytics/{report}/{symbol}

Pre-computed intraday statistical reports built from 1-minute futures data, refreshed daily. 19 report slugs: opening-range-breakout-standard, initial-balance-breakout-standard, intraday-timing-standard, opening-candle-continuation-standard, opening-range-breakout-by-time, power-hour-breakout-standard, gap-fill-standard, gap-fill-by-fill-time, previous-days-range-standard, opening-stats-standard, atr-average-true-range-standard, overnight-range-breakout-standard, green-and-red-days-by-weekday-standard, opening-range-breakout-by-size, opening-range-breakout-by-retracement, initial-balance-breakout-by-retracement, aln-sessions-standard, noon-curve-standard, hourly-reversion-standard. A 20th slug, seasonality-standard (monthly-return seasonality), is also served by this route.

Params — report (path) = one of the slugs above; symbol (path) = ES | NQ | GC (case-insensitive; full vendor codes CME_MINI:ES1!, CME_MINI:NQ1!, COMEX:GC1! also accepted); lookback (query) = 1m | 3m | 6m | 1y | 5y (default 1y); slice (query) = 15min | 30min | 60min — only for the 4 opening-window reports (opening-range-breakout-standard, initial-balance-breakout-standard, intraday-timing-standard, opening-candle-continuation-standard); when omitted the smallest stored slice (15min) is returned for those, 'all' for everything else

Recomputed daily (computed_at shows freshness). Two summary shapes — single-row reports (like this one) return summary as a flat object of stats; multiRow reports return summary.rows (see the next card). Unknown symbol → 400; a (report, symbol, lookback, slice) combo with no stored row → 404 (this includes passing ?slice on a non-sliced report or a bad lookback value).

Example
Request
curl -s "https://api.1bb.me/v1/analytics/gap-fill-standard/ES?lookback=1y" -H "x-api-key: YOUR_API_KEY"
Response (truncated)
{
  "report": "gap-fill-standard",
  "symbol": "CME_MINI:ES1!",
  "slice": "all",
  "lookback": "1y",
  "window_from": "2025-07-01T00:00:00.000Z",
  "window_to": "2026-07-02T00:00:00.000Z",
  "summary": {
    "gap_up": 154, "gap_up_pct": 60,
    "gap_up_filled": 96, "gap_up_filled_pct": 62,
    "gap_up_not_filled": 58, "gap_up_not_filled_pct": 38,
    "gap_down": 104, "gap_down_pct": 40,
    "gap_down_filled": 65, "gap_down_filled_pct": 63,
    "gap_down_not_filled": 39, "gap_down_not_filled_pct": 38
  },
  "computed_at": "2026-07-01T19:46:38.268Z"
}
GET/v1/analytics/{report}/{symbol}multiRow shape

Same endpoint — multiRow report shape. Reports that break stats into buckets (weekday, time-of-day, size bucket, hour, session pattern) return summary.rows as an array instead of a flat object. The 6 multiRow slugs: intraday-timing-standard, gap-fill-by-fill-time, green-and-red-days-by-weekday-standard, opening-range-breakout-by-size, aln-sessions-standard, hourly-reversion-standard.

Params — Same as above. This example: report = green-and-red-days-by-weekday-standard (multiRow, no slices).

summary.rows keys vary per report: intraday-timing-standard → bucket/high_count/high_pct/low_count/low_pct; gap-fill-by-fill-time → dir/trim_count/full_count/total_fills/trim_pct/full_pct; opening-range-breakout-by-size → bucket + break counts; aln-sessions-standard → pattern 1-4 + break counts; hourly-reversion-standard → h (hour, 8-15 ET) + breaches/reversions/mae_pct.

Example
Request
curl -s "https://api.1bb.me/v1/analytics/green-and-red-days-by-weekday-standard/ES?lookback=1y" -H "x-api-key: YOUR_API_KEY"
Response (truncated)
{
  "report": "green-and-red-days-by-weekday-standard",
  "symbol": "CME_MINI:ES1!",
  "slice": "all",
  "lookback": "1y",
  "window_from": "2025-07-01T00:00:00.000Z",
  "window_to": "2026-07-02T00:00:00.000Z",
  "summary": {
    "rows": [
      { "dow": "Monday", "green": 34, "red": 18 },
      { "dow": "Tuesday", "green": 22, "red": 31 },
      "…"
    ]
  },
  "computed_at": "2026-07-01T19:47:55.247Z"
}
Notes & caveats — Analytics

Macro & Rates

GET/v1/macro

Fundamental macro scoreboard: ~50 instruments (indices, FX, commodities) each scored across economic pillars (inflation, labour, GDP, PMIs, retail, COT, trend, seasonality) into a weighted bias, with the intraday Macro Pulse signal overlay per instrument.

Recomputed every 4h, plus event-driven recompute when a HIGH-impact economic actual prints; the pulse overlay refreshes every 15 min on weekdays. 404 {"error":"no macro data yet"} if the snapshot is missing.

Example
Request
curl -s https://api.1bb.me/v1/macro -H "x-api-key: YOUR_API_KEY"
Response (truncated)
{
  "data": {
    "version": "v3",
    "updated_at": "2026-07-02T05:04:44.603Z",
    "instruments": [
      {
        "base": "ES", "name": "S&P 500", "type": "index", "bias": "Very Bullish",
        "rawWeighted": 54.8, "macroConfidence": 78, "final": 71,
        "gdp": 1.9, "pce": 2.9, "ppi": -0.5, "adp": -2.8, "claim": 2.2, "jolts": 1.7,
        "labour": 2.2, "retail": 3, "inflation": 0, "interest": 0, "unemployment": 2.3,
        "mpmi": -1.2, "spmi": 0.7, "confidence": -2.5, "sales": 2.7,
        "cot": 3, "trend": 3, "seasonality": 1, "risk": 0.2,
        "pulse": {
          "signal": "Wait", "conviction": "Low", "quality": 44, "momentum": 0.5,
          "sessionReturn": 0.28, "relativeVolume": 0.1, "prevSignal": "Strong Buy",
          "updatedAt": "2026-07-02T05:03:49.238Z"
        }
      },
      { "base": "YM", "name": "Dow Jones", "…": "… 49 more instruments" }
    ]
  },
  "source_updated_at": null,
  "updated_at": "2026-07-02T05:04:44.605Z"
}
GET/v1/rate-tracker

Central-bank rate-probability tracker for 8 banks (Fed, ECB, BoE, BoJ, SNB, BoC, RBA, RBNZ), keyed by country code. Per country: next-meeting hold/hike/cut odds, the full meeting path, week/month-ago comparisons, implied policy curve, and a realtime 24h intraday change[] series merged in at serve time from 5-min STIR ticks.

LARGE payload (~8 countries × 10+ meetings + intraday series). Probability table rebuilt every 6h with an hourly top-up; change[] is 5-min STIR-implied bp, pinned to the published table's front-meeting implied_bp at snapshot time. implied_bp is basis points priced for the meeting; curve y is the implied policy rate in %.

Example
Request
curl -s https://api.1bb.me/v1/rate-tracker -H "x-api-key: YOUR_API_KEY"
Response (truncated)
{
  "data": {
    "version": "v3",
    "countries": {
      "US": {
        "central_bank": "Federal Reserve", "central_bank_code": "Fed",
        "current_rate": 3.75, "std_move": 25, "anchor_date": "2026-06-17T18:00:00.000Z",
        "next": { "meeting_date": "2026-07-29", "probable_move": "Hold",
                  "hold_pct": 67.4, "hike_pct": 32.6, "cut_pct": 0, "implied_bp": 8.2 },
        "meetings": [
          { "date": "2026-07-29", "probable_move": "Hold", "hold_pct": 67.4,
            "hike_pct": 32.6, "cut_pct": 0, "implied_bp": 8.2 },
          "… 9 more meetings"
        ],
        "week":  [ { "date": "2026-07-29", "probable_move": "Hold", "hold_pct": 83.1, "hike_pct": 16.9, "cut_pct": 0, "implied_bp": 4.2 }, "…" ],
        "month": [ { "date": "2026-07-29", "probable_move": "Hold", "hold_pct": 97.3, "hike_pct": 0, "cut_pct": 2.7, "implied_bp": 1.2 }, "…" ],
        "curve":  [ { "x": "2026-07-29", "y": 3.832 }, "…" ],
        "points": [ { "x": "2026-07-29", "y": 8.2 }, "…" ],
        "change": [ { "x": "2026-07-02T05:08:59.250Z", "y": 8.2 }, "… 5-min points, last 24h" ]
      },
      "…": "AU, CA, CH, EU, GB, JP, NZ same shape"
    }
  },
  "source_updated_at": null,
  "updated_at": "2026-07-02T04:12:35.096Z"
}
GET/v1/committee-bias

Latest AI hawkish/dovish sentiment for each of the 8 central-bank committees: overall bias label with confidence, the dovish/neutral/hawkish member breakdown, and the classified recent headlines behind the call.

LARGE payload (8 banks × ~50 headlines each). Refreshed hourly; updated_at is the newest fetched_at across banks.

Example
Request
curl -s https://api.1bb.me/v1/committee-bias -H "x-api-key: YOUR_API_KEY"
Response (truncated)
{
  "updated_at": "2026-07-02T04:49:32.662Z",
  "banks": [
    {
      "bank_code": "Fed",
      "fetched_at": "2026-07-02T04:49:30.247Z",
      "bias_label": "Hawkish",
      "bias_confidence": "0.9",
      "bias_summary": "The majority of FED bankers are hawkish (Dovish: 1, Neutral: 8, Hawkish: 12. ). The overall tone is hawkish because the majority of recent comments from Fed officials…",
      "breakdown_dovish": 1,
      "breakdown_neutral": 8,
      "breakdown_hawkish": 12,
      "headlines": [
        { "ts": "2026-07-01 19:30",
          "text": "Fed's Warsh taps Treasury official Schwab for advisory role, source says",
          "label": "Neutral", "confidence": 0.8 },
        "… up to 50 headlines per bank"
      ],
      "headlines_count": 50
    },
    { "bank_code": "BoE", "bias_label": "Hawkish", "bias_confidence": "0.74", "…": "…" },
    "… 8 banks total: BoC, BoE, BoJ, ECB, Fed, RBA, RBNZ, SNB"
  ]
}
GET/v1/cesi

Country macro overview: one headline row per tracked country with the latest CESI (economic-surprise index), 2s10s spread, REER, terms-of-trade, CPI, unemployment and GDP readings.

Refreshed every 12h. Countries ordered by country_code.

Example
Request
curl -s https://api.1bb.me/v1/cesi -H "x-api-key: YOUR_API_KEY"
Response (truncated)
{
  "count": 8,
  "countries": [
    {
      "country_code": "AU", "country_label": "Australia",
      "latest_cesi": -1.776, "latest_spread": 0.31, "latest_reer": 2.005, "latest_ctot": 0.66,
      "latest_cpi": 4, "latest_unemployment": 4.4, "latest_gdp": 0.3,
      "updated_at": "2026-07-01T19:45:37.637Z"
    },
    {
      "country_code": "CA", "country_label": "Canada",
      "latest_cesi": -0.607, "latest_spread": 0.637, "latest_reer": -2.292, "latest_ctot": 0.167,
      "latest_cpi": 3.2, "latest_unemployment": 6.6, "latest_gdp": 0,
      "updated_at": "2026-07-01T19:45:35.870Z"
    },
    "… 6 more: CH, EU, GB, JP, NZ, US"
  ]
}
GET/v1/cesi/{country}

Full 9-tile macro detail for one country: complete time series (date/value points) for CESI, yield spread, REER, terms-of-trade, inflation expectations, yield curve, CPI, GDP and unemployment — chart-ready.

Params — country (path) = 2-letter code, case-insensitive (uppercased server-side); one of AU, CA, CH, EU, GB, JP, NZ, US — 404 otherwise

Each tile is { latest, series: [{ name, points: [[date, value], …] }] }; some tiles carry multiple series (e.g. Headline + Core CPI). Refreshed every 12h, with the newest official prints spliced onto the series tails.

Example
Request
curl -s https://api.1bb.me/v1/cesi/US -H "x-api-key: YOUR_API_KEY"
Response (truncated)
{
  "country_code": "US", "country_label": "United States",
  "latest_cesi": 0.799, "latest_spread": 0.291, "latest_reer": 0.463, "latest_ctot": 0.123,
  "cesi": {
    "latest": 0.799,
    "series": [ { "name": "CESI", "points": [ "…", ["2026-06-30", 0.799] ] } ]
  },
  "cpi": {
    "latest": 4.2,
    "series": [
      { "name": "Headline CPI", "points": [ "… 128 monthly points", ["2026-05-31", 4.2] ] },
      { "name": "Core CPI", "points": [ "…" ] }
    ]
  },
  "spread": { "latest": "…", "series": [ "…" ] },
  "reer": "… same { latest, series } shape",
  "ctot": "…", "infl_exp": "…", "yield_curve": "…", "gdp": "…", "unemployment": "…",
  "updated_at": "2026-07-01T19:45:26.788Z"
}
GET/v1/iv

Cross-asset implied-volatility table: 42 assets (FX pairs, indices, commodities) with annualized IV%, the expected daily move, and 1/2/3-standard-deviation price bands for the session — flat rows plus the same rows grouped by asset class.

Refreshed every 12h. Numeric fields in rows are returned as strings (as scraped); 404 {"error":"no iv data yet"} if the snapshot is missing.

Example
Request
curl -s https://api.1bb.me/v1/iv -H "x-api-key: YOUR_API_KEY"
Response (truncated)
{
  "data": {
    "row_count": 42,
    "updated_at": "2026-07-01T19:44:32.247Z",
    "rows": [
      {
        "asset": "AUDCAD", "date": "2026-07-01", "iv": "5.74", "exp_d_move": "0.0036",
        "low_1sd": "0.9786", "high_1sd": "0.9858",
        "low_2sd": "0.9751", "high_2sd": "0.9893",
        "low_3sd": "0.9715", "high_3sd": "0.9929"
      },
      "… 41 more rows"
    ],
    "grouped": {
      "forex": [ "… same row objects" ],
      "indices": [ "… e.g. DAX" ],
      "commodities": [ "…" ]
    }
  },
  "source_updated_at": null,
  "updated_at": "2026-07-01T19:44:32.248Z"
}
Notes & caveats — Macro & Rates

Sentiment & Positioning + Research

GET/v1/fng

Fear & Greed dashboard: CNN's stock-market Fear & Greed index (composite score plus its 7 sub-indicators and ~1y daily history) and the alternative.me crypto Fear & Greed index (365 daily readings), in one payload.

Refreshed every 12h. Either stock or crypto can be null if that source hasn't loaded yet; 404 only when both are missing. Crypto values/timestamps are strings (upstream format); historical x is epoch ms.

Example
Request
curl -s https://api.1bb.me/v1/fng -H "x-api-key: YOUR_API_KEY"
Response (truncated)
{
  "stock": {
    "data": {
      "fear_and_greed": { "score": 33.14, "rating": "fear", "timestamp": "2026-07-01T19:36:20+00:00",
        "previous_close": 31.23, "previous_1_week": 25.97, "previous_1_month": 56.54, "previous_1_year": 67.54 },
      "market_momentum_sp500": { "score": 54.8, "rating": "neutral", "...": "..." },
      "...": "6 more sub-indicators: put_call_options, market_volatility_vix, market_volatility_vix_50, stock_price_strength, stock_price_breadth, junk_bond_demand, safe_haven_demand, market_momentum_sp125",
      "fear_and_greed_historical": { "score": 33.14, "rating": "fear",
        "data": [ { "x": 1751328000000, "y": 67.54, "rating": "greed" }, "… 252 more daily points" ] }
    },
    "updated_at": "2026-07-01T19:44:09.400Z"
  },
  "crypto": {
    "data": {
      "name": "Fear and Greed Index",
      "data": [ { "value": "11", "value_classification": "Extreme Fear", "timestamp": "1782864000" }, "… 364 more daily points" ],
      "metadata": { "error": null }
    },
    "source_updated_at": "2026-07-01T00:00:00.000Z",
    "updated_at": "2026-07-01T19:44:10.432Z"
  }
}
GET/v1/cot

CFTC Commitment of Traders (Legacy report) positioning for 26 futures markets — FX, indices, metals, energy, ag, rates, VIX, Bitcoin — with the last 52 weekly records per instrument plus the matching weekly price bar.

Positioning is weekly (CFTC report date = Tuesday, field d); the job re-checks every 6h so new releases land within hours. Symbols: 6A 6B 6C 6E 6J 6N 6S BT CL DC DX ES GC HG KC NG NQ PL RTY SI VX YM ZC ZN ZS ZW. Price bar (o/h/l/c/ch) can be null if the weekly price fetch failed; oi is null for the first ~10 weeks of a series and can slightly exceed 100% at extremes.

Example
Request
curl -s https://api.1bb.me/v1/cot -H "x-api-key: YOUR_API_KEY"
Response (truncated)
{
  "data": [
    {
      "s": "ES", "n": "S&P 500 E-Mini", "t": "INDICES", "d": "2026-06-23",
      "co": -87130,        // commercials net (long - short)
      "hf": -35448,        // non-commercials / hedge-fund net
      "rt": 122578,        // non-reportables / retail net
      "hl": "47%", "hs": "53%",   // non-comm long/short share of positioning
      "hlc": -10985, "hsc": -169515, // weekly change in non-comm longs / shorts
      "pt": -1.8,          // net % of open interest (long% - short%)
      "oi": "102%",        // percent-rank of pt vs trailing 52 weeks
      "o": 7543.25, "h": 7599.25, "l": 7357.25, "c": 7401.75, "ch": "-2.2%" // weekly price bar
    },
    "… ~1351 more rows (26 instruments x 52 weeks, sorted by symbol then date)"
  ],
  "source_updated_at": null,
  "updated_at": "2026-07-02T01:44:56.540Z"
}
GET/v1/seasonality

Monthly % returns per asset per year for the same 26 futures symbols — one row per asset-year (2015-2026) plus aggregate rows: 3y/5y/7y/10y averages, 10std (10y std-dev), and pos/neg (share of positive/negative years).

Rebuilt daily. year is a number for calendar years but a string for aggregate rows ("3y","5y","7y","10y","10std","pos","neg"). Month values are percent strings.

Example
Request
curl -s https://api.1bb.me/v1/seasonality -H "x-api-key: YOUR_API_KEY"
Response (truncated)
{
  "data": [
    { "asset": "ES", "year": 2015,
      "jan": "-2.10%", "feb": "4.24%", "mar": "-1.25%", "apr": "1.51%",
      "may": "-0.33%", "jun": "-0.53%", "jul": "1.06%", "aug": "-6.26%",
      "sep": "1.18%", "oct": "4.88%", "nov": "0.53%", "dec": "-1.56%" },
    { "asset": "ES", "year": "10y",
      "jan": "0.99%", "feb": "-0.30%", "mar": "-1.30%", "apr": "1.85%",
      "may": "1.90%", "jun": "1.49%", "jul": "1.27%", "aug": "1.26%",
      "sep": "-1.12%", "oct": "0.85%", "nov": "3.25%", "dec": "0.13%" },
    "… ~487 more rows"
  ],
  "source_updated_at": null,
  "updated_at": "2026-07-01T19:44:33.097Z"
}
GET/v1/research

Bank research feed: sell-side reports (Danske, MUFG, ING, Rabobank, BofA, UniCredit, Reuters/LSEG and ~50 more sources) with an extracted summary, directional bias, key points and topic tags. Newest first.

Params — limit = 1-300, default 100 — max reports returned

New reports ingested hourly; a daily cleanup drops reports older than 14 days, so this is a rolling 2-week window (~270 reports). report_id serializes as a JSON string (bigint). has_pdf tells you whether /v1/research/{id}/pdf will work.

Example
Request
curl -s "https://api.1bb.me/v1/research?limit=5" -H "x-api-key: YOUR_API_KEY"
Response (truncated)
{
  "count": 5,
  "reports": [
    {
      "report_id": "2611",
      "bank": "Danske Bank", "date": "2026-07-01", "type": "credit",
      "title": "Deutsche Pfandbriefbank AG",
      "bias": "descriptive", "bias_scope": "descriptive", "bias_confidence": "high",
      "summary": "This is a Danske Bank Credit Research issuer profile on Deutsche Pfandbriefbank (PBB) rather than a directional trade note. The report focuses on PBB's credit profile…",
      "main_points": ["1. PBB is a German commercial real estate lender focused on selected European markets…", "…"],
      "key_views": [],
      "tags": ["PBBGR", "PBBGY", "PBB COVERED BONDS", "PBB SENIOR BONDS"],
      "topics": ["commercial real estate lending", "US exit", "risk transfer (SRT)", "…"],
      "bias_evidence": "The note is an issuer profile and credit analysis with no recommendation or directional market call…",
      "pdf_url": "https://…",
      "pdf_filename": "Deutsche_Pfandbriefbank_IssuerProfile_300626_Retail_version.pdf",
      "processed_at": "Danske Bank 2026-07-01 credit Deutsche Pfandbriefbank AG Processed: 2026-07-01 18:10 Berlin",
      "created_at": "2026-07-01T16:53:58.703Z",
      "has_pdf": true
    },
    "… 4 more"
  ]
}
GET/v1/research/{id}

One research report by its report_id — identical shape to a /v1/research list item (summary, bias, main_points, tags, topics, has_pdf).

Params — id (path) = numeric report_id from /v1/research

404 { "error": "no report {id}" } if the id doesn't exist (or has aged out of the 14-day window).

Example
Request
curl -s https://api.1bb.me/v1/research/2611 -H "x-api-key: YOUR_API_KEY"
Response (truncated)
{
  "report_id": "2611",
  "bank": "Danske Bank", "date": "2026-07-01", "type": "credit",
  "title": "Deutsche Pfandbriefbank AG",
  "bias": "descriptive", "bias_scope": "descriptive", "bias_confidence": "high",
  "summary": "This is a Danske Bank Credit Research issuer profile on Deutsche Pfandbriefbank (PBB)…",
  "main_points": ["1. PBB is a German commercial real estate lender focused on selected European markets…", "…"],
  "key_views": [],
  "tags": ["PBBGR", "PBBGY", "PBB COVERED BONDS", "PBB SENIOR BONDS"],
  "topics": ["commercial real estate lending", "US exit", "…"],
  "bias_evidence": "The note is an issuer profile and credit analysis with no recommendation or directional market call…",
  "pdf_url": "https://…",
  "pdf_filename": "Deutsche_Pfandbriefbank_IssuerProfile_300626_Retail_version.pdf",
  "created_at": "2026-07-01T16:53:58.703Z",
  "has_pdf": true
}
GET/v1/research/{id}/pdf

The original report PDF, streamed as binary. Only available when the report's has_pdf flag is true.

Params — id (path) = numeric report_id from /v1/research

Check has_pdf on the report card first; PDFs age out with their reports after 14 days.

Example
Request
curl -s https://api.1bb.me/v1/research/2611/pdf -H "x-api-key: YOUR_API_KEY" -o report-2611.pdf
Response (truncated)
(binary PDF bytes — not JSON)
Content-Type: application/pdf
Content-Disposition: inline; filename="Deutsche_Pfandbriefbank_IssuerProfile_300626_Retail_version.pdf"

On failure: 404 JSON — { "error": "no PDF for report 2611" } when the report has
no stored PDF, or { "error": "PDF file missing for report 2611 (will re-fetch)" }
if the file is temporarily absent on disk.
Notes & caveats — Sentiment & Positioning + Research

Calendar & Economics

GET/v1/calendar

Economic calendar events in a time window, with CE enrichment (impact override, surprise thresholds, playbook notes, forecast distribution). Rows update in place as actuals land at release.

Params — from = ISO datetime (default now-1d); to = ISO datetime (default now+7d); country = ISO-2 code or ALL (default: majors AU,CA,CH,CN,DE,EU,FR,GB,JP,NZ,US); impact = HIGH|MEDIUM|LOW|NONE (matched against CE-effective impact); enriched = true — only CE-enriched events; limit = default 200, max 1000

impact is CE-effective: COALESCE(ce_impact, impact) — CE enrichment overrides the raw feed's impact where available. Values: HIGH|MEDIUM|LOW|NONE. ce_* fields are null until the enrichment job has processed the event. Rows mutate in place at release; poll or use /v1/calendar/stream.

Example
Request
curl "https://api.1bb.me/v1/calendar?country=US&impact=HIGH&enriched=true" -H "x-api-key: YOUR_API_KEY"
Response (truncated)
{
  "count": 42,
  "events": [
    {
      "cbf_uid": "6489d5b59a2596ec5c56dd2d:396183",
      "release_at": "2026-07-02T12:30:00.000Z",
      "country": "US", "currency": "USD",
      "event_name": "Initial Jobless Claims", "category_name": "Initial Jobless Claims",
      "impact": "HIGH", "is_holiday": false,
      "actual": null, "forecast": 220, "previous": 215, "revised": null,
      "actual_display": "", "forecast_display": "220K", "previous_display": "215K",
      "surprise_score": 2, "surprise_basis": 13,
      "ec_code": "USJOB=ECI", "mapping_method": "llm", "mapping_confidence": 96,
      "ce_min_threshold": 215, "ce_max_threshold": 230,
      "ce_notes": { "comments": ["Fed's Williams sees US GDP growth at 2.25% and unemployment at 4% in 2028", "…"], "playbook": "…", "what_to_watch": "…" },
      "ce_forecast_dist": { "low": 215, "high": 230, "ric": "USJOB=ECI", "period": "W 27 Jun", "history": { "latest": { "date": "2026-06-06", "actual": 229000 }, "points": ["…"] } },
      "ce_enriched_at": "2026-06-28T18:43:29.986Z"
    },
    "…"
  ]
}
GET/v1/calendar/upcoming

Next upcoming events at a given impact level (default HIGH) — a slim convenience feed that lifts the CE playbook and what-to-watch text out of ce_notes.

Params — impact = HIGH|MEDIUM|LOW|NONE (default HIGH, CE-effective); country = ISO-2 (optional; unlike /v1/calendar there is NO default majors filter — all countries); limit = default 20, max 200

Only future events (release_at >= now). ce_min_threshold/ce_max_threshold bracket the consensus range — an actual outside them is a tradeable surprise.

Example
Request
curl "https://api.1bb.me/v1/calendar/upcoming?limit=5" -H "x-api-key: YOUR_API_KEY"
Response (truncated)
{
  "count": 5,
  "events": [
    {
      "release_at": "2026-07-02T09:00:00.000Z",
      "country": "EU", "event_name": "Unemployment Rate", "impact": "HIGH",
      "currency": "EUR", "forecast": 6.3, "previous": 6.3, "forecast_display": "6.3%",
      "ec_code": "EUUNR=ECI",
      "ce_min_threshold": 6.3, "ce_max_threshold": 6.4,
      "playbook": "Notes:\n\nUnemployment is a lagging indicator but still important. Declines support EUR via stronger growth outlook; increases weaken it…",
      "what_to_watch": null
    },
    "…"
  ]
}
GET/v1/calendar/history

Historical releases from the Refinitiv feed (1yr+ of past prints per series) — pull a series' full print history by ticker, or slice by country/date range.

Params — ticker = Refinitiv ECI code (e.g. USCPI=ECI); country = ISO-2; from / to = ISO datetime; limit = default 50, max 1000. At least one of ticker/country/from/to is required (else 400).

Sorted newest-first (release_at DESC). impact here is Refinitiv's numeric 1-3 scale, NOT the HIGH/MEDIUM/LOW string used by the other calendar endpoints. min_exp/max_exp are the economist forecast range.

Example
Request
curl "https://api.1bb.me/v1/calendar/history?ticker=USCPI%3DECI&limit=24" -H "x-api-key: YOUR_API_KEY"
Response (truncated)
{
  "count": 24,
  "events": [
    {
      "release_at": "2026-06-10T12:30:00.000Z",
      "ticker": "USCPI=ECI", "name": "CPI MM, SA", "country": "US",
      "reference_period": null,
      "actual": 0.5, "consensus": 0.5, "previous": 0.6, "revised": null,
      "min_exp": 0.4, "max_exp": 0.7, "impact": 3
    },
    {
      "release_at": "2026-05-12T12:30:00.000Z",
      "ticker": "USCPI=ECI", "name": "CPI MM, SA", "country": "US",
      "actual": 0.6, "consensus": 0.6, "previous": 0.9,
      "min_exp": 0.4, "max_exp": 0.9, "impact": 3
    },
    "…"
  ]
}
GET/v1/econ

Directory of economic indicator series (metadata only, no data points) — each series carries ~14 months of chart-ready history retrievable via /v1/econ/{indicator}.

Params — country = ISO-2; q = name substring search (case-insensitive); limit = default 500, max 2000

Sorted by country then name. urgency (HIGH|MEDIUM|LOW) is the series' importance tier. Use exo as the {indicator} path param on the detail endpoint.

Example
Request
curl "https://api.1bb.me/v1/econ?country=US&q=inflation" -H "x-api-key: YOUR_API_KEY"
Response (truncated)
{
  "count": 2,
  "series": [
    {
      "exo": "AUCON=ECI", "code": "AUCON=ECI",
      "name": "Ai Group Construction Index", "country": "AU",
      "category": "Miscellaneous", "urgency": "LOW", "unit": null,
      "points": 14,
      "first_date": "2025-05-06T00:00:00.000Z",
      "last_date": "2026-06-30T00:00:00.000Z",
      "last_updated": "2026-07-01T19:44:09.393Z"
    },
    "…"
  ]
}
GET/v1/econ/{indicator}

One economic series with its full data[] array (each point = release date, actual value, consensus, and the low/high forecast range) — ready to chart.

Params — indicator (path) = series exo ticker, URL-encoded (e.g. USCPF%3DECI). 404 if unknown.

History depth is ~14 months per series (refreshed daily). Remember to URL-encode the = in the ticker.

Example
Request
curl "https://api.1bb.me/v1/econ/USCPF%3DECI" -H "x-api-key: YOUR_API_KEY"
Response (truncated)
{
  "exo": "USCPF=ECI", "code": "USCPF=ECI",
  "name": "Core Inflation Rate MoM", "country": "US",
  "category": "Inflation", "urgency": "HIGH", "unit": "%",
  "points": 13,
  "first_date": "2025-05-13T00:00:00.000Z",
  "last_date": "2026-06-10T00:00:00.000Z",
  "last_updated": "2026-07-01T19:44:09.393Z",
  "data": [
    { "date": "2025-05-13", "value": 0.2, "consensus": 0.3, "low": 0.1, "high": 0.6 },
    { "date": "2025-06-11", "value": 0.1, "consensus": 0.3, "low": 0.1, "high": 0.5 },
    "…"
  ]
}
Notes & caveats — Calendar & Economics

Errors

StatusMeaning
401Missing or invalid API key.
403Endpoint not included in your plan.
404Unknown path or resource.
429Rate limit exceeded — back off and retry.
500Server error — safe to retry shortly.

Error bodies are JSON: { "error": "…" }.

Need a key, a higher limit, or an endpoint that isn't here? Contact the 1BullBear team.