Taiwan Stock API

31 endpoints · Market Data · Fundamentals · Institutional Flow · Other

Last updated 2026-10
Base URL
https://api.blave.org
Auth
api-key + secret-key headers on every request
Access
API plan or data fee
Rate Limit
500 requests / 5 min per key and per IP

Conventions

  • stock_id — TWSE (上市) / TPEx (上櫃) code, e.g. 2330, 0050, 00878, 006208. Letters and digits (dots allowed), ≤ 8 chars, else 400 {"error": "Invalid stock_id"}.
  • start / end — YYYY-MM-DD, inclusive. Unless a block says otherwise they are not format-validated (filtering is a string comparison), so always send YYYY-MM-DD.
  • Upstream quota exhausted → 503 {"error": "Upstream rate limit. Please retry shortly."} — retry later.
  • For many stocks use batch/<data_type> (≤ 50 ids per call); never loop a single-stock endpoint over a universe.

Taiwan Stock List

GET/studio/market/twstock/list
Data From
Snapshot (currently listed securities)
Update
Server cache 24 h (10 min when one market failed to load)
Source
TWSE STOCK_DAY_ALL + TPEx mainboard quotes; company profiles from TWSE t187ap03_L / TPEx mopsfin_t187ap03_O

Parameters

None.

Example

Python
data = requests.get(f"{BASE_URL}/studio/market/twstock/list", headers=headers, timeout=60).json()["data"]# [{"stock_id": "00400A", "name": "主動國泰動能高息", "close": 14.72, "market": "TWSE",#   "industry_code": None, "listing_date": None}, ...]   # 2,392 rows in this response

Response

{"data": [...]}, ~2,400 rows (上市 + 上櫃, incl. ETFs; 興櫃 excluded).

FieldTypeDescription
stock_idstringCode
namestringName
closefloat | nullLatest daily close (null when upstream had none, e.g. halted)
marketstringTWSE (上市) or TPEx (上櫃)
industry_codestring | nullTWSE/TPEx raw numeric 產業別 code, passthrough (not decoded). null for ETFs / non-company securities. Common codes: 15 航運業, 17 金融保險業, 22 生技醫療業, 24 半導體業, 25 電腦及週邊設備業, 26 光電業, 27 通信網路業, 28 電子零組件業, 29 電子通路業, 30 資訊服務業, 31 其他電子業
listing_datestring | nullYYYY-MM-DD; null for ETFs / non-company securities

Errors

Shared errors only (500 if both markets fail).

Notes

  • Use for universe building. When sampling, spread across industries — codes are grouped by sector, so [:N] truncation concentrates in a few sectors.
  • A list noticeably shorter than ~2,400 means one market failed to load — treat as degraded, not as delistings.

Taiwan Stock Info

GET/studio/market/twstock/info/<stock_id>
Data From
Snapshot
Update
Same cache as /list (24 h)
Source
Same as /list

Parameters

stock_idstringpathRequired
Code

Example

Python
info = requests.get(f"{BASE_URL}/studio/market/twstock/info/2330", headers=headers, timeout=30).json()["data"]# {"stock_id": "2330", "name": "台積電", "close": 2410.0, "industry_code": "24", "listing_date": "1994-09-05"}

Response

{"stock_id": "<stock_id>", "data": {...}}; data has the same fields as one /list row.

Errors

StatusBodyWhen
400{"error": "Invalid stock_id"}Format check failed
404{"error": "Stock not found"}Not a currently listed security

Taiwan Stock Daily Price (Raw)

GET/studio/market/twstock/price/<stock_id>
Data From
1994-10-01 — the floor the server asks upstream from. 2330 with no start / end returned 8,069 rows starting at exactly that floor. Codes listed later start later
Update
Daily. Same-day row after the official TWSE/TPEx close publication (~14:00 Taipei); server cache 5 min
Source
FinMind (history); TWSE / TPEx official daily quotes (same-day row)

Parameters

stock_idstringpathRequired
Listed (上市) or OTC (上櫃) stock / ETF code
Allowed Letters/digits (dots allowed), ≤ 8 chars — e.g. 2330, 0050, 00878, 006208
startstringquery
First date, inclusive
Default full historyAllowed YYYY-MM-DD
endstringquery
Last date, inclusive
Default latest availableAllowed YYYY-MM-DD

Example

Python
response = requests.get(f"{BASE_URL}/studio/market/twstock/price/2330", headers=headers, timeout=60)data = response.json()["data"]   # no start/end → full history, 8,069 rows# [{"date": "1994-10-01", "stock_id": "2330", "open": 171.0, "high": 172.0,#   "low": 171.0, "close": 172.0, "spread": 1.0,#   "volume": 1660506, "turnover_value": 284858008, "turnover_count": 684},#  {"date": "1994-10-03", "stock_id": "2330", "open": 172.0, "high": 173.0,#   "low": 171.0, "close": 172.0, "spread": 0.0,#   "volume": 1560000, "turnover_value": 268084000, "turnover_count": 711}, ...]

Response

{"stock_id": "<stock_id>", "data": [ ... ]}, rows ascending by date.

FieldTypeUnitDescription
datestringYYYY-MM-DDTrading day
stock_idstring—Stock code
open / high / low / closefloatTWDUnadjusted prices
spreadfloatTWDChange vs the previous close
volumeintshares (股)Trading volume
turnover_valueintTWDTrading value
turnover_countinttrades (筆)Number of trades

Errors

StatusBodyWhen
400{"error": "Invalid stock_id"}stock_id fails the format check
404{"error": "No data found for stock_id: <stock_id>"}Unknown code / no data upstream
503{"error": "Upstream rate limit. Please retry shortly."}Upstream quota exhausted — retry later

Notes

  • start / end are not format-validated: a malformed value filters by string comparison instead of returning 400, so always send YYYY-MM-DD.
  • Use price_adj for backtesting total return; this endpoint is unadjusted.
  • For many stocks use batch/price (≤ 50 ids per call) instead of looping this endpoint.

Taiwan Stock Daily Price (Forward-Adjusted)

GET/studio/market/twstock/price_adj/<stock_id>
Data From
Same as /price — 1994-10-01 for 2330, same 8,069 rows
Update
Daily, rebuilt when the raw series advances; server cache 5 min
Source
/price series adjusted with FinMind dividend data

Parameters

Same as /price/<stock_id>.

Example

Python
params = {"start": "2020-01-01", "end": "2024-12-31"}data = requests.get(f"{BASE_URL}/studio/market/twstock/price_adj/2330", headers=headers, params=params, timeout=60).json()["data"]

Response

Same shape and fields as /price/<stock_id>; open / high / low / close are adjusted, volume / turnover_* are identical to the raw series.

Errors

Same as /price/<stock_id>.

Notes

  • Forward adjustment (後復權 / 向後調整) for cash and stock dividends: early prices stay as traded; from each ex-dividend date onward prices are multiplied by the cumulative factor, so recent adjusted prices sit above raw. Use for total-return backtests.

Taiwan Stock Real-Time Quote

GET/studio/market/twstock/quote/<stock_id>
Data From
Snapshot only — no history
Update
~10 s (server cache 10 s)
Source
FinMind real-time snapshot

Parameters

stock_idstringpathRequired
Code

Example

Python
data = requests.get(f"{BASE_URL}/studio/market/twstock/quote/2330", headers=headers, timeout=30).json()["data"]# {"open": 2415.0, "high": 2465.0, "low": 2415.0, "close": 2445.0,#  "change_price": -20.0, "change_rate": -0.81, "average_price": 2432.58,#  "volume": 4245, "total_volume": 26403, "amount": 10379025000, "total_amount": 64227410000,#  "yesterday_volume": 27390, "buy_price": 2445.0, "buy_volume": 17,#  "sell_price": 2450.0, "sell_volume": 11, "volume_ratio": 0.96,#  "quote_time": "2026-07-03 14:30:00", "stock_id": "2330", "tick_type": 2}

Response

{"stock_id": "<stock_id>", "data": {...}}; data is a flat object.

FieldTypeDescription
open / high / low / closefloatToday's OHLC so far
change_price / change_ratefloatChange vs previous close (TWD / %)
average_pricefloatAverage traded price
volumeintLatest tick's volume
total_volumeintCumulative volume today
amount / total_amountfloatLatest tick / cumulative traded value
yesterday_volumeintPrevious day's volume
buy_price / buy_volumefloat / intBest bid
sell_price / sell_volumefloat / intBest ask
volume_ratiofloatVolume ratio
quote_timestringYYYY-MM-DD HH:MM:SS — a full timestamp, unlike other endpoints' bare date
stock_idstringCode
tick_typeint0 indeterminate, 1 sell-initiated (賣盤成交), 2 buy-initiated (買盤成交)

Errors

StatusBodyWhen
400{"error": "Invalid stock_id"}Format check failed
404{"error": "No data found for stock_id: <stock_id>"}No quote
503{"error": "Upstream rate limit. Please retry shortly."}Upstream quota exhausted

Notes

  • For intraday price checks only — no history, not for backtesting.

Taiwan Stock Real-Time Quote (Batch)

GET/studio/market/twstock/quote
Data From
Snapshot only — no history
Update
~10 s (server cache 10 s)
Source
FinMind real-time snapshot

Parameters

stock_idsstringqueryRequired
Codes
Allowed Comma-separated, ≤ 50 ids

Example

Python
data = requests.get(f"{BASE_URL}/studio/market/twstock/quote", headers=headers,                    params={"stock_ids": "2330,2317"}, timeout=30).json()["data"]# {"2330": {...}, "2317": {...}}

Response

{"data": {"<stock_id>": {...quote fields...}, ...}}; fields as /quote/<stock_id>. Ids with no quote are absent.

Errors

StatusBodyWhen
400{"error": "stock_ids parameter required"}Missing
400{"error": "stock_ids exceeds max 50 per request"}Too many
400{"error": "Invalid stock_id in list"}A code fails the format check
503{"error": "Upstream rate limit. Please retry shortly."}Upstream quota exhausted

Taiwan Stock Real-Time Quote (All)

GET/studio/market/twstock/quote/all
Data From
Snapshot only — no history
Update
~10 s (server cache 10 s)
Source
FinMind real-time snapshot

Parameters

None.

Example

Python
rows = requests.get(f"{BASE_URL}/studio/market/twstock/quote/all", headers=headers, timeout=30).json()["data"]

Response

{"data": [{...quote fields...}, ...]} (~2,800 rows); fields as /quote/<stock_id>.

Errors

StatusBodyWhen
503{"error": "Upstream rate limit. Please retry shortly."}Upstream quota exhausted

Taiwan Stock Minute-Line OHLCV

GET/studio/market/twstock/minute/ohlcv/<stock_id>/<schema>
Data From
2019-01 (backfilled per stock)
Update
Intraday live bars; official correction after the close
Source
Shioaji (live) + FinMind (official history)

Parameters

stock_idstringpathRequired
Code
Allowed A currently listed TWSE/TPEx code, or TAIEX (加權指數)
schemastringpathRequired
Bar size
Allowed 1m, 5m, 15m, 30m, 60m, 1d
startstringquery
First day
Default end − the schema's max rangeAllowed YYYY-MM-DD
endstringquery
Last day, inclusive
Default today (UTC)Allowed YYYY-MM-DD
adjuststringquery
1 = forward-adjusted (後復權) OHLC, same factors as /price_adj; volume is never adjusted
Default 0Allowed 0, 1, true, false

Max range per request (end − start): 1m 31 d · 5m 62 d · 15m 93 d · 30m 186 d · 60m 365 d · 1d 3650 d.

Example

Python
body = requests.get(f"{BASE_URL}/studio/market/twstock/minute/ohlcv/2330/1m", headers=headers,                    params={"start": "2026-08-06", "end": "2026-08-06"}, timeout=60).json()# {"stock_id": "2330", "schema": "1m", "data": [#   {"close": 2385.0, "high": 2395.0, "low": 2385.0, "open": 2395.0, "ts": "2026-08-06 01:00:00+00:00", "volume": 2540}, ...]}

Response

{"stock_id", "schema", "data": [...]}.

FieldTypeUnitDescription
tsstringUTC ISOBar open time (minute-start label); the 13:30 Taipei bar is the closing auction
open / high / low / closefloatTWDPrices
volumeintlots (張), not sharesVolume. TAIEX bars always carry volume: 0 at every schema — the index has no volume, so none is served; do not read it as a turnover figure

Errors

StatusBodyWhen
400{"error": "Invalid stock_id (must be a listed TWSE/TPEx security)"}Not listed
400{"error": "Invalid schema. Allowed: [...]"}Bad schema
400{"error": "Invalid adjust (use 0 or 1)"}Bad adjust
400{"error": "Invalid date format. Use YYYY-MM-DD"}Malformed date
400{"error": "date_range_too_large", "message": "...", "max_days": <n>}Range over the cap
503{"error": "market data temporarily unavailable"}Store read failure
503{"error": "adjust factors temporarily unavailable"}adjust=1 and factors unavailable — never silently served unadjusted

Notes

  • Coverage is demand-driven: any listed stock can be queried; the first query seeds ~30 recent days and enrolls the stock for live tracking, and deep history (2019-01 →) backfills afterwards. Check minute/ohlcv/symbols before requesting years of history.
  • A range with no data returns 200 with [].
  • A still-forming bar is not returned; use /quote/<stock_id> for the live price.
  • Split long spans into chunks within the per-schema cap.

Minute-Line Covered Symbols

GET/studio/market/twstock/minute/ohlcv/symbols
Data From
Snapshot
Update
Live
Source
Blave minute store

Parameters

None.

Example

Python
covered = requests.get(f"{BASE_URL}/studio/market/twstock/minute/ohlcv/symbols", headers=headers, timeout=30).json()["data"]

Response

{"data": ["2330", "TAIEX", ...]} — ids that already have minute-line data on disk (currently listed stocks plus TAIEX), sorted.

Errors

Shared errors only.

Taiwan Stock 1-Minute Bars (Legacy)

GET/studio/market/twstock/kbar/<stock_id>
Data From
2019-01-01, for ids covered by minute/ohlcv/symbols
Update
Server cache 60 s when the range includes today, else 5 min
Source
Same store as minute/ohlcv (no upstream call)

Parameters

stock_idstringpathRequired
Code
Allowed A currently listed TWSE/TPEx code, or TAIEX
startstringquery
First day
Default today − 6 daysAllowed YYYY-MM-DD
endstringquery
Last day; end − start ≤ 31 days
Default todayAllowed YYYY-MM-DD

Example

Python
data = requests.get(f"{BASE_URL}/studio/market/twstock/kbar/2330", headers=headers,                    params={"start": "2026-08-03", "end": "2026-08-07"}, timeout=60).json()["data"]

Response

{"stock_id", "data": [...]}, ascending.

FieldTypeDescription
datestringYYYY-MM-DD (Taipei)
minutestringHH:MM:SS Taipei, bar start (09:00–13:30)
stock_idstringCode
open / high / low / closefloatTWD
volumeintLots (張)

Errors

StatusBodyWhen
400{"error": "Invalid stock_id (must be a listed TWSE/TPEx security)"}Not listed
400{"error": "Date range exceeds 31 days"}Range too long, or malformed date

Notes

  • Legacy — prefer minute/ohlcv/<stock_id>/1m. A listed stock with no minute data returns [] (this endpoint does not trigger seeding).
  • Today's bars are provisional intraday and replaced by official bars after ~16:10 Taipei.

Taiwan Stock Market Value

GET/studio/market/twstock/market_value/<stock_id>
Data From
2004-01-01
Update
Daily; server cache 5 min
Source
FinMind TaiwanStockMarketValue

Parameters

stock_idstringpathRequired
Code
startstringquery
First date
Default full historyAllowed YYYY-MM-DD
endstringquery
Last date
Default latestAllowed YYYY-MM-DD

Example

Python
data = requests.get(f"{BASE_URL}/studio/market/twstock/market_value/2330", headers=headers,                    params={"start": "2026-01-01"}, timeout=60).json()["data"]

Response

{"stock_id", "data": [{"date", "stock_id", "market_value"}]}; market_value in TWD.

Errors

400 invalid stock_id, 404 no data, 503 upstream quota.

Taiwan Stock Market Value Ranking

GET/studio/market/twstock/market_value/all
Data From
Snapshot (latest published day)
Update
Daily (EOD); server cache 30 min
Source
FinMind TaiwanStockMarketValue, filtered to /list

Parameters

topintquery
Keep only the top N by market value
Default all (~2,400)Allowed 1–3000

Example

Response

{"date": "YYYY-MM-DD", "twse_ex_etf_market_value": int, "data": [...]}, sorted by market value descending.

FieldTypeDescription
datestringAs-of day actually used (latest published; can lag today by a day)
twse_ex_etf_market_valueintTWD. Sum of the market == "TWSE" rows that are not ETFs, on the same as-of day. Whole-market denominator — not reduced by top
data[].rankint1-based rank
data[].stock_idstringCode
data[].namestringName
data[].marketstring"TWSE" (上市) or "TPEx" (上櫃), from the TWSE / TPEx listing rosters
data[].market_valueintTWD
data[].is_etfbooltrue for the ETFs in the ranking (359 of 2,370 rows on 2026-09-17). false means "not in the ETF set", not "confirmed not an ETF": a security FinMind publishes no industry_category for — e.g. REIT 01010T — is false, and it stays inside twse_ex_etf_market_value

Errors

StatusBodyWhen
400{"error": "top must be an integer between 1 and 3000"}Bad top
404{"error": "No recent market value data available"} (or a listing-incomplete message)No recent data
503{"error": "Upstream rate limit. Please retry shortly."}Upstream quota exhausted

Notes

  • Universe is 上市 + 上櫃 + ETF (興櫃 excluded; ETNs have no data). Use this instead of looping /market_value/<stock_id> for "top N by market cap" questions.
  • rank and twse_ex_etf_market_value are different universes. rank is whole-market — 上市 + 上櫃, ETFs included — and did not change when the denominator was added. The denominator is 上市 only and excludes ETFs (FinMind industry_category in {ETF, 上櫃ETF, 上櫃指數股票型基金(ETF)}); REITs and preferred shares are not excluded from it. So market_value / twse_ex_etf_market_value is an index weight only for a row whose market is "TWSE" and which is not an ETF — a TPEx or ETF row over this denominator is not a weight — while rank stays a whole-market rank. Don't read the two as one ranking.
  • market is a listing-board tag, not an ETF flag — is_etf is the ETF flag. Use it for an ETF-free universe. Don't infer ETFs from the stock_id prefix (00 is a market convention rather than a contract, and it misses REITs 01xxxT) and don't re-fetch a classification from FinMind. is_etf is exactly the criterion twse_ex_etf_market_value is computed on, so an is_etf filter and the denominator can never disagree.

Taiwan Stock PER / PBR / Dividend Yield

GET/studio/market/twstock/per/<stock_id>
Data From
2005-10-01
Update
Daily; same-day row from TWSE / TPEx the same afternoon; server cache 5 min
Source
FinMind TaiwanStockPER; TWSE BWIBBU_d / TPEx pera_result (same day)

Parameters

stock_idstringpathRequired
Code
startstringquery
First date
Default full historyAllowed YYYY-MM-DD
endstringquery
Last date
Default latestAllowed YYYY-MM-DD

Example

Python
data = requests.get(f"{BASE_URL}/studio/market/twstock/per/2330", headers=headers,                    params={"start": "2026-01-01", "end": "2026-07-22"}, timeout=60).json()["data"]# [{"date": "2026-07-21", "stock_id": "2330", "dividend_yield": 0.95, "PER": 34.87, "PBR": 11.05}, ...]

Response

{"stock_id", "data": [...]}.

FieldTypeDescription
datestringTrading day
stock_idstringCode
dividend_yieldfloatDividend yield (%)
PERfloatPrice / earnings
PBRfloatPrice / book

Errors

400 invalid stock_id, 404 no data, 503 upstream quota.

Notes

  • For value screens across many stocks use batch/per.

Taiwan Stock Income Statement

GET/studio/market/twstock/financials/<stock_id>
Data From
2013-01-01 is only the default start. Send an earlier start and earlier rows come back — 2330 with start=2000-01-01 returned rows from 2000-06-30
Update
Quarterly; server cache 24 h
Source
FinMind TaiwanStockFinancialStatements

Parameters

stock_idstringpathRequired
Code
startstringquery
First date
Default 2013-01-01Allowed YYYY-MM-DD
endstringquery
Last date
Default todayAllowed YYYY-MM-DD

Example

Python
import pandas as pddata = requests.get(f"{BASE_URL}/studio/market/twstock/financials/2330", headers=headers,                    params={"start": "2022-01-01", "end": "2024-12-31"}, timeout=30).json()["data"]# [{"date": "2022-03-31", "stock_id": "2330", "type": "Revenue", "value": 491075000000.0, "origin_name": "營業收入"}, ...]wide = pd.DataFrame(data).pivot_table(index="date", columns="type", values="value", aggfunc="first")

Response

{"stock_id", "data": [...]}, long format (one row per item per quarter).

FieldTypeDescription
datestringQuarter end (03-31 / 06-30 / 09-30 / 12-31)
stock_idstringCode
typestringItem code, e.g. Revenue, GrossProfit, OperatingIncome, NetIncome, EPS, TAX, OtherComprehensiveIncome
valuefloatValue (TWD; EPS in TWD per share)
origin_namestringChinese label — use it to identify unfamiliar type codes

Errors

400 invalid stock_id, 404 no data, 503 upstream quota.

Taiwan Stock Balance Sheet

GET/studio/market/twstock/balance_sheet/<stock_id>
Data From
2013-01-01 is only the default start. Send an earlier start and earlier rows come back — 2330 with start=2000-01-01 returned rows from 2012-12-31
Update
Quarterly; server cache 24 h
Source
FinMind TaiwanStockBalanceSheet

Parameters

Same as /financials/<stock_id>.

Example

Python
data = requests.get(f"{BASE_URL}/studio/market/twstock/balance_sheet/2330", headers=headers,                    params={"start": "2022-01-01"}, timeout=30).json()["data"]

Response

Same long format as /financials/<stock_id>. Key type codes: CashAndCashEquivalents, TotalAssets, TotalLiabilities, TotalEquity. Codes ending in _per are % of total assets.

Errors

400 invalid stock_id, 404 no data, 503 upstream quota.

Taiwan Stock Cash Flow Statement

GET/studio/market/twstock/cashflow/<stock_id>
Data From
2013-01-01 is only the default start. Send an earlier start and earlier rows come back — 2330 with start=2000-01-01 returned rows from 2012-03-31
Update
Quarterly; server cache 24 h
Source
FinMind TaiwanStockCashFlowsStatement

Parameters

Same as /financials/<stock_id>.

Example

Python
import pandas as pddata = requests.get(f"{BASE_URL}/studio/market/twstock/cashflow/2330", headers=headers,                    params={"start": "2022-01-01"}, timeout=30).json()["data"]cfo = (pd.DataFrame(data).query("type == 'CashFlowsFromOperatingActivities'")         .assign(date=lambda d: pd.to_datetime(d["date"])).set_index("date")["value"].sort_index())cfo_q = cfo - cfo.groupby(cfo.index.year).shift(1).fillna(0)   # YTD → single quarter

Response

Same long format as /financials/<stock_id>. Main type codes (FinMind's own codes; the full set varies by company and year, so treat the response as the authoritative list and use origin_name for anything not below). Filter on type, not origin_name — the same code's label switches between full- and half-width brackets across years:

typeorigin_name
CashFlowsFromOperatingActivities營業活動之淨現金流入(流出)
CashProvidedByInvestingActivities投資活動之淨現金流入(流出)
CashFlowsProvidedFromFinancingActivities籌資活動之淨現金流入(流出)
PropertyAndPlantAndEquipment取得不動產、廠房及設備 (capex, negative)
Depreciation / AmortizationExpense折舊費用 / 攤銷費用
CashBalancesIncrease本期現金及約當現金增加(減少)數
CashBalancesBeginningOfPeriod / CashBalancesEndOfPeriod期初(年初)/ 期末現金及約當現金餘額

Flow items are year-to-date (YTD) cumulative, as filed in Taiwan: the 03-31 row is Q1 alone, 06-30 is Q1–Q2, 09-30 is Q1–Q3, 12-31 is the full year. For a single quarter subtract the previous quarter of the same year (Q1 needs no subtraction) — so fetch from a January start. CashBalancesEndOfPeriod is the balance at the quarter end; CashBalancesBeginningOfPeriod is the balance at the start of the year. This endpoint has no period parameter — it always returns YTD (the ?period=quarter single-quarter view exists only on the web chart route, not here).

Errors

400 invalid stock_id, 404 no data, 503 upstream quota.

Taiwan Stock Monthly Revenue

GET/studio/market/twstock/monthly_revenue/<stock_id>
Data From
2000-01-01
Update
Monthly; server cache 24 h
Source
FinMind TaiwanStockMonthRevenue (passed through unchanged)

Parameters

stock_idstringpathRequired
Code
startstringquery
First date
Default 2000-01-01Allowed YYYY-MM-DD
endstringquery
Last date
Default todayAllowed YYYY-MM-DD

Example

Python
import pandas as pddata = requests.get(f"{BASE_URL}/studio/market/twstock/monthly_revenue/2330", headers=headers,                    params={"start": "2024-01-01", "end": "2024-12-31"}, timeout=30).json()["data"]df = pd.DataFrame(data).sort_values(["revenue_year", "revenue_month"])df["yoy_pct"] = df["revenue"].pct_change(periods=12) * 100# [{"date": "2024-01-01", "stock_id": "2330", "country": "Taiwan", "create_time": "",#   "revenue": 176299866000, "revenue_year": 2023, "revenue_month": 12},#  {"date": "2024-02-01", "stock_id": "2330", "country": "Taiwan", "create_time": "",#   "revenue": 215785127000, "revenue_year": 2024, "revenue_month": 1}, ...]

Response

{"stock_id", "data": [...]}, one row per month.

FieldTypeDescription
datestringFirst day of the month after the revenue month — 2024-01-01 carries revenue_year: 2023, revenue_month: 12. Never use it as the period
stock_idstringCode
countrystringMarket — observed value is Taiwan
create_timestringObserved empty ("") on every row
revenueintMonthly revenue in TWD (元), not thousands — inferred from magnitude, not stated upstream: 2330 for revenue_year 2024, revenue_month 1 returned 215785127000, which is only a sane figure read as 元 (NT$215.8bn)
revenue_monthintRevenue month (1–12) — use this, not date, for the period
revenue_yearintRevenue year

Errors

400 invalid stock_id, 404 no data, 503 upstream quota.

Taiwan Stock Financial Ratios

GET/studio/market/twstock/ratios/<stock_id>
Data From
2013-03-31 (2013 Q1) — a hard floor; an earlier start returns nothing older
Update
Quarterly; recomputed by the same job that refreshes the statements (runs twice daily, 13:30 and 23:00 UTC); server cache 6 h
Source
Derived by Blave from financials / balance_sheet / cashflow (FinMind)

Parameters

stock_idstringpathRequired
Code
startstringquery
First quarter-end date
Default 2013-01-01Allowed YYYY-MM-DD
endstringquery
Last quarter-end date
Default todayAllowed YYYY-MM-DD

Example

Python
import pandas as pddata = requests.get(f"{BASE_URL}/studio/market/twstock/ratios/2330", headers=headers,                    params={"start": "2024-01-01"}, timeout=30).json()["data"]# [..., {"date": "2026-06-30", "stock_id": "2330",#   "gross_margin_q": 67.7207, "gross_margin_ttm": 64.2302, "operating_margin_q": 60.3443, "operating_margin_ttm": 56.1009,#   "net_margin_q": 55.6354, "net_margin_ttm": 50.3717, "roe_q": 11.3934, "roe_ttm": 40.3342, "roa_q": 7.8372, "roa_ttm": 27.3074,#   "eps_q": 27.25, "eps_ttm": 86.28, "eps_yoy_q": 77.4089, "eps_yoy_ttm": 53.2232,#   "revenue_yoy_q": 36.0453, "revenue_yoy_ttm": 30.5567, "revenue_qoq_q": 12.0163, "revenue_qoq_ttm": 8.2017,#   "debt_ratio": 30.9438, "current_ratio": 245.7635, "quick_ratio": 225.0114, "equity_multiplier": 1.4481,#   "interest_coverage_q": 280.5515, "interest_coverage_ttm": 227.0232,#   "inventory_turnover_q": 1.1767, "inventory_turnover_ttm": 4.6058, "cfo_to_net_income_q": 1.1084, "cfo_to_net_income_ttm": 1.1779,#   "fcf_q": 287363030000.0, "fcf_ttm": 1143556366000.0, "fcf_per_share_q": 11.0827, "fcf_per_share_ttm": 44.1036, ...}]df = pd.DataFrame(data).assign(date=lambda d: pd.to_datetime(d["date"])).set_index("date")

Response

{"stock_id", "data": [...]}, wide format: one row per quarter, oldest first, one column per ratio. Every ratio is a float rounded to 4 decimals, or null.

FieldTypeDescription
datestringQuarter end (03-31 / 06-30 / 09-30 / 12-31) — not the day the filing became public
stock_idstringCode
<ratio>_qfloat | nullThe quarter alone
<ratio>_ttmfloat | nullTrailing four quarters (the four single quarters summed); null if any of the four is missing
debt_ratio, current_ratio, quick_ratio, equity_multiplierfloat | nullPeriod-end balance-sheet ratios — no suffix

<ratio> (each has both _q and _ttm):

GroupRatiosUnit
Profitabilitygross_margin, operating_margin, pretax_margin, net_margin, opex_ratio, roe, roa% (already ×100)
Per shareeps, revenue_per_share, fcf_per_shareTWD per share
Growthrevenue_yoy, revenue_qoq, operating_income_yoy, net_income_yoy, eps_yoy% (already ×100)
Structureinterest_coveragetimes
Efficiencyreceivable_turnover, inventory_turnover, asset_turnover, cfo_to_net_incometimes
Cash flowfcfTWD, raw amount (not thousands)

No-suffix columns: debt_ratio, current_ratio, quick_ratio are % (already ×100 — a current ratio of 2.46× comes back as 245.7635); equity_multiplier is times.

Errors

StatusBodyWhen
400{"error": "Invalid stock_id"}Code fails the format check
404{"error": "No data found for stock_id: <id>"}No financial statements for the code (ETFs such as 0050 / 00878), or no row inside start–end
503{"error": "Data temporarily unavailable. Please retry shortly."}Ratio store not ready — retry later, never read it as "no data"

Notes

  • Single-quarter ROE / ROA are not annualised; per-share figures use a share count derived from net income ÷ EPS; banks, securities firms and insurers have several ratios that are always null (no gross margin, current ratio, etc.).
  • Rows are stamped with the quarter end, but a quarter's filing is public only weeks later. In a backtest shift each row to its availability date (the statutory filing deadlines, same as the statements) — joining on date is lookahead.
  • A missing line item or a zero denominator gives null, never 0.
  • Not included: PER / PBR / dividend yield (daily — per/<stock_id>) and the monthly-revenue ratios (compute YoY / MoM from monthly_revenue/<stock_id>).
  • Many stocks → GET /studio/market/twstock/batch/ratios?stock_ids=... (≤ 50 ids per call).

Taiwan Stock Dividend Events

GET/studio/market/twstock/dividend/<stock_id>
Data From
Per stock — 2330 with no start / end returned 43 rows, earliest effective date 2005-06-19
Update
Refreshed on request when stale
Source
FinMind dividend data

Parameters

stock_idstringpathRequired
Code
Allowed Currently listed code (4–6 digits, optional trailing letter)
startstringquery
First effective date
Default full historyAllowed strict YYYY-MM-DD
endstringquery
Last effective date
Default full historyAllowed strict YYYY-MM-DD

Range filtering uses a three-tier effective date: cash_ex_date if set, else stock_ex_date, else record_date — freshly announced events without an ex date still show up.

Example

Python
data = requests.get(f"{BASE_URL}/studio/market/twstock/dividend/2330", headers=headers,                    params={"start": "2025-01-01", "end": "2025-12-31"}, timeout=30).json()["data"]# [{"record_date": "2025-03-24", "period": "113年第3季", "announce_date": "2025-03-03",#   "cash_ex_date": "2025-03-18", "stock_ex_date": "", "pay_date": "2025-04-10",#   "cash": 4.50002042, "stock": 0.0, "stock_ratio": 0.0}, ...]

Response

{"stock_id", "data": [...]}, one row per announcement row.

FieldTypeDescription
record_datestring權利分派基準日 YYYY-MM-DD
periodstring股利所屬期間 — an opaque label (114年第3季, 113, 不適用, …); compare as string, do not parse into a year
announce_datestring公告日; "" when unknown
cash_ex_datestring除息交易日; "" when none / not decided
stock_ex_datestring除權交易日; "" when none
pay_datestring現金股利發放日; "" when unknown
cashfloatCash dividend per share (TWD, 盈餘 + 公積)
stockfloatStock dividend per share (元面額, 盈餘 + 公積)
stock_ratiofloatstock / 10 — split-style adjustment ratio

Errors

StatusBodyWhen
400{"error": "Invalid stock_id"}Format check failed
400{"error": "Invalid start date, expected YYYY-MM-DD"} (or end)Malformed date
404{"error": "No data found for stock_id: <id>"} / {"error": "No dividend records for stock_id: <id>"}Not listed, or no dividend history at all
503{"error": "Upstream rate limit. Please retry shortly."}Upstream quota exhausted

Notes

  • Zero-value rows (cash == 0 and stock == 0) are kept — an announced no-distribution decision.
  • Effective dates can be in the future (announced but not yet ex) — the 2330 full-history call ran to a date a week ahead of the request. Filter by date yourself if you need past events only.
  • A valid stock with history but no events in range returns 200 + [] (distinct from 404).
  • Delisted stocks are 404.

Taiwan Stock News

GET/studio/market/twstock/news/<stock_id>
Data From
Upstream coverage begins around mid-2020 (rows found as early as 2020-08 on spot checks; the true start is not documented) and is sparse — a few articles a day or none — until about 2021-02; earlier dates may return []
Update
Fetched per day on first request; server cache 5 min
Source
FinMind TaiwanStockNews

Parameters

stock_idstringpathRequired
Code
startstringquery
First day
Default today − 6 daysAllowed YYYY-MM-DD
endstringquery
Last day; end − start ≤ 31 days
Default todayAllowed YYYY-MM-DD

Example

Python
data = requests.get(f"{BASE_URL}/studio/market/twstock/news/2330", headers=headers,                    params={"start": "2026-08-01", "end": "2026-08-07"}, timeout=60).json()["data"]

Response

{"stock_id", "data": [...]}, several articles per day (weekends included).

FieldTypeDescription
datestringPublish datetime
stock_idstringCode
titlestringHeadline
sourcestringPublisher
linkstringURL

Errors

StatusBodyWhen
400{"error": "Date range exceeds 31 days"} or a date-parse messageRange too long / malformed date
503{"error": "Upstream rate limit. Please retry shortly."}Upstream quota exhausted

Taiwan Stock Institutional Investors

GET/studio/market/twstock/institutional/<stock_id>
Data From
Per stock — 2330 with no start / end returned 3,523 daily rows from 2012-05-02
Update
Daily; same-day row from TWSE / TPEx when available; server cache 5 min
Source
FinMind TaiwanStockInstitutionalInvestorsBuySell; TWSE / TPEx official (same day)

Parameters

stock_idstringpathRequired
Code
startstringquery
First date
Default full historyAllowed YYYY-MM-DD
endstringquery
Last date
Default latestAllowed YYYY-MM-DD

Example

Python
data = requests.get(f"{BASE_URL}/studio/market/twstock/institutional/2330", headers=headers,                    params={"start": "2024-01-01", "end": "2024-12-31"}, timeout=60).json()["data"]# [{"date": "2024-01-02", "stock_id": "2330", "foreign_buy": 28464159, "foreign_sell": 47404324,#   "trust_buy": 5553520, "trust_sell": 269712, "dealer_self_buy": 452000, "dealer_self_sell": 366190,#   "dealer_hedge_buy": 942546, "dealer_hedge_sell": 780090,#   "foreign_dealer_self_buy": 0, "foreign_dealer_self_sell": 0}, ...]

Response

{"stock_id", "data": [...]}, wide format, one row per day. All values in shares (股).

FieldDescription
date, stock_idDay, code
foreign_buy / foreign_sell外資 (excl. foreign dealers)
trust_buy / trust_sell投信
dealer_self_buy / dealer_self_sell自營商自行買賣
dealer_hedge_buy / dealer_hedge_sell自營商避險
foreign_dealer_self_buy / foreign_dealer_self_sell外資自營商 (mostly 0)

Errors

400 invalid stock_id, 404 no data, 503 upstream quota.

Notes

  • Net buy = *_buy − *_sell.
  • Per-stock layer — no bucketing. These are the raw per-category columns; how you group them is your call. Blave's own per-stock surfaces (web, app, AI chat) count 外資自營商 as part of 外資, matching the exchanges' own column naming, and treat 自營商 as 自行買賣 plus 避險. The whole-market endpoint (GET /studio/market/twmarket/institutional) groups it differently — see that section.

Taiwan Stock Margin Trading

GET/studio/market/twstock/margin/<stock_id>
Data From
Per stock — 2330 with no start / end returned 6,323 daily rows from 2001-01-05
Update
Daily; server cache 5 min
Source
FinMind TaiwanStockMarginPurchaseShortSale

Parameters

stock_idstringpathRequired
Code
startstringquery
First date
Default full historyAllowed YYYY-MM-DD
endstringquery
Last date
Default latestAllowed YYYY-MM-DD

Example

Python
import pandas as pddata = requests.get(f"{BASE_URL}/studio/market/twstock/margin/2330", headers=headers,                    params={"start": "2024-01-01"}, timeout=60).json()["data"]df = pd.DataFrame(data)df["margin_util"] = df["margin_balance"] / df["margin_limit"]   # 融資使用率

Response

{"stock_id", "data": [...]}, one row per day. Values are lots (張), not shares — verified against TWSE MI_MARGN: for 2026-09-15 2330 returned margin_balance: 29282 and margin_limit: 6483092, matching TWSE's own 今日餘額 29,282 and 次一營業日限額 6,483,092 for that date exactly. Every row carries its own date and stock_id; when reading full history (no start / end) take every figure from the same row — margin_limit is re-published per day and changes over time.

FieldDescription
margin_buy / margin_sell融資買進 / 賣出
margin_cash_repay融資現金償還
margin_balance / margin_prev_balance融資今日 / 前日餘額
margin_limit融資限額 — TWSE's 次一營業日限額 as published on that row's date, i.e. the cap that applies to the NEXT business day (2330 on 2026-09-15: 6,483,092 lots)
short_buy / short_sell融券買進 / 賣出
short_cash_repay融券現金償還
short_balance / short_prev_balance融券今日 / 前日餘額
short_limit融券限額
offset_loan_short資券相抵

Errors

400 invalid stock_id, 404 no data, 503 upstream quota.

Taiwan Stock Shareholding Distribution

GET/studio/market/twstock/shareholding/<stock_id>
Data From
Per stock — 2330 with no start / end returned 10,656 rows across 649 weekly dates, earliest 2010-01-29
Update
Weekly (TDCC, Fridays); server cache 5 min
Source
FinMind TaiwanStockHoldingSharesPer

Parameters

stock_idstringpathRequired
Code
startstringquery
First date
Default full historyAllowed YYYY-MM-DD
endstringquery
Last date
Default latestAllowed YYYY-MM-DD

Example

Python
data = requests.get(f"{BASE_URL}/studio/market/twstock/shareholding/2330", headers=headers,                    params={"start": "2024-01-01", "end": "2024-03-31"}, timeout=60).json()["data"]

Response

{"stock_id", "data": [...]}, one row per date × level.

FieldTypeDescription
datestringWeek date
stock_idstringCode
levelstringHolding bracket: 1-999, 1,000-5,000, 5,001-10,000, 10,001-15,000, 15,001-20,000, 20,001-30,000, 30,001-40,000, 40,001-50,000, 50,001-100,000, 100,001-200,000, 200,001-400,000, 400,001-600,000, 600,001-800,000, 800,001-1,000,000, more than 1,000,001, total, 差異數調整(說明4)
peopleintNumber of holders
unitintShares held
percentfloat% of issued shares

Errors

400 invalid stock_id, 404 no data, 503 upstream quota.

Taiwan Stock Foreign Shareholding

GET/studio/market/twstock/foreign_shareholding/<stock_id>
Data From
2013-01-01 is only the default start. Send an earlier start and earlier rows come back — 2330 with start=2000-01-01 returned rows from 2004-02-12
Update
Daily; server cache 24 h
Source
FinMind TaiwanStockShareholding

Parameters

stock_idstringpathRequired
Code
startstringquery
First date
Default 2013-01-01Allowed YYYY-MM-DD
endstringquery
Last date
Default todayAllowed YYYY-MM-DD

Example

Python
data = requests.get(f"{BASE_URL}/studio/market/twstock/foreign_shareholding/2330", headers=headers,                    params={"start": "2026-01-01"}, timeout=60).json()["data"]

Response

{"stock_id", "data": [...]}, one row per day.

FieldDescription
date, stock_idDay, code
ForeignInvestmentSharesShares held by foreign investors
ForeignInvestmentSharesRatioForeign holding ratio (%)
ForeignInvestmentRemainingSharesRemaining shares foreigners may buy
ForeignInvestmentRemainRatioRemaining ratio (%)
ForeignInvestmentUpperLimitRatioForeign holding cap (%)
ChineseInvestmentUpperLimitRatioPRC-investor holding cap (%)
NumberOfSharesIssuedShares issued

Errors

400 invalid stock_id, 404 no data, 503 upstream quota.

Taiwan Stock Government Bank Buy/Sell

GET/studio/market/twstock/gov_bank/<stock_id>
Data From
2021-06-30
Update
Daily; server cache 5 min
Source
FinMind TaiwanStockGovernmentBankBuySell

Parameters

stock_idstringpathRequired
Code
startstringquery
First day
Default today − 6 daysAllowed YYYY-MM-DD
endstringquery
Last day; end − start ≤ 31 days
Default todayAllowed YYYY-MM-DD

Example

Python
data = requests.get(f"{BASE_URL}/studio/market/twstock/gov_bank/2330", headers=headers,                    params={"start": "2026-08-01", "end": "2026-08-07"}, timeout=60).json()["data"]

Response

{"stock_id", "data": [...]}, up to 8 rows per day (one per bank), sorted by date, bank.

FieldDescription
date, stock_idDay, code
bank_nameBank
buy / sellShares bought / sold
buy_amount / sell_amountTWD bought / sold

Errors

StatusBodyWhen
400{"error": "Date range exceeds 31 days"} or a date-parse messageRange too long / malformed date
503{"error": "Upstream rate limit. Please retry shortly."}Upstream quota exhausted

Taiwan Stock Securities Lending

GET/studio/market/twstock/lending/<stock_id>
Data From
2001-05-01
Update
Daily; server cache 5 min
Source
FinMind TaiwanStockSecuritiesLending

Parameters

stock_idstringpathRequired
Code
startstringquery
First date
Default full historyAllowed YYYY-MM-DD
endstringquery
Last date
Default latestAllowed YYYY-MM-DD

Example

Python
data = requests.get(f"{BASE_URL}/studio/market/twstock/lending/2330", headers=headers,                    params={"start": "2026-08-01"}, timeout=60).json()["data"]

Response

{"stock_id", "data": [...]}, several rows per day.

FieldDescription
date, stock_idDay, code
transaction_type競價 / 議借
volumeVolume
fee_rateLending fee rate
closeClose price
original_return_dateOriginal return date
original_lending_periodOriginal lending period

Errors

400 invalid stock_id, 404 no data, 503 upstream quota.

Broker Buy/Sell by Stock

GET/studio/market/twstock/broker/stock/<stock_id>
Data From
2021-06-30
Update
Daily, after ~21:30 Taipei; server cache 5 min
Source
FinMind TaiwanStockTradingDailyReport (whole-market daily file)

Parameters

Send date for one day, or start (+ end) for a range; start wins if both are sent.

stock_idstringpathRequired
Code
datestringquery
Single day
Default todayAllowed YYYY-MM-DD
startstringquery
Range start
Allowed YYYY-MM-DD
endstringquery
Range end; range ≤ 366 days inclusive
Default startAllowed YYYY-MM-DD

Example

Python
rows = requests.get(f"{BASE_URL}/studio/market/twstock/broker/stock/2330", headers=headers,                    params={"start": "2026-08-03", "end": "2026-08-07"}, timeout=60).json()["data"]

Response

{"stock_id", "data": [...]}, one row per branch per day.

FieldTypeDescription
datestringTrading day
stock_idstringCode
broker_idstringBranch code (may be alphanumeric, e.g. 920A)
broker_namestringBranch name
pricefloatVolume-weighted average traded price
buy / sellintShares bought / sold

Errors

StatusBodyWhen
400{"error": "Invalid stock_id"}Format check failed
400{"error": "Invalid date (YYYY-MM-DD)"}Malformed date
400{"error": "start/end range is <n> days; max 366 (inclusive) — split into smaller ranges"}Range too long
503{"error": "Upstream rate limit. Please retry shortly."}Data temporarily unavailable — retry; do not read as "no trades"

Notes

  • A day with no data (holiday, before 2021-06-28, or today before ~21:30) is 200 + [].
  • Range queries are live: start / end over 2026-09-06 → 2026-09-13 returned rows spanning five distinct trading days, for both broker/stock/<stock_id> and broker/trader/<trader_id>.

Broker Buy/Sell by Branch

GET/studio/market/twstock/broker/trader/<trader_id>
Data From
2021-06-30
Update
Daily, after ~21:30 Taipei; server cache 5 min
Source
FinMind TaiwanStockTradingDailyReport (whole-market daily file)

Parameters

Same date / start / end rules as /broker/stock/<stock_id>.

trader_idstringpathRequired
Branch code from /broker/search
Allowed Letters, digits, - (e.g. 9217, 920A)
datestringquery
Single day
Default todayAllowed YYYY-MM-DD
startstringquery
Range start
Allowed YYYY-MM-DD
endstringquery
Range end; range ≤ 366 days inclusive
Default startAllowed YYYY-MM-DD

Example

Python
rows = requests.get(f"{BASE_URL}/studio/market/twstock/broker/trader/9217", headers=headers,                    params={"date": "2026-08-07"}, timeout=60).json()["data"]

Response

{"trader_id", "data": [...]}, one row per stock per day; fields date, broker_id, broker_name, stock_id, price, buy, sell (as /broker/stock).

Errors

As /broker/stock/<stock_id>, with 400 {"error": "Invalid trader_id"} for a bad code.

Taiwan Stock Attention & Disposition Announcements

GET/studio/market/twstock/alerts
Data From
TWSE disposals and notices 2001-01; TPEx notices 2002-02; TPEx disposals 2003-09
Update
Store refreshed 17:45, 21:30 and 07:00 Taipei; a TPEx evening announcement may first appear in the 07:00 run
Source
TWSE (臺灣證券交易所) and TPEx (證券櫃檯買賣中心) announcements

Parameters

Give date, or start / end, or neither (= today, Taipei).

datestringquery
One day: disposals in force that day, notices announced that day
Allowed strict YYYY-MM-DD; not with start / end
startstringquery
First day of the range
Default 2001-01-01 with stock_id, else end − 365 daysAllowed strict YYYY-MM-DD
endstringquery
Last day of the range (inclusive)
Default today (or start if later)Allowed strict YYYY-MM-DD
kindstringquery
Announcement type
Default bothAllowed disposal (處置), notice (注意)
marketstringquery
Exchange
Default bothAllowed twse, tpex
stock_idstringquery
One code; lifts the 366-day cap
Default allAllowed See category conventions

Range semantics: a disposal is returned when its period (start–end) overlaps the range, a notice when its announcement date is inside it. Without stock_id the range is at most 366 days inclusive — split longer whole-market ranges (one calendar year per call fits). Unlike the category default, start / end / date are strictly validated.

Example

Response

{"start", "end", "data": [...]}; start / end echo the resolved range (both equal date when date was sent). Rows sorted by date, market, kind, stock_id. One row per announcement.

FieldTypeDescription
datestringAnnouncement day YYYY-MM-DD (a disposal takes effect the next trading day)
stock_idstringCode — includes warrants, ETFs and convertible bonds (e.g. 037060, 45402)
stock_namestring | nullName as published
marketstringtwse (上市) or tpex (上櫃)
kindstringdisposal or notice
countintThe exchange's cumulative count (累計 / 累計次數)
start, endstring | nullDisposal period, both days included; null on notices
conditionstring | nullDisposal: the trigger as published (連續三次, 連續3個營業日, 最近十個營業日已有六次 …). Notice: the clauses cited (第一款、第五款); null if none
measurestring | nullDisposal type: 第一次處置, 第二次處置, 再次處置, 人工管制撮合, 收足五成款券 …; null on notices and on early TPEx rows
match_minutesint | nullDisposal: N of 「約每N分鐘撮合一次」 read from the text; null when the text names none (every TWSE disposal before ~2011 — its text is blank — and non-matching measures) and on notices
reasonstring | nullFull announcement text; null where the exchange published none (every TWSE disposal before 2011-03) (disposal 處置內容, notice 注意交易資訊)
close, pefloat | nullClose and P/E on the announcement; always null on TWSE disposals

Errors

StatusBodyWhen
400{"error": "Invalid start date, expected YYYY-MM-DD"} (or end)Malformed start / end
400{"error": "start must not be after end"}Reversed range
400{"error": "Invalid date, expected YYYY-MM-DD"}Malformed date
400{"error": "use either date or start/end"}date sent with start or end
400{"error": "Invalid stock_id"}Format check failed
400{"error": "kind must be one of ['disposal', 'notice']"} / {"error": "market must be one of ['twse', 'tpex']"}Unknown value
400{"error": "start/end range is <n> days; max 366 (inclusive) without stock_id — split into smaller ranges"}Range over 366 days without stock_id
503{"error": "Data temporarily unavailable. Please retry shortly."}A table you asked for is not servable (not refreshed for 48 h, or unreadable) — retry later, never read it as "no announcements". Only the requested kind / market tables are checked

Notes

  • A disposal is announced after the close and takes effect the next trading day — date is the announcement day, start the first day in force. To ask "under disposition on D", use date=D (or start=end=D), not the announcement date.
  • A stock can have overlapping disposal periods (a 第二次處置 announced inside the first).
  • start / end are the announced period; a market holiday or trading suspension inside it extends the real end, which the data does not record.
  • match_minutes follows the rules of the day (TPEx 5 / 20 minutes → 2 from 2026-08-10) — read it per row, never assume a value.
  • Pre-disposal candidate lists (處置候選) are not included.
  • Cite as 「資料來源:臺灣證券交易所、證券櫃檯買賣中心」.

Taiwan Stock Batch Fetch

GET/studio/market/twstock/batch/<data_type>
Data From
Per data_type (same as the single-stock endpoint)
Update
Per data_type
Source
Per data_type

Parameters

data_typestringpathRequired
Dataset
Allowed price, price_adj, per, institutional, shareholding, foreign_shareholding, financials, balance_sheet, monthly_revenue, ratios, dividend
stock_idsstringqueryRequired
Codes
Allowed Comma-separated, ≤ 50
startstringquery
First date
Default per data_typeAllowed YYYY-MM-DD (strictly validated for dividend only)
endstringquery
Last date
Default per data_typeAllowed YYYY-MM-DD

Example

Python
payload = requests.get(f"{BASE_URL}/studio/market/twstock/batch/per", headers=headers,                       params={"stock_ids": "2330,6182", "start": "2026-08-04", "end": "2026-08-08"},                       timeout=120).json()# {"data_type": "per",#  "data": {"2330": [{"date": "2026-08-04", "dividend_yield": 0.95, "PER": 31.19, "PBR": 10.21, "stock_id": "2330"}, ...],#           "6182": [...]},#  "failed": []}

Response

{"data_type", "data": {"<stock_id>": [...rows...]}, "failed": [...]}. Rows are identical to the matching single-stock endpoint.

  • failed — ids whose server-side fetch failed (rate limit or upstream error); retry those.
  • An id absent from both data and failed genuinely has no data.

Errors

StatusBodyWhen
400{"error": "Unsupported data_type '<x>'. Supported: [...]"}Bad data_type
400{"error": "stock_ids parameter required"}Missing
400{"error": "stock_ids exceeds max 50 per request"}Too many
400{"error": "Invalid stock_id in list"}A code fails the format check
400{"error": "Invalid start date, expected YYYY-MM-DD"}dividend only

Notes

  • The whole market (~2,000 stocks) is ~40 calls; never fan out single-stock endpoints.
  • ratios (財務比率): each id's rows are the wide per-quarter rows of /ratios/<stock_id> ("data": {"2330": [{"date": "2026-06-30", "stock_id": "2330", "roe_q": 11.3934, ...}], "2317": [...]}). A code with no financial statements (ETFs) is absent from both data and failed (no 404); a ratio store that is not ready puts every id in failed (no 503).
  • dividend differs from /dividend/<stock_id>: it reads stored data only (no refresh), an unknown id or a stock with no dividend history is silently absent (no 404), and an upstream quota problem shows up in failed, not as a 503.