BULLINV DATA
API down

bullinv-quant-data API

REST API for the internal quant market-data platform: aggregate bars, raw ticks, symbol metadata, pipeline status, bar coverage and admin backfills. All timestamps are Asia/Taipei (+08:00). Every response (except /health) is wrapped in a standard envelope:

{ "code": 200, "message": "ok", "data": { ... } }

Authentication

Authenticate by passing your key in the X-API-Key header on every request. Keys are issued per service by the platform team.

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/symbols"
Base URLhttps://bullinv-data-api.bullinv.appAuth headerX-API-Key: $BULLINV_API_KEYTimezoneAsia/Taipei (+08:00)

The “Try it” panels below send requests through this console’s server-side proxy, which attaches the API key for you — the key never reaches the browser.

GET/v1/bars/{symbol}

Aggregate Bars

OHLCV bars for a symbol over a date range, aggregated from raw ticks. Supports 1m to 1d timeframes and day/night/full session filters. Timestamps are Asia/Taipei.

Parameters

NameInTypeDescription
symbol*pathstringContract symbol, e.g. the continuous front-month TXF contract.
timeframequerystringBar aggregation window.one of: 1m | 5m | 15m | 30m | 1h | 1ddefault: 1m
startquerystring (ISO 8601)Inclusive range start. Defaults to the current trading day's open.
endquerystring (ISO 8601)Exclusive range end. Defaults to now.
sessionquerystringSession filter. day = 08:45-13:45, night = 15:00-05:00 (crosses midnight), full = both.one of: day | night | fulldefault: full
limitqueryintegerMaximum number of bars returned (max 5000). Use next_start to paginate.default: 1500

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/bars/TXFR1?timeframe=1m&start=2026-06-30T08%3A45%3A00%2B08%3A00&end=2026-06-30T13%3A45%3A00%2B08%3A00&session=day&limit=500"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "symbol": "TXFR1",
    "timeframe": "1m",
    "session": "day",
    "tz": "Asia/Taipei",
    "count": 300,
    "results": [
      {
        "ts": "2026-06-30T08:45:00+08:00",
        "open": 23012,
        "high": 23021,
        "low": 23008,
        "close": 23017,
        "volume": 412,
        "amount": 9482804,
        "tick_count": 287,
        "buy_volume": 221,
        "sell_volume": 191
      }
    ],
    "next_start": null
  }
}
  • Bars are keyed by their opening minute; a bar covering 08:45:00-08:45:59 has ts 08:45:00.
  • When more bars exist than limit allows, next_start holds the ts to pass as start on the next call.
Try it
GET/v1/ticks/{symbol}

Raw Ticks

Raw trade ticks for a symbol. Ascending (oldest first) by default; pass order=desc for a newest-first tape. tick_type 1 = outer (traded at ask), 2 = inner (traded at bid), 0 = indeterminate.

Parameters

NameInTypeDescription
symbol*pathstringContract symbol.
startquerystring (ISO 8601)Inclusive range start.
endquerystring (ISO 8601)Exclusive range end. Defaults to now.
limitqueryintegerMaximum number of ticks returned (max 1000).default: 200
include_simtradequerybooleanInclude simulated pre-open matching ticks (simtrade=true).one of: 0 | 1default: 0
orderquerystringSort order. desc returns newest first; next_start pagination is only supported for asc (desc always returns next_start=null).one of: asc | descdefault: asc

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/ticks/TXFR1?start=2026-06-30T09%3A00%3A00%2B08%3A00&end=2026-06-30T09%3A05%3A00%2B08%3A00&limit=50&include_simtrade=0&order=desc"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "count": 50,
    "results": [
      {
        "ts": "2026-06-30T09:04:59.812+08:00",
        "recv_ts": "2026-06-30T09:04:59.819+08:00",
        "contract": "TXFR1",
        "price": 23015,
        "volume": 3,
        "total_volume": 48211,
        "tick_type": 1,
        "bid_side_total_vol": 22903,
        "ask_side_total_vol": 25308,
        "simtrade": false
      }
    ],
    "next_start": "2026-06-30T09:03:12.044+08:00"
  }
}
  • next_start holds the ts to pass as start on the next call; it is null when order=desc.
Try it
GET/v1/live/{symbol}

Live Snapshot

Single-call snapshot for a live trading view: running day stats, the level-5 order book and the most recent trades (newest first). Poll this instead of stitching /v1/ticks and /v1/bidask together client-side.

Parameters

NameInTypeDescription
symbol*pathstringContract symbol.
tape_limitqueryintegerNumber of tape entries returned, newest first (max 200).default: 40

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/live/TXFR1?tape_limit=60"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "symbol": "TXFR1",
    "tz": "Asia/Taipei",
    "market_open": true,
    "stats": {
      "last_price": 46231.0,
      "price_chg": 35.0,
      "pct_chg": 0.08,
      "open": 46200.0,
      "high": 46280.0,
      "low": 46150.0,
      "avg_price": 46210.5,
      "total_volume": 41231,
      "total_amount": 1904000000000,
      "buy_volume_today": 21031,
      "sell_volume_today": 19822,
      "underlying_price": 46210.11,
      "basis": 20.89,
      "last_tick_ts": "2026-06-30T09:04:59.812+08:00",
      "last_tick_age_s": 1.2
    },
    "book": {
      "ts": "2026-06-30T09:05:00.104+08:00",
      "bids": [{ "price": 46230.0, "volume": 12 }],
      "asks": [{ "price": 46231.0, "volume": 9 }],
      "bid_total_vol": 123,
      "ask_total_vol": 140
    },
    "tape": [
      {
        "ts": "2026-06-30T09:04:59.812331+08:00",
        "price": 46231.0,
        "volume": 2,
        "tick_type": 1,
        "total_volume": 41231
      }
    ]
  }
}
  • stats come from the latest non-simtrade tick; buy/sell_volume_today are summed from 1m bars since Taipei midnight.
  • basis = last_price - underlying_price; both are null when the feed has no underlying price.
  • book is null until bidask snapshots exist for the symbol; levels with price or volume <= 0 are dropped.
  • tape is newest first and excludes simtrade ticks.
Try it
GET/v1/bidask/{symbol}

Order Book Snapshots

Raw level-5 bid/ask snapshots as captured from the feed, keyset-paginated in the same style as /v1/ticks. Arrays are ordered L1 (best) to L5.

Parameters

NameInTypeDescription
symbol*pathstringContract symbol.
startquerystring (ISO 8601)Inclusive range start.
endquerystring (ISO 8601)Exclusive range end. Defaults to now.
limitqueryintegerMaximum number of snapshots returned (max 1000).default: 200
orderquerystringSort order. desc returns newest first; next_start pagination is only supported for asc (desc always returns next_start=null).one of: asc | descdefault: asc

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/bidask/TXFR1?start=2026-06-30T09%3A00%3A00%2B08%3A00&end=2026-06-30T09%3A05%3A00%2B08%3A00&limit=50&order=desc"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "count": 1,
    "results": [
      {
        "ts": "2026-06-30T09:04:59.900+08:00",
        "recv_ts": "2026-06-30T09:04:59.904+08:00",
        "contract": "TXFR1",
        "bid_prices": [46230, 46229, 46228, 46227, 46226],
        "bid_volumes": [12, 8, 21, 17, 30],
        "ask_prices": [46231, 46232, 46233, 46234, 46235],
        "ask_volumes": [9, 14, 25, 11, 19],
        "bid_total_vol": 123,
        "ask_total_vol": 140,
        "underlying_price": 46210.11,
        "simtrade": false
      }
    ],
    "next_start": null
  }
}
  • Snapshots dedupe on (ts, symbol, level-1 fields); identical Kafka redeliveries collapse to one row.
Try it
GET/v1/stream/{symbol}

Live Tick Stream (SSE)

Server-Sent Events 即時成交推播:每 interval_ms 查一次最新非試撮 tick,有變化才推一筆(同 ts+price 不重複推)。連線閒置 15 秒會送 : ping 心跳;EventSource 自動重連(retry: 3000)。

Parameters

NameInTypeDescription
symbol*pathstring連續代碼。
interval_msqueryinteger輪詢間隔(100–2000ms)。default: 300

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/stream/TXFR1?interval_ms=300"

Response

retry: 3000

data: {"price": 23145, "volume": 3, "ts": "2026-07-02T10:31:07.123+08:00"}

: ping
  • media type 是 text/event-stream,不走標準 envelope。
  • 此端點無法用本頁 Try-It 測試(console 的 BFF proxy 會緩衝整個回應)——請直連 data-api 並帶 X-API-Key。
Try it
GET/v1/symbols

List Symbols

All symbols tracked by the platform, with their session schedule and stored-bar extent.

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/symbols"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "results": [
      {
        "symbol": "TXFR1",
        "source": "shioaji",
        "display_name": "台指期近月",
        "sessions": {
          "day": ["08:45", "13:45"],
          "night": ["15:00", "05:00"]
        },
        "first_bar_ts": "2026-01-05T08:45:00+08:00",
        "last_bar_ts": "2026-06-30T13:44:00+08:00",
        "bar_count": 137940
      }
    ]
  }
}
Try it
GET/v1/internal/bars/{symbol}

Internal Bulk Bars

Unpaginated bulk 1m bars for internal batch consumers (e.g. the backtest engine). Streams QuestDB's raw /exec JSON straight through — no envelope, no per-row transformation — so multi-hundred-thousand-row fetches run at near-direct speed while still being authenticated and visible in /usage.

Parameters

NameInTypeDescription
symbol*pathstringContract symbol.
startquerystring (ISO 8601)Inclusive range start (naive = Asia/Taipei). Defaults to end − 7d.
endquerystring (ISO 8601)Inclusive range end. Defaults to now.

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/internal/bars/TXFR1?start=2026-06-20T08%3A45%3A00%2B08%3A00&end=2026-06-30T13%3A45%3A00%2B08%3A00"

Response

{
  "query": "SELECT ts, open, high, low, close, volume, amount FROM bars_1m ...",
  "columns": [
    { "name": "ts", "type": "TIMESTAMP" },
    { "name": "open", "type": "DOUBLE" },
    { "name": "high", "type": "DOUBLE" },
    { "name": "low", "type": "DOUBLE" },
    { "name": "close", "type": "DOUBLE" },
    { "name": "volume", "type": "LONG" },
    { "name": "amount", "type": "DOUBLE" }
  ],
  "dataset": [
    ["2026-06-30T00:45:00.000000Z", 46100.0, 46120.0, 46081.0, 46095.0, 2362, 108870000.0]
  ],
  "count": 7122
}
  • Requires an admin key (DATA_API_ADMIN_KEYS).
  • Raw QuestDB /exec passthrough: NOT wrapped in the {code,message,data} envelope; ts values are UTC ISO — convert timezone client-side (vectorized).
  • Hard safety cap 5,000,000 rows (≈15 years of 1m bars) — a guardrail, not pagination.
Try it
GET/v1/fundamentals/companies

Company Directory

上市公司目錄(搜尋 / 清單)。可用 q 依股號前綴或名稱/簡稱子字串過濾,或用 industry 依產業代碼過濾;回傳附產業名稱。資料取自 company_info 每公司最新一筆。

Parameters

NameInTypeDescription
qquerystring股號前綴或名稱/簡稱子字串(大小寫不敏感)。
industryquerystring產業代碼過濾(見 /v1/fundamentals/industries)。
limitqueryinteger回傳上限(1–2000)。default: 50

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/fundamentals/companies?q=%E5%8F%B0%E7%A9%8D&industry=24&limit=50"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "count": 1,
    "results": [
      {
        "symbol": "2330",
        "name": "台灣積體電路製造股份有限公司",
        "short_name": "台積電",
        "industry_code": "24",
        "industry": "半導體業"
      }
    ]
  }
}
Try it
GET/v1/fundamentals/industries

Industry List

產業分類清單(代碼、名稱、公司數),依公司數排序。對照由 company_info 的產業代碼 × fin_revenue 的產業名稱依 company_id 推導而成(industry_map 表)。

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/fundamentals/industries"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "count": 33,
    "results": [
      { "code": "28", "name": "電子零組件業", "company_count": 104 },
      { "code": "24", "name": "半導體業", "company_count": 95 }
    ]
  }
}
Try it
GET/v1/fundamentals/profile/{symbol}

Company Profile

公司完整基本資料:董事長、總經理、發言人、成立/上市日期、實收資本額、已發行股數、簽證會計師、股務代理、網址等(取自 company_info 的原始揭露欄位)。

Parameters

NameInTypeDescription
symbol*pathstring上市股號.

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/fundamentals/profile/2330"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "symbol": "2330",
    "profile": {
      "name": "台灣積體電路製造股份有限公司",
      "short_name": "台積電",
      "industry": "半導體業",
      "chairman": "魏哲家",
      "president": "總裁: 魏哲家",
      "listed": "19940905",
      "paid_in_capital": "259323700670",
      "shares_outstanding": "25932370067",
      "auditor_firm": "勤業眾信聯合會計師事務所",
      "website": "https://www.tsmc.com"
    }
  }
}
  • 日期欄位為 YYYYMMDD 字串;金額/股數為元/股(字串)。
Try it
GET/v1/fundamentals/income/{symbol}

Income Statement (PIT)

Point-in-time quarterly income statements for a listed company (by stock code). Each period carries period_end (fiscal quarter end) and published_at (when it became knowable); restatements are kept as separate versions. Pass as_of to reconstruct exactly what was known on a given date — the guard against look-ahead bias in fundamental backtests.

Parameters

NameInTypeDescription
symbol*pathstring上市股號 / stock code.
as_ofquerystring (ISO 8601)Point-in-time: only versions knowable on/before this date. Omit for latest.
limitqueryintegerMost-recent N periods (1–400).default: 40

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/fundamentals/income/2330?as_of=2022-05-20&limit=40"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "symbol": "2330",
    "company_name": "台積電",
    "statement": "income",
    "unit": "TWD thousands (per-share: TWD)",
    "as_of": null,
    "count": 4,
    "results": [
      {
        "period_end": "2022-12-31T00:00:00+08:00",
        "published_at": "2023-03-31T00:00:00+08:00",
        "year": 2022, "quarter": 4,
        "revenue": 2263891292.0, "gross_profit": 1348357000.0,
        "operating_income": 1121279000.0, "pretax_income": 1144190000.0,
        "net_income": 1016900515.0, "comprehensive_income": 1050000000.0,
        "eps": 39.2, "source": "mops_hist"
      }
    ]
  }
}
  • 單位:金額類欄位為仟元 (TWD thousands);eps 為元 (TWD)。
  • TWSE 損益為累計制:Q2=上半年累計、Q3=前三季累計、Q4=全年。
  • source: openapi=最新一季快照(真實出表日) / mops_hist=歷史回補(published_at 用法定申報期限保守估)。
Try it
GET/v1/fundamentals/balance/{symbol}

Balance Sheet (PIT)

Point-in-time quarterly balance sheets by stock code. Same PIT semantics and as_of parameter as the income statement.

Parameters

NameInTypeDescription
symbol*pathstring上市股號.
as_ofquerystring (ISO 8601)Point-in-time filter on published_at.
limitqueryintegerMost-recent N periods (1–400).default: 40

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/fundamentals/balance/2330?as_of=2022-05-20&limit=40"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "symbol": "2330",
    "company_name": "台積電",
    "statement": "balance",
    "unit": "TWD thousands (per-share: TWD)",
    "count": 1,
    "results": [
      {
        "period_end": "2026-03-31T00:00:00+08:00",
        "published_at": "2026-05-15T00:00:00+08:00",
        "year": 2026, "quarter": 1,
        "total_assets": 8660949685.0, "total_liabilities": 2728560764.0,
        "equity": 5932388921.0, "book_value_ps": 227.17
      }
    ]
  }
}
  • 金額=仟元、book_value_ps=元。
Try it
GET/v1/fundamentals/revenue/{symbol}

Monthly Revenue

Monthly operating revenue with MoM / YoY changes and cumulative figures, by stock code.

Parameters

NameInTypeDescription
symbol*pathstring上市股號.
limitqueryintegerMost-recent N months (1–600).default: 36

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/fundamentals/revenue/2330?limit=36"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "symbol": "2330",
    "company_name": "台積電",
    "statement": "revenue",
    "unit": "TWD thousands",
    "count": 1,
    "results": [
      {
        "month_end": "2026-05-31T00:00:00+08:00",
        "year": 2026, "month": 5,
        "revenue_month": 416975163.0, "mom_pct": 1.52, "yoy_pct": 30.09,
        "revenue_cum": 1961803721.0, "cum_yoy_pct": 29.98
      }
    ]
  }
}
  • 金額=仟元。
Try it
GET/v1/fundamentals/metrics/{symbol}

Derived Financial Metrics

衍生財務指標(每期一列,新→舊)。TWSE 損益為累計制——這裡還原單季(de-cumulate)、算近四季 TTM,並結合資產負債算 ROE/ROA/槓桿。利潤率用累計數(穩定)、成長率用累計 YoY、ROE/ROA 用 TTM 淨利(年化),單季數供成長柱狀圖。

Parameters

NameInTypeDescription
symbol*pathstring上市股號。
as_ofquerystring (YYYY-MM-DD)PIT 重建:只用該日已可知(published_at ≤ as_of)的資料。
limitqueryinteger回傳期數(1–100)。default: 24

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/fundamentals/metrics/2330?as_of=2025-12-31&limit=24"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "symbol": "2330",
    "count": 1,
    "results": [
      {
        "year": 2026, "quarter": 1,
        "revenue_q": 839253000, "net_income_q": 361564000,
        "gross_margin": 58.8, "operating_margin": 48.5, "net_margin": 43.1,
        "revenue_yoy": 41.6, "net_income_yoy": 60.3,
        "roe_ttm": 34.2, "roa_ttm": 25.1, "eps_q": 13.94
      }
    ]
  }
}
  • 金額單位=仟元(TWSE 原始揭露);比率為 %。
Try it
GET/v1/reports/{symbol}

Annual Reports

Shareholder-meeting annual report (股東會年報) index for a company, each with a presigned MinIO download URL (1-hour expiry). The PDF is served directly from object storage, not proxied through the API.

Parameters

NameInTypeDescription
symbol*pathstring上市股號.
limitqueryintegerMost-recent N years (1–100).default: 20

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/reports/2330?limit=20"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "symbol": "2330",
    "count": 1,
    "results": [
      {
        "year": 2025,
        "doc_type": "F04",
        "filename": "2025_2330_20260604F04.pdf",
        "size_bytes": 7006132,
        "sha256": "7cbf779114d4825f...",
        "status": "downloaded",
        "download_url": "https://minio.bullinv.app/zeabur/annual_reports/sii/2330/2025/...?X-Amz-..."
      }
    ]
  }
}
  • download_url 為 presigned GET(1 小時效期);未設定 MinIO 時為 null。
  • status: downloaded/skipped 皆有物件可下載;failed 無 download_url。
Try it
GET/v1/fundamentals/reports-coverage

Reports Coverage

年報下載覆蓋(監控用):全上市公司 × 年度矩陣,快速看出誰完全沒年報、哪些年漏抓、哪些年下載失敗。dash 的 Fundamentals 頁「年報下載覆蓋」面板吃這支。

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/fundamentals/reports-coverage"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "total_companies": 1089,
    "with_reports": 33,
    "no_reports": 1056,
    "total_pdfs": 35,
    "failed": 30,
    "years": [2024, 2025],
    "by_year": [{ "year": 2025, "companies": 33 }],
    "companies": [
      { "symbol": "2330", "name": "台灣積體電路製造股份有限公司", "listed_year": 1994, "years": [2024, 2025], "failed_years": [] }
    ]
  }
}
Try it
GET/v1/fundamentals/status

Fundamentals Status

基本面資料狀態總覽(給監控頁):各表筆數/公司數、逐年覆蓋 + 來源分佈(openapi 快照 / mops_hist 歷史回補)、資料新鮮度、年報摘要,以及缺口年度(期望 2013 IFRS 元年 ~ 今年,未出現視為缺口)。

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/fundamentals/status"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "tables": {
      "income": { "rows": 5102, "companies": 1079 },
      "balance": { "rows": 5102, "companies": 1079 },
      "revenue": { "rows": 1082, "companies": 1082 },
      "companies": 1089
    },
    "coverage_by_year": [
      { "year": 2022, "source": "mops_hist", "rows": 4024, "companies": 1059 },
      { "year": 2026, "source": "openapi", "rows": 1078, "companies": 1078 }
    ],
    "years_present": [2022, 2026],
    "years_missing": [2013, 2014, 2015, 2023, 2024, 2025],
    "freshness": {
      "latest_period": "2026-03-31T08:00:00+08:00",
      "latest_published": "2026-07-06T08:00:00+08:00",
      "latest_revenue_month": "2026-05-31T08:00:00+08:00"
    },
    "reports": {
      "by_status": [{ "status": "downloaded", "count": 1, "bytes": 7006132 }],
      "total": 1, "total_bytes": 7006132, "companies": 1
    }
  }
}
Try it
GET/v1/fundamentals/jobs

Fundamentals Jobs

基本面回補任務清單(data_jobs 佇列,每 job 最新狀態,新→舊)。任務由 dash 觸發、fund-worker(bullinv-fundamentals repo)執行,狀態機 requested → running → done | failed。

Parameters

NameInTypeDescription
limitqueryinteger回傳上限(1–200)。default: 30
statusqueryenum依目前狀態過濾。one of: requested | running | done | failed
kindqueryenum依任務種類過濾。one of: snapshot | backfill | revenue | reports

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/fundamentals/jobs?limit=30&status=running&kind=reports"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "count": 1,
    "results": [
      {
        "job_id": "1f2e3d4c5b6a79880911aabbccddeeff",
        "kind": "revenue",
        "status": "done",
        "worker": "fund-worker-1",
        "params": "{\"months_back\": 36}",
        "rows_written": 38904,
        "requested_by": "dash",
        "detail": "36 months × 1082 companies",
        "error": null,
        "ts": "2026-07-12T04:12:44+08:00"
      }
    ]
  }
}
Try it
POST/v1/fundamentals/jobs

Trigger Fundamentals Backfill

觸發基本面回補:落一列 'requested' 到 data_jobs 後立即回傳 job_id,實際抓取由 fund-worker 非同步執行(年報可能跑數小時)。需要 admin key——跟 POST /v1/admin/backfill 同一道門。

Parameters

NameInTypeDescription
kind*bodyenumsnapshot=OpenAPI 最新一季全上市;backfill=MOPS 財報逐年;revenue=月營收逐月;reports=年報 PDF → MinIO。one of: snapshot | backfill | revenue | reports
from_year / to_yearbodyintegerbackfill 用,民國年(90 起,預設 102~今年)。
months_backbodyintegerrevenue 用(1–240)。default: 36
allbodybooleanreports 用:全上市。
symbolsbodystring[]reports 用:指定股號清單(與 all 擇一)。
years_backbodyintegerreports 用:往回抓幾個年度。

Request

curl -X POST \
  -H "X-API-Key: $BULLINV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"kind":"revenue","from_year / to_year":"102","months_back":"36","all":"true","symbols":"[\"2330\"]","years_back":"2"}' \
  "https://bullinv-data-api.bullinv.app/v1/fundamentals/jobs"

Response

{
  "code": 200,
  "message": "ok",
  "data": { "job_id": "1f2e3d4c5b6a79880911aabbccddeeff" }
}
  • 需要 admin key。fund-worker 只能跑單一實例(佇列無原子 claim)。
Try it
GET/v1/analytics/latency/{symbol}

Ingest Latency

Pipeline latency analytics over a rolling window: tick and bidask feed latency (recv_ts − ts, ms) with percentiles, a fixed-bucket histogram and a per-minute series, plus bar-write latency (minute close → ingested_at) for realtime bars.

Parameters

NameInTypeDescription
symbol*pathstringContract symbol.
windowquerystringRolling window the stats are computed over.one of: 15m | 1h | 4h | 1ddefault: 1h

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/analytics/latency/TXFR1?window=1h"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "symbol": "TXFR1",
    "window": "1h",
    "tick": {
      "count": 8123,
      "avg_ms": 142.1,
      "p50_ms": 120.0,
      "p90_ms": 210.0,
      "p99_ms": 480.0,
      "max_ms": 2210.0,
      "negative_count": 0,
      "histogram": [
        { "le_ms": 50, "count": 10 },
        { "le_ms": 100, "count": 900 },
        { "le_ms": null, "count": 2 }
      ],
      "series": [
        { "t": "2026-07-02T10:04:00+08:00", "count": 210, "avg_ms": 130.5, "p99_ms": 320.0 }
      ]
    },
    "bidask": null,
    "bar_write": {
      "count": 120,
      "avg_s": 6.8,
      "p50_s": 6.5,
      "p99_s": 9.1,
      "max_s": 22.0
    }
  }
}
  • Latency = recv_ts − ts in milliseconds; simtrade ticks are excluded.
  • Negative latencies (local vs exchange clock skew) are excluded from the percentiles and reported via negative_count.
  • Histogram bucket upper bounds are fixed: 25, 50, 75, 100, 150, 200, 300, 500, 1000, 2000, 5000 and null (= above); series is SAMPLE BY 1m.
  • bidask is null when the bidask table has no rows for the symbol.
  • bar_write measures src='rt' bars as ingested_at − (ts + 60s); its window is widened to at least 4h so there are enough samples.
Try it
GET/v1/analytics/quality/{symbol}

Data Quality

Per-trading-day bar provenance for a symbol: how many 1m bars came from the realtime stream (rt), backfill or the vendor seed, plus volume-check failures and per-day activity aggregates.

Parameters

NameInTypeDescription
symbol*pathstringContract symbol.
daysqueryintegerNumber of trading days to return (max 60), ending at the current trading day.default: 14

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/analytics/quality/TXFR1?days=14"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "days": [
      {
        "trading_day": "2026-07-02",
        "total_bars": 1108,
        "rt_bars": 420,
        "backfill_bars": 10,
        "seed_bars": 678,
        "vol_check_fail_bars": 0,
        "avg_tick_count": 38.2,
        "total_volume": 183422
      }
    ]
  }
}
  • Bars are grouped by the src column of bars_1m; rt_bars + backfill_bars + seed_bars = total_bars.
  • trading_day attribution uses the 05:01 cut: night-session bars before 05:01 belong to the previous trading day.
  • vol_check_fail_bars counts bars whose volume disagrees with the vendor kbar during the nightly coverage check.
Try it
GET/v1/analytics/profile/{symbol}

Intraday Profile

Average intraday structure over the last N trading days: mean volume and tick count per Taipei minute-of-day, session-filtered. Useful for spotting the open/close U-shape and the night-session US-open bump.

Parameters

NameInTypeDescription
symbol*pathstringContract symbol.
daysqueryintegerNumber of recent trading days to average over (max 60).default: 20
sessionquerystringSession filter. day = 08:45-13:45, night = 15:00-05:00, full = both.one of: day | night | fulldefault: day

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/analytics/profile/TXFR1?days=20&session=day"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "session": "day",
    "days_sampled": 20,
    "minutes": [
      { "minute": "08:45", "avg_volume": 812.3, "avg_tick_count": 95.1 },
      { "minute": "08:46", "avg_volume": 590.8, "avg_tick_count": 71.4 }
    ]
  }
}
  • minute is a Taipei minute-of-day (HH:mm); values are means over the sampled trading days.
Try it
GET/v1/analytics/basis/{symbol}

Futures Basis

Futures-vs-underlying basis series over a rolling window, sampled per minute from ticks that carry an underlying price. basis = price − underlying.

Parameters

NameInTypeDescription
symbol*pathstringContract symbol.
windowquerystringRolling window of the series.one of: 1h | 4h | 1ddefault: 1d

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/analytics/basis/TXFR1?window=1d"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "series": [
      {
        "t": "2026-07-02T10:04:00+08:00",
        "price": 46231.0,
        "underlying": 46720.1,
        "basis": -489.1
      }
    ]
  }
}
  • SAMPLE BY 1m average over ticks whose underlying_price is not null; minutes without such ticks are omitted.
Try it
GET/v1/analytics/spread/{symbol}

Bid/Ask Spread

Level-1 spread analytics from bidask snapshots over a rolling window: per-minute average spread (ask L1 − bid L1) and average bid/ask total queue volumes.

Parameters

NameInTypeDescription
symbol*pathstringContract symbol.
windowquerystringRolling window of the series.one of: 1h | 4h | 1ddefault: 1d

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/analytics/spread/TXFR1?window=1d"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "series": [
      {
        "t": "2026-07-02T10:04:00+08:00",
        "avg_spread": 1.2,
        "avg_bid_total": 123.1,
        "avg_ask_total": 140.2
      }
    ],
    "count": 421
  }
}
  • SAMPLE BY 1m over bidask snapshots; a snapshot only counts when both L1 prices are > 0.
  • When the bidask table has no rows for the symbol the series is empty ([]), not an error.
Try it
GET/v1/status

Platform Status

Live health of every pipeline service (ingestor, writer, backfill, data-api), per-symbol tick freshness with a 60-minute rate history, and recent bar gaps.

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/status"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "services": [
      {
        "service": "ingestor",
        "symbol": "TXFR1",
        "status": "ok",
        "lag_ms": 12,
        "ticks_1m": 431,
        "heartbeat_age_s": 1,
        "detail": "shioaji stream connected"
      }
    ],
    "symbols": [
      {
        "symbol": "TXFR1",
        "last_tick_ts": "2026-06-30T09:04:59.812+08:00",
        "last_tick_age_s": 2,
        "last_price": 23015,
        "ticks_today": 48211,
        "simtrade_ticks_today": 193,
        "ticks_per_min": [{ "t": "2026-06-30T08:05:00+08:00", "v": 388 }]
      }
    ],
    "gaps": {
      "detected": 2,
      "filling": 1,
      "unfillable": 1,
      "recent": [
        {
          "gap_id": "gap-txfr1-20260610-1031",
          "symbol": "TXFR1",
          "gap_start": "2026-06-10T10:31:00+08:00",
          "gap_end": "2026-06-10T10:34:00+08:00",
          "expected_bars": 3,
          "actual_bars": 0,
          "status": "unfillable"
        }
      ]
    },
    "market_open": true
  }
}
Try it
GET/v1/coverage/{symbol}

Bar Coverage

Per-trading-day 1m-bar coverage for a symbol: expected vs actual bar counts and the resulting percentage, session-aware.

Parameters

NameInTypeDescription
symbol*pathstringContract symbol.
fromquerystring (date)First trading day, yyyy-MM-dd. Defaults to 90 days ago.
toquerystring (date)Last trading day, yyyy-MM-dd. Defaults to today.
sessionquerystringSession filter used for the expected-bar count.one of: day | night | fulldefault: full

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/coverage/TXFR1?from=2026-04-01&to=2026-06-30&session=full"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "results": [
      {
        "trading_day": "2026-06-10",
        "expected": 1140,
        "actual": 1071,
        "coverage_pct": 93.95,
        "is_trading_day": true,
        "verified": true,
        "vendor_bars": 1071,
        "checked_at": "2026-06-11T05:20:00+08:00"
      },
      {
        "trading_day": "2026-06-13",
        "expected": 0,
        "actual": 0,
        "coverage_pct": 0,
        "is_trading_day": false,
        "verified": false,
        "vendor_bars": null,
        "checked_at": null
      }
    ]
  }
}
Try it
GET/v1/usage/summary

API Usage Summary

Aggregated API-usage analytics over a rolling window, computed from the api_requests log: totals by status class, latency (avg / p99), a time series, the top endpoints and per-key counts.

Parameters

NameInTypeDescription
routequerystring只看某條路由的統計(usage 頁全域 filter)。
exclude_routesquerystring逗號清單,排除輪詢噪音路由(如 /v1/status)。
keyquerystring只看某把 key 的統計(整頁視角:total/series/endpoints 全部只算它)。
windowquerystringRolling window the summary is computed over.one of: 1h | 4h | 1d | 7ddefault: 1d
exclude_keysquerystringComma-separated key labels to exclude from ALL stats (totals, series, endpoints, keys) — e.g. hide the dashboard's own traffic by its named key.

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/usage/summary?window=1d&exclude_keys=dash"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "window": "1d",
    "total": 8213,
    "count_2xx": 8100,
    "count_4xx": 100,
    "count_5xx": 13,
    "avg_ms": 11.2,
    "p99_ms": 88.0,
    "series": [
      { "t": "2026-07-02T10:05:00+08:00", "count": 140, "errors": 2, "avg_ms": 10.1 }
    ],
    "endpoints": [
      { "route": "/v1/status", "count": 5200, "avg_ms": 8.9, "p99_ms": 40.1, "errors": 0 }
    ],
    "keys": [
      { "api_key": "key-1", "count": 8100, "last_seen": "2026-07-02T10:09:41+08:00" }
    ]
  }
}
  • series granularity follows the window: 1h/4h → SAMPLE BY 1m, 1d → 5m, 7d → 1h.
  • endpoints are the top 20 routes by count; route is the FastAPI route template (e.g. /v1/bars/{symbol}).
  • api_key is an identity label — "key-N" / "admin-N" for configured keys, "invalid" for a bad key, "anonymous" when no key was sent (dev mode); raw keys are never stored.
  • The api_requests table keeps 30 days of data (PARTITION BY DAY TTL 30 DAYS); /health and OPTIONS requests are not logged.
Try it
GET/v1/usage/requests

API Request Log

The raw api_requests tail, newest first: one row per API call with the matched route template, actual path, truncated query string, key label, status code, duration and client IP.

Parameters

NameInTypeDescription
limitqueryintegerMaximum number of rows returned (1–500), newest first.default: 100
statusquerystringFilter by status-code class (range match on status_code).one of: all | 2xx | 4xx | 5xxdefault: all
routequerystringExact-match filter on the route template; use values from the summary's endpoints list.
exclude_routesquerystringComma-separated route templates to hide (e.g. the dashboard's own polling). Rows with route=null (unmatched 404s) are always kept.
exclude_keysquerystringComma-separated key labels to hide — e.g. exclude_keys=dash hides the dashboard's own traffic. Keys are named in DATA_API_KEYS as "name:secret".
qquerystringCase-insensitive substring search over path and query string (max 64 chars; letters, digits and /_{}.,=&%?:- only).

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/usage/requests?limit=100&status=4xx&route=%2Fv1%2Fbars%2F%7Bsymbol%7D&exclude_routes=%2Fv1%2Fstatus%2C%2Fv1%2Flive%2F%7Bsymbol%7D&exclude_keys=dash&q=timeframe%3D5m"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "count": 100,
    "results": [
      {
        "ts": "2026-07-02T10:09:41.213+08:00",
        "method": "GET",
        "route": "/v1/bars/{symbol}",
        "path": "/v1/bars/TXFR1",
        "query": "timeframe=5m&session=day",
        "api_key": "key-1",
        "status_code": 200,
        "duration_ms": 12.4,
        "client_ip": "127.0.0.1"
      }
    ]
  }
}
  • route is null when the request matched no FastAPI route (e.g. a scanner probing /admin/login); path always holds the actual request path.
  • query is truncated to 512 characters; ts is the request start time.
  • api_requests is an append-only log with a 30-day TTL; /health and OPTIONS are excluded as probe noise.
Try it
POST/v1/admin/backfill

Trigger Backfill

Queue a backfill run that re-downloads ticks for a symbol over a time range and rebuilds the affected bars. Returns the run id.

Parameters

NameInTypeDescription
symbol*bodystringContract symbol to backfill.
start*bodystring (ISO 8601)Inclusive range start.
end*bodystring (ISO 8601)Exclusive range end.

Request

curl -X POST \
  -H "X-API-Key: $BULLINV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"symbol":"TXFR1","start":"2026-06-10T08:45:00+08:00","end":"2026-06-11T05:00:00+08:00"}' \
  "https://bullinv-data-api.bullinv.app/v1/admin/backfill"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "run_id": "bf-20260630-0912-a41c"
  }
}
  • Runs are idempotent per (symbol, start, end); re-posting the same range returns a new run that no-ops on already-filled bars.
Try it
GET/v1/backfill-runs

Backfill Run History

Execution history of backfill runs (one row per run, latest status). Runs are created by the admin trigger, the seed CLI, or the automatic gap scanner, and progress requested → running → done | failed.

Parameters

NameInTypeDescription
limitqueryintMax runs to return (1–200), newest first.default: 20
statusqueryenumFilter by current status.one of: requested | running | done | failed

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/backfill-runs?limit=20&status=done"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "count": 1,
    "results": [
      {
        "run_id": "9c2f1c40e2a34d0f8b1d6c1c8f0a2b7e",
        "symbol": "TXFR1",
        "range_start": "2026-06-10T08:45:00+08:00",
        "range_end": "2026-06-11T05:00:00+08:00",
        "status": "done",
        "rows_written": 1108,
        "requested_by": "api",
        "error": null,
        "ts": "2026-07-02T18:40:11+08:00"
      }
    ]
  }
}
  • ts is the run's latest status-change time; a run stuck in running for >30 min is reaped and re-executed by the backfill loop.
Try it
POST/v1/admin/keys

Create API Key

簽發 API key:指定 label(usage 歸因用,唯一)、允許端點清單(權限 ID = 本頁端點 ID,["*"] 全通)、可選到期日與每分鐘限流。secret 只在本回應出現一次,之後只存 SHA-256 hash — 遺失要重發。

Parameters

NameInTypeDescription
label*bodystring2-32 字,小寫英數與 '-',字首英數;不可與 env key 名字/既有 label 重複。
perms*bodystring[]允許端點的權限 ID 清單;["*"] = 全通含 admin。
notebodystring備註(128 字內)。
expiresAtbodystring (YYYY-MM-DD)到期日(台北,含當日)。
rateLimitPerMinbodyinteger每分鐘請求上限(1-100000);外發 key 建議必填。

Request

curl -X POST \
  -H "X-API-Key: $BULLINV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"label":"friend-ticks","perms":"[\"get-ticks\", \"get-symbols\"]","note":"朋友要 tick 資料","expiresAt":"2026-10-14","rateLimitPerMin":"120"}' \
  "https://bullinv-data-api.bullinv.app/v1/admin/keys"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "keyId": "3f2a9c81d0b4",
    "label": "friend-ticks",
    "secret": "bq_9f8e7d6c5b4a3928171605f4e3d2c1b0a9988776"
  }
}
  • 需要 admin key。secret 僅此一次;建立後立即生效(不等快取刷新)。
Try it
GET/v1/admin/keys

List API Keys

全部後台簽發的 key(含已撤銷),附最後使用時間與近 7 日請求數(從 api_requests 按 label 聚合)。env 清單的 bootstrap key 不在此列。

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/admin/keys"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "count": 1,
    "keys": [
      {
        "keyId": "3f2a9c81d0b4",
        "label": "friend-ticks",
        "perms": ["get-symbols", "get-ticks"],
        "active": true,
        "note": "朋友要 tick 資料",
        "createdAt": "2026-07-14T10:00:00+08:00",
        "createdBy": "root",
        "expiresAt": "2026-10-15T00:00:00+08:00",
        "rateLimitPerMin": 120,
        "lastSeen": "2026-07-14T11:23:45+08:00",
        "requests7d": 883
      }
    ]
  }
}
Try it
PATCH/v1/admin/keys/{key_id}

Update API Key

編輯 key 的權限/備註/到期日/限流(label 與 secret 不可改——要換名就重發)。欄位語意:沒帶 = 不改、帶 null = 清除(perms 除外,不可清空)。改動立即生效。

Parameters

NameInTypeDescription
key_id*pathstring12 碼 hex(見清單)。
permsbodystring[]新的允許端點清單(整份取代)。
notebodystring | null備註;null 清除。
expiresAtbodystring | nullYYYY-MM-DD;null 清除(改為不過期)。
rateLimitPerMinbodyinteger | null每分鐘上限;null 清除(不限)。

Request

curl -X POST \
  -H "X-API-Key: $BULLINV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"perms":"[\"get-ticks\", \"get-bars\"]","note":"加開 bars","expiresAt":"2026-12-31","rateLimitPerMin":"300"}' \
  "https://bullinv-data-api.bullinv.app/v1/admin/keys/3f2a9c81d0b4"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "keyId": "3f2a9c81d0b4",
    "label": "friend-ticks",
    "perms": ["get-bars", "get-ticks"],
    "note": "加開 bars",
    "expiresAt": "2027-01-01T00:00:00+08:00",
    "rateLimitPerMin": 300
  }
}
  • 已撤銷的 key 不可編輯(400 KEY_REVOKED)——請重發。
Try it
POST/v1/admin/keys/{key_id}/revoke

Revoke API Key

撤銷 key(追加 active=false 事件列,歷史可稽核)。立即生效 — 該 key 的下一個請求就是 401。冪等:重複撤銷回同樣結果。

Parameters

NameInTypeDescription
key_id*pathstring12 碼 hex(見清單)。

Request

curl -X POST \
  -H "X-API-Key: $BULLINV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}' \
  "https://bullinv-data-api.bullinv.app/v1/admin/keys/3f2a9c81d0b4/revoke"

Response

{
  "code": 200,
  "message": "ok",
  "data": { "keyId": "3f2a9c81d0b4", "active": false }
}
Try it
POST/v1/admin/keys/reload

Reload Key Cache

強制刷新 data-api 的 key 快取(平常每 30 秒自動刷新)。別台 worker 簽發的 key 要馬上可用、或撤銷要跨 worker 即時生效時用。

Request

curl -X POST \
  -H "X-API-Key: $BULLINV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}' \
  "https://bullinv-data-api.bullinv.app/v1/admin/keys/reload"

Response

{
  "code": 200,
  "message": "ok",
  "data": { "activeKeys": 4 }
}
Try it
GET/v1/fund/catalog

Tracked Fund Catalog

追蹤中基金清單 + 淨值覆蓋摘要(首尾日期、筆數)。消費端開機對帳用。fund_id 是 Yahoo 代碼(如 F0GBR067HA:FO)——與基富通代碼是兩個空間,對照由消費端維護。

Parameters

NameInTypeDescription
activequerybooleanfalse 時含已停用(untrack)項。default: true

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/fund/catalog?active=true"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "count": 1,
    "funds": [
      {
        "fundId": "F0GBR067HA:FO",
        "name": "安聯收益成長基金AM穩定月收類股(美元)",
        "currency": "USD",
        "currencyRaw": "美元",
        "category": "平衡型",
        "inception": "2005-09-12",
        "rating": "4",
        "active": true,
        "nav": { "firstDate": "2005-09-12", "lastDate": "2026-07-10", "count": 5214 },
        "profileUpdatedAt": "2026-07-13T18:10:22+08:00"
      }
    ]
  }
}
  • catalog 的 currency 是正規化 ISO;/profile 的 currency 則保留 Yahoo 中文原文(凍結合約)。
Try it
GET/v1/fund/nav/{fund_id}

Fund NAV History

單檔基金的日淨值歷史(由舊到新)。change(漲跌額)讀取時相鄰列現算——帶 start 分頁時會先取 start 前一列當基準,首筆的 change 不會因分頁而變 null。未追蹤的 fundId 回 404 FUND_NOT_TRACKED(消費端據此觸發備援 + 自動 track)。

Parameters

NameInTypeDescription
fund_id*pathstringYahoo 基金代碼;無 ':' 時 F 開頭自動補 ':FO'。
startquerystring (YYYY-MM-DD)含當日。
endquerystring (YYYY-MM-DD)含當日。
limitqueryinteger1–20000。default: 20000
orderqueryenumdesc = 最新的 limit 筆。one of: asc | descdefault: asc

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/fund/nav/F0GBR067HA%3AFO?start=2026-01-01&end=2026-07-13&limit=500&order=asc"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "fundId": "F0GBR067HA:FO",
    "rows": [
      { "date": "2026-07-09", "nav": 8.42, "change": -0.03, "changePct": -0.36 },
      { "date": "2026-07-10", "nav": 8.45, "change": 0.03, "changePct": 0.36 }
    ],
    "count": 2,
    "truncated": false,
    "pending": false
  }
}
  • 404 message 精確等於 FUND_NOT_TRACKED——消費端的備援觸發條件,勿改字。
  • pending=true 代表「該基金整體還沒有資料」(剛 track 等回補),不是查詢窗剛好沒資料。
Try it
GET/v1/fund/profile/{fund_id}

Fund Profile

基金基本資料:名稱、幣別、類別、成立日、最新淨值、規模、評等、持股/產業/區域配置等(Yahoo 最新快照)。已追蹤但 profile 尚未抓到時回完整鍵集(值為 null)+ pending=true。

Parameters

NameInTypeDescription
fund_id*pathstringYahoo 基金代碼。

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/fund/profile/F0GBR067HA%3AFO"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "fundId": "F0GBR067HA:FO",
    "name": "安聯收益成長基金AM穩定月收類股(美元)",
    "currency": "美元",
    "currencyNormalized": "USD",
    "category": "平衡型",
    "inception": "2005-09-12",
    "latestPriceDate": "2026-07-10",
    "latestNav": 8.45,
    "topHoldings": [{ "name": "Microsoft", "ticker": "MSFT", "weight": 2.9 }],
    "sectors": [{ "name": "科技", "weight": 31.2 }],
    "pending": false,
    "profileUpdatedAt": "2026-07-13T18:10:22+08:00"
  }
}
  • currency 刻意保留 Yahoo 中文原文(消費端現行程式依中文幣別運作);正規化值在 currencyNormalized。
Try it
POST/v1/fund/track

Track Fund

註冊追蹤(冪等)。無資料時自動排入全歷史回補(fund_backfill_runs),由 fund-nav loop 撿走。對 sitemap 全量層(一天 1 輪)的基金呼叫 = 升級成 priority 層(一天 3 輪)。

Parameters

NameInTypeDescription
fundId*bodystringYahoo 基金代碼。
notebodystring備註(128 字內,中英數與常見標點)。

Request

curl -X POST \
  -H "X-API-Key: $BULLINV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"fundId":"F0GBR067HA:FO","note":"super-funds-trade 對帳"}' \
  "https://bullinv-data-api.bullinv.app/v1/fund/track"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "fundId": "F0GBR067HA:FO",
    "tracked": true,
    "backfillQueued": true
  }
}
  • 限流較嚴(預設 10/min/key)。
Try it
GET/v1/fund/status

Fund Pipeline Status

維運監控:priority 層逐檔新鮮度(庫內最後淨值日 vs vendor 最新日、落後平日數)+ sitemap 全量層彙總 + fund-nav 管線心跳。dash 的 Funds 頁吃這支。

Parameters

NameInTypeDescription
scopequeryenumwatch = priority 明細 + bulk 彙總;all = 全部 ~8,900 檔明細(回應大,除錯用)。one of: watch | alldefault: watch

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/fund/status?scope=watch"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "funds": [
      {
        "fundId": "F0GBR067HA:FO",
        "name": "安聯收益成長基金AM穩定月收類股(美元)",
        "lastNavDate": "2026-07-10",
        "vendorLatestPriceDate": "2026-07-10",
        "navCount": 5214,
        "behindWeekdays": 0
      }
    ],
    "count": 1,
    "bulk": { "count": 8877, "withData": 8402, "noData": 475, "behind": 121 },
    "pipeline": {
      "lastHeartbeat": "2026-07-13T18:11:05+08:00",
      "status": "ok",
      "detail": "{\"snapshot\":{\"funds\":62}}"
    }
  }
}
Try it
GET/v1/fund/fundrich/status

FundRich Scan Status

基富通全目錄掃描管線狀態:最近一輪(含 failed 錯誤)、最近完整輪(done)、目錄覆蓋(檔數 + 掃描時間)。目錄讀取永遠跟著最新 done 輪走,failed 輪不影響供應。

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/fund/fundrich/status"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "lastScan": {
      "scanId": "fdd65599d39e48fd8d9879ec7816d587",
      "status": "done",
      "ts": "2026-07-13T18:14:27+08:00",
      "declaredTotal": 4797,
      "fetched": 4797,
      "pages": 240,
      "requestedBy": "cli",
      "error": null
    },
    "lastDoneScan": { "scanId": "fdd65599d39e48fd8d9879ec7816d587", "status": "done" },
    "catalog": { "count": 4797, "scanTs": "2026-07-13T18:13:00+08:00", "pending": false }
  }
}
Try it
GET/v1/fund/fundrich/catalog

FundRich Full Catalog

基富通全基金清單(最新完整掃描輪,~4,800 檔)——消費端基金篩選器的資料源。fundId 是基富通數字代碼,與 Yahoo 代碼互不相通。raw=true 附上官網原始欄位(data),單次回應約數 MB。

Parameters

NameInTypeDescription
rawquerybooleantrue 時每檔附 data(基富通 FundTableInfo 原始欄位,前端篩選用)。default: false
limitqueryinteger1–5000。default: 5000
offsetqueryinteger分頁位移。default: 0

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/fund/fundrich/catalog?raw=false&limit=5000&offset=0"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "funds": [
      {
        "fundId": "002008",
        "name": "野村台灣運籌基金",
        "company": "野村投信",
        "currency": "TWD",
        "dividendRate": "nothave",
        "risk": "4",
        "morningstar": "5",
        "beginningDate": "2010-01-01"
      }
    ],
    "count": 1,
    "scanTs": "2026-07-13T18:13:00+08:00",
    "pending": false
  }
}
  • pending=true 代表庫內尚無任何完整掃描輪(首次部署、掃描還沒跑完)。
Try it
POST/v1/fund/refresh/{fund_id}

Fund Manual Refresh

手動更新:把已追蹤基金排入全歷史回補佇列(DEDUP 冪等),fund-nav loop 下一輪(≤60 秒)撿走直接向 Yahoo 重抓。給消費端「平台資料落後/缺漏時的手動把手」;與 track 共用限流(預設 10 次/分/key)。

Parameters

NameInTypeDescription
fund_id*pathstringYahoo 基金代碼(未追蹤 → 404 FUND_NOT_TRACKED)。

Request

curl -X POST \
  -H "X-API-Key: $BULLINV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}' \
  "https://bullinv-data-api.bullinv.app/v1/fund/refresh/F0GBR067HA%3AFO"

Response

{
  "code": 200,
  "message": "ok",
  "data": { "fundId": "F0GBR067HA:FO", "queued": true, "alreadyPending": false }
}
  • 已有 requested/running 中的回補單時冪等回覆 alreadyPending=true,不重複排工。
  • 單檔佇列由獨立執行緒服務(≤10 秒撿單)——bulk 全量掃描進行中也即時處理,不被卡。
Try it
POST/v1/fund/bulk-snapshot

Fund Bulk Snapshot Trigger

手動觸發 sitemap 全量層(~8,900 檔)快照一輪,不必等每日排程點。落一張哨兵請求單(fund_id='*');fund-nav loop ≤60 秒撿走,一輪約 1.5–3 小時。admin 權限。

Request

curl -X POST \
  -H "X-API-Key: $BULLINV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}' \
  "https://bullinv-data-api.bullinv.app/v1/fund/bulk-snapshot"

Response

{
  "code": 200,
  "message": "ok",
  "data": { "queued": true, "alreadyPending": false, "runId": "8f21c0de…" }
}
  • 已有 requested/running 中的全量輪哨兵單時冪等回覆 alreadyPending=true。
  • 跑完會把今天的 bulk 排程點標記為已跑——同一天不會再跑排程輪(省請求配額)。
  • 進度看 /v1/fund/status 的 pipeline detail(每 25 檔回報)或 /v1/tasks(kind=fund-backfill、對象顯示「全量輪」)。
  • 單檔回補佇列(refresh/track)由獨立執行緒服務,全量輪執行期間照常即時處理。
Try it
GET/v1/market/summary

Global Market Summary

全球市場總覽(StockQ):20 檔股市指數(亞/歐/美,含 5–260 日均線、偏離、期間報酬)+ 6 檔商品 + 8 檔匯率的最新報價,附近 N 日日頻序列。讀取一律鎖定最新一輪 done quote 掃描(quote 每 30 分、指數統計每日兩輪)。

Parameters

NameInTypeDescription
seriesDaysqueryinteger0–60;每檔附回的日頻序列天數,0 = 不附序列。歷史每日累積,上線初期最多約 20 日。default: 20

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/market/summary?seriesDays=20"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "asOf": "2026-07-31T05:55:06+08:00",
    "scanId": "a19ea6ecf638466e81c2d3b128dd158d",
    "pending": false,
    "indices": [
      {
        "symbol": "SPX",
        "name": "S&P 500",
        "category": "index",
        "region": "americas",
        "price": 7437.63,
        "change": 121.48,
        "changePct": 1.66,
        "localTime": "16:44",
        "ma5": 7402.26,
        "ma20": 7484.77,
        "ma60": 7452.8,
        "deviationPct": 0.06,
        "retYtd": 6.88,
        "ret1y": 14.84,
        "statsAsOf": "2026-07-31T05:54:19+08:00",
        "series": [{ "date": "2026-07-30", "price": 7437.63, "changePct": 1.66 }]
      }
    ],
    "commodities": [
      { "symbol": "GOLD", "name": "黃金", "category": "commodity", "region": "commodity", "price": 4104.6, "change": 37.4, "changePct": 0.92, "localTime": "04:40", "series": [] }
    ],
    "fx": [
      { "symbol": "USDJPY", "name": "美元/日圓", "category": "fx", "region": "fx", "price": 159.443, "change": -3.94, "changePct": -2.41, "localTime": "05:02", "series": [] }
    ]
  }
}
  • pending=true 代表平台尚無完整掃描輪(首次部署)——indices/commodities/fx 為空陣列。
  • changePct / ret* 是百分比數值(1.66 = +1.66%),不是小數。
  • 商品與匯率只有首頁報價(無均線/期間報酬);其日頻序列由 quote 掃描逐日累積。
  • 34 檔標的清單定義於 bullinv-quant-data 的 market/registry.py(單一事實來源)。
Try it
GET/v1/market/history/{symbol}

Global Market History

單一標的的日頻歷史(market_daily 累積表)。指數由 detail 掃描逐日補近 20 日(重掃冪等),商品/匯率由 quote 掃描每日落一筆——歷史每天長高,60 日以上約兩三個月後自然長齊。

Parameters

NameInTypeDescription
symbol*pathstringregistry 內的 canonical symbol(不分大小寫),例:SPX、TWSE、GOLD、USDJPY。
daysqueryinteger1–365;回傳最近 N 筆交易日。default: 60

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/market/history/SPX?days=60"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "symbol": "SPX",
    "name": "S&P 500",
    "category": "index",
    "days": 60,
    "rows": [
      { "date": "2026-07-29", "price": 7316.15, "changePct": -1.61 },
      { "date": "2026-07-30", "price": 7437.63, "changePct": 1.66 }
    ]
  }
}
  • 未知 symbol → HTTP 404、message = SYMBOL_NOT_FOUND。
Try it
GET/v1/market/status

Global Market Scan Status

維運監控:quote 與 detail 兩種掃描的最新輪次(含 failed 錯誤訊息)、日頻累積覆蓋範圍。dash 的 Market 頁吃這支。

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/market/status"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "symbols": 34,
    "detailSymbols": 20,
    "lastQuoteScan": { "scanId": "a19ea6ec…", "status": "done", "asOf": "2026-07-31T05:55:06+08:00", "at": "2026-07-31T05:55:11+08:00", "expected": 34, "fetched": 34, "error": null },
    "lastDetailScan": { "scanId": "30623355…", "status": "done", "asOf": "2026-07-31T05:54:19+08:00", "at": "2026-07-31T05:55:00+08:00", "expected": 20, "fetched": 20, "error": null },
    "dailyRows": 414,
    "oldestDaily": "2026-07-01",
    "newestDaily": "2026-07-31"
  }
}
Try it
GET/v1/stock/daily/{symbol}

Stock Daily History

台股單一個股日線歷史(stock_daily 累積表;每日收盤掃描落全市場一輪,歷史逐日累積)。受限資料域:權限需在 /keys 明確授予,env key 隱含清單不含此端點。

Parameters

NameInTypeDescription
symbol*pathstring台股代碼(上市/上櫃),例:2330、00878。不存在 → 200 空結果。
daysqueryinteger1–2000;回傳最近 N 個交易日(輸出由舊到新)。default: 240

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/stock/daily/2330?days=240"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "symbol": "2330",
    "market": "twse",
    "name": "台積電",
    "count": 2,
    "rows": [
      { "date": "2026-07-31", "open": 990, "high": 1002, "low": 988, "close": 1000, "change": 10, "volume": 18000000, "turnover": 18000000000, "transactions": 30000 },
      { "date": "2026-08-03", "open": 1000, "high": 1010, "low": 995, "close": 1005, "change": 5, "volume": 20000000, "turnover": 20100000000, "transactions": 35000 }
    ]
  }
}
  • volume 單位 = 股;turnover = 成交金額(元)。無成交的價格欄為 null。
  • 來源:TWSE STOCK_DAY_ALL + TPEx mainboard daily(官方 OpenAPI,收盤後 16:30 掃描)。
  • 歷史自掃描上線日起逐日累積;深歷史回補另議(TWSE 逐檔逐月端點)。
Try it
GET/v1/stock/quotes

Stock Latest Quotes

最新交易日的全市場(或指定清單)收盤快照。tradeDate = stock_daily 的 max(ts)。受限資料域。

Parameters

NameInTypeDescription
symbolsquerystring逗號分隔代碼清單,最多 100 檔;不帶 = 全市場(配 limit)。
marketquerystringtwse|tpex。
limitqueryinteger1–3000。default: 100

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/stock/quotes?symbols=2330%2C2317%2C00878&market=twse&limit=100"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "tradeDate": "2026-08-03",
    "count": 1,
    "quotes": [
      { "symbol": "2330", "market": "twse", "name": "台積電", "date": "2026-08-03", "open": 1000, "high": 1010, "low": 995, "close": 1005, "change": 5, "volume": 20000000, "turnover": 20100000000, "transactions": 35000 }
    ]
  }
}
  • 庫內尚無資料(管線剛部署)→ pending=true、quotes=[]。
Try it
GET/v1/stock/status

Stock Scan Status

維運監控:最新一輪台股 EOD 掃描(含 failed 錯誤)與日頻累積覆蓋。受限資料域。

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/stock/status"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "lastScan": { "scanId": "8f21c0de…", "status": "done", "tradeDate": "2026-08-03", "fetched": 1802, "requestedBy": "schedule", "at": "2026-08-03T16:30:12+08:00", "error": null },
    "dailyRows": 3604,
    "symbols": 1802,
    "oldestDaily": "2026-08-01",
    "newestDaily": "2026-08-03"
  }
}
Try it
GET/v1/stock/live

Stock Live Quotes

訂閱中個股的即時報價(盤中 ~5 秒更新;來源 TWSE MIS 官方報價)。訂閱清單制:先 POST /v1/stock/track 加入,worker 60 秒內開始輪詢。受限資料域。

Parameters

NameInTypeDescription
symbolsquerystring逗號分隔,最多 100 檔;不帶 = 全部訂閱中個股。

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/stock/live?symbols=2330%2C00878"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "count": 1,
    "quotes": [
      { "symbol": "2330", "market": "twse", "last": 2320, "open": 2300, "high": 2330, "low": 2295, "prevClose": 2310, "change": 10, "changePct": 0.43, "volume": 35741000, "at": "2026-08-04T14:30:00+08:00" }
    ]
  }
}
  • at = 交易所成交時間;非盤中回傳最後一筆(收盤價)。
  • volume 單位 = 股(MIS 原始為張,已 ×1000)。
  • 逐筆 tick 級(策略執行用)另議 Shioaji 訂閱管線;本端點定位是報價板。
Try it
GET/v1/stock/watchlist

Stock Watchlist

即時報價訂閱清單現況(active 中的個股與其 market/來源/備註)。受限資料域。

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/stock/watchlist"

Response

{
  "code": 200,
  "message": "ok",
  "data": { "count": 1, "watchlist": [ { "symbol": "2330", "market": "twse", "requestedBy": "root", "note": "策略 A", "trackedAt": "2026-08-04T09:00:00+08:00" } ] }
}
Try it
POST/v1/stock/track

Stock Track

把個股加入/移出即時報價訂閱清單(admin)。market 從 stock_daily 自動反查;worker 的輪詢每 60 秒重載清單。

Parameters

NameInTypeDescription
symbol*bodystring台股代碼;stock_daily 查無 → 404 SYMBOL_NOT_FOUND(EOD 掃描涵蓋全市場,新代碼隔日可 track)。
activebodybooleanfalse = 取消訂閱。default: true
notebodystring備註(≤128 字)。

Request

curl -X POST \
  -H "X-API-Key: $BULLINV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"symbol":"2330","active":"true","note":"策略 A 用"}' \
  "https://bullinv-data-api.bullinv.app/v1/stock/track"

Response

{
  "code": 200,
  "message": "ok",
  "data": { "symbol": "2330", "market": "twse", "active": true }
}
Try it
GET/v1/me

Who Am I

呼叫者身分自省:這把 key 的 label、來源(env-api/env-admin/db)與允許的權限 ID 清單(["*"] = 全通)。多前端共用 codebase 的關鍵:前端啟動時打這支,按權限清單決定渲染哪些區塊。

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/me"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "label": "terminal",
    "source": "db",
    "perms": ["get-bars", "get-ticks", "get-symbols", "get-status"],
    "expiresAt": null,
    "rateLimitPerMin": null
  }
}
  • 任何有效 key 都可呼叫(不需在權限清單勾選)。
  • db 來源另附 expiresAt / rateLimitPerMin;env key 無這兩個欄位。
Try it
GET/v1/tasks

Unified Tasks

全平台任務統一視圖:把五張任務表(fund_backfill_runs/backfill_runs/data_jobs/fundrich_scans/market_scans)正規化成同一形狀後聚合輸出,limit/offset 分頁。dash 的 Tasks 頁吃這支。

Parameters

NameInTypeDescription
statusquerystringrequested|running|done|failed。
kindquerystringfund-backfill|bars-backfill|fundamentals-job|fundrich-scan|market-quote-scan|market-detail-scan。
limitqueryinteger1–500;每頁筆數。default: 100
offsetqueryinteger0–10000;分頁位移(合併排序後跳過前 N 筆)。default: 0

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/tasks?status=failed&kind=fund-backfill&limit=100&offset=100"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "tasks": [
      { "id": "8f21c0de…", "kind": "fund-backfill", "target": "F0GBR067HA:FO", "status": "done", "requestedBy": "jsmart", "progress": "900 rows", "error": null, "updatedAt": "2026-07-31T22:41:07+08:00" },
      { "id": "a19ea6ec…", "kind": "market-quote-scan", "target": "StockQ 全球市場", "status": "done", "requestedBy": "worker", "progress": "34/34 檔", "error": null, "updatedAt": "2026-07-31T05:55:11+08:00" }
    ],
    "counts": { "requested": 1, "running": 0, "done": 812, "failed": 3 },
    "total": 816,
    "kinds": ["fund-backfill", "bars-backfill", "fundamentals-job", "fundrich-scan", "market-quote-scan", "market-detail-scan"]
  }
}
  • 唯讀聚合層:不改任何管線的寫入設計,各任務表仍是自己領域的事實來源。
  • 每個來源各取最近 1,000 筆任務的最新狀態(LATEST ON)再合併排序;counts 是未過濾的全來源統計。
  • total = 過濾後的合併總筆數(分頁用);分頁深度受每來源窗口所限,最多五來源合計 5,000 筆。
  • status/kind 過濾在聚合後套用;單一來源讀取失敗(例如表尚未建立)只降級略過,不影響其它來源。
  • updatedAt = 該任務最後一次狀態轉移時間(台北時區 ISO)。
Try it
GET/v1/schedule

Scheduler View

fund-nav worker 的生效排程:每個排程任務的時段/頻率、來源(env 預設或 service_settings 覆寫)、下次執行時間。dash 的 Tasks 頁排程區塊吃這支。

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/v1/schedule"

Response

{
  "code": 200,
  "message": "ok",
  "data": {
    "jobs": [
      { "key": "fund_bulk_snapshot_time", "label": "bulk 全量輪(sitemap 層)", "kind": "times", "value": "10:20", "default": "10:20", "source": "default", "updatedBy": null, "updatedAt": null, "nextRun": "2026-08-04T10:20:00+08:00" },
      { "key": "market_quote_interval_minutes", "label": "市場報價掃描間隔", "kind": "interval-minutes", "value": "30", "default": "30", "source": "default", "updatedBy": null, "updatedAt": null, "nextRun": null, "min": 5, "max": 720 }
    ],
    "appliesWithinSeconds": 60
  }
}
  • 排程真相 = env/Settings 預設 + service_settings 覆寫(事件流,現況取每 key 最新一列)。
  • worker 每輪 loop(60s)重讀生效值——改排程不必重啟服務。
Try it
POST/v1/schedule

Scheduler Update

更新排程覆寫(admin)。部分更新:body 只帶要改的 key;value 空字串/null = 清除覆寫回預設。times 型為逗號分隔 HH:MM(1~6 個),interval 型為整數分鐘。

Parameters

NameInTypeDescription
{key}*bodystring排程 key → 新值的映射(自由組合,可一次改多個)。合法 key 見 GET /v1/schedule 的 jobs[].key。

Request

curl -X POST \
  -H "X-API-Key: $BULLINV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"{key}":"{ \"fund_bulk_snapshot_time\": \"09:00\" }"}' \
  "https://bullinv-data-api.bullinv.app/v1/schedule"

Response

{
  "code": 200,
  "message": "ok",
  "data": { "jobs": [ { "key": "fund_bulk_snapshot_time", "value": "09:00", "source": "override", "updatedBy": "root", "nextRun": "2026-08-04T09:00:00+08:00" } ], "appliesWithinSeconds": 60 }
}
  • worker ≤60 秒套用。新增且當天已過的時段不補跑(標記已跑,隔天起生效)——避免「下午把 bulk 改到早上」瞬間觸發一輪數小時全量。
  • 非法 key / 時間格式 / 間隔範圍 → 400,一項失敗整包不寫。
Try it
GET/health

Health Check

Liveness probe for the data-api process and its QuestDB connection. This is the only endpoint that is not wrapped in the standard envelope and requires no API key.

Request

curl -H "X-API-Key: $BULLINV_API_KEY" \
  "https://bullinv-data-api.bullinv.app/health"

Response

{
  "status": "ok",
  "questdb": "ok"
}
  • No X-API-Key required.
Try it

Questions? Ping #quant-data. Method badges: GET read, POST mutate.