31 endpoints · Datos de mercado · Fundamentales · Flujo institucional · Otros
https://api.blave.orgapi-key + secret-key en cada solicitudAPI plan or Blave Agent planstock_id — código de TWSE (上市) / TPEx (上櫃), p. ej. 2330, 0050, 00878, 006208. Letras y dígitos (se admiten puntos), ≤ 8 caracteres; en caso contrario, 400 {"error": "Invalid stock_id"}.start / end — YYYY-MM-DD, inclusive. Salvo que un bloque indique lo contrario, no se valida su formato (el filtrado es una comparación de cadenas), por lo que siempre debe enviarse YYYY-MM-DD.503 {"error": "Upstream rate limit. Please retry shortly."} — reintentar más tarde.batch/<data_type> (≤ 50 ids por llamada); nunca recorra un universo en bucle con un endpoint de una sola acción./studio/market/twstock/list Ninguno.
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{"data": [...]}, ~2,400 filas (上市 + 上櫃, incl. ETF; 興櫃 excluido).
| Campo | Tipo | Descripción |
|---|---|---|
stock_id | string | Código |
name | string | Nombre |
close | float | null | Último cierre diario (null cuando el origen de datos no lo tenía, p. ej. negociación suspendida) |
market | string | TWSE (上市) o TPEx (上櫃) |
industry_code | string | null | Código numérico de 產業別 de TWSE/TPEx, tal como se recibe (sin decodificar). null para ETF / valores que no son de empresas. Códigos habituales: 15 航運業, 17 金融保險業, 22 生技醫療業, 24 半導體業, 25 電腦及週邊設備業, 26 光電業, 27 通信網路業, 28 電子零組件業, 29 電子通路業, 30 資訊服務業, 31 其他電子業 |
listing_date | string | null | YYYY-MM-DD; null para ETF / valores que no son de empresas |
Solo errores comunes (500 si fallan ambos mercados).
[:N] se concentra en unos pocos sectores./studio/market/twstock/info/<stock_id> | Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
stock_idstring · pathObligatorio | Código | Ver convenciones de la categoría |
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"}{"stock_id": "<stock_id>", "data": {...}}; data tiene los mismos campos que una fila de /list.
| Estado | Cuerpo | Cuándo |
|---|---|---|
| 400 | {"error": "Invalid stock_id"} | Falló la validación de formato |
| 404 | {"error": "Stock not found"} | No es un valor cotizado actualmente |
/list./studio/market/twstock/price/<stock_id> | Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
stock_idstring · pathObligatorio | Código de acción o ETF cotizado (上市) u OTC (上櫃) | Letras/dígitos (se admiten puntos), ≤ 8 caracteres — p. ej. 2330, 0050, 00878, 006208 | |
startstring · query | Primera fecha, inclusive | YYYY-MM-DD | historial completo |
endstring · query | Última fecha, inclusive | YYYY-MM-DD | la más reciente disponible |
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}, ...]{"stock_id": "<stock_id>", "data": [ ... ]}, filas en orden ascendente por date.
| Campo | Tipo | Unidad | Descripción |
|---|---|---|---|
date | string | YYYY-MM-DD | Día de negociación |
stock_id | string | — | Código de la acción |
open / high / low / close | float | TWD | Precios sin ajustar |
spread | float | TWD | Variación respecto al cierre anterior |
volume | int | acciones (股) | Volumen negociado |
turnover_value | int | TWD | Importe negociado |
turnover_count | int | operaciones (筆) | Número de operaciones |
| Estado | Cuerpo | Cuándo |
|---|---|---|
| 400 | {"error": "Invalid stock_id"} | stock_id no supera la validación de formato |
| 404 | {"error": "No data found for stock_id: <stock_id>"} | Código desconocido / sin datos en el origen |
| 503 | {"error": "Upstream rate limit. Please retry shortly."} | Cuota del origen de datos agotada — reintentar más tarde |
start / end: un valor mal formado filtra por comparación de cadenas en lugar de devolver 400, por lo que siempre debe enviarse YYYY-MM-DD.price_adj para backtests de rentabilidad total; este endpoint no está ajustado.batch/price (≤ 50 ids por llamada) en lugar de llamar a este endpoint en bucle./studio/market/twstock/price_adj/<stock_id> Igual que /price/<stock_id>.
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"]Misma estructura y mismos campos que /price/<stock_id>; open / high / low / close están ajustados, volume / turnover_* son idénticos a la serie sin ajustar.
Igual que /price/<stock_id>.
/price sin ajustar./studio/market/twstock/quote/<stock_id> | Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
stock_idstring · pathObligatorio | Código | Ver convenciones de la categoría |
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}{"stock_id": "<stock_id>", "data": {...}}; data es un objeto plano.
| Campo | Tipo | Descripción |
|---|---|---|
open / high / low / close | float | OHLC del día hasta el momento |
change_price / change_rate | float | Variación respecto al cierre anterior (TWD / %) |
average_price | float | Precio medio negociado |
volume | int | Volumen del último tick |
total_volume | int | Volumen acumulado del día |
amount / total_amount | float | Importe negociado del último tick / acumulado |
yesterday_volume | int | Volumen del día anterior |
buy_price / buy_volume | float / int | Mejor precio de compra (bid) |
sell_price / sell_volume | float / int | Mejor precio de venta (ask) |
volume_ratio | float | Ratio de volumen |
quote_time | string | YYYY-MM-DD HH:MM:SS — una marca de tiempo completa, a diferencia del date sin hora de otros endpoints |
stock_id | string | Código |
tick_type | int | 0 indeterminado, 1 iniciada por el vendedor (賣盤成交), 2 iniciada por el comprador (買盤成交) |
| Estado | Cuerpo | Cuándo |
|---|---|---|
| 400 | {"error": "Invalid stock_id"} | Falló la validación de formato |
| 404 | {"error": "No data found for stock_id: <stock_id>"} | Sin cotización |
| 503 | {"error": "Upstream rate limit. Please retry shortly."} | Cuota del origen de datos agotada |
/studio/market/twstock/quote | Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
stock_idsstring · queryObligatorio | Códigos | Separados por comas, ≤ 50 ids |
data = requests.get(f"{BASE_URL}/studio/market/twstock/quote" , headers=headers, params={"stock_ids": "2330,2317"}, timeout=30).json()["data"]# {"2330": {...}, "2317": {...}}{"data": {"<stock_id>": {...quote fields...}, ...}}; campos como en /quote/<stock_id>. Los ids sin cotización no aparecen.
| Estado | Cuerpo | Cuándo |
|---|---|---|
| 400 | {"error": "stock_ids parameter required"} | Falta |
| 400 | {"error": "stock_ids exceeds max 50 per request"} | Demasiados |
| 400 | {"error": "Invalid stock_id in list"} | Un código no supera la validación de formato |
| 503 | {"error": "Upstream rate limit. Please retry shortly."} | Cuota del origen de datos agotada |
/studio/market/twstock/quote/all Ninguno.
rows = requests.get(f"{BASE_URL}/studio/market/twstock/quote/all" , headers=headers, timeout=30).json()["data"]{"data": [{...quote fields...}, ...]} (~2,800 filas); campos como en /quote/<stock_id>.
| Estado | Cuerpo | Cuándo |
|---|---|---|
| 503 | {"error": "Upstream rate limit. Please retry shortly."} | Cuota del origen de datos agotada |
/studio/market/twstock/minute/ohlcv/<stock_id>/<schema> | Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
stock_idstring · pathObligatorio | Código | Un código cotizado actualmente en TWSE/TPEx, o TAIEX (加權指數) | |
schemastring · pathObligatorio | Tamaño de la vela | 1m, 5m, 15m, 30m, 60m, 1d | |
startstring · query | Primer día | YYYY-MM-DD | end − el rango máximo del schema |
endstring · query | Último día, inclusive | YYYY-MM-DD | hoy (UTC) |
adjuststring · query | 1 = OHLC ajustado hacia adelante (後復權), con los mismos factores que /price_adj; volume nunca se ajusta | 0, 1, true, false | 0 |
Rango máximo por solicitud (end − start): 1m 31 d · 5m 62 d · 15m 93 d · 30m 186 d · 60m 365 d · 1d 3650 d.
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}, ...]}{"stock_id", "schema", "data": [...]}.
| Campo | Tipo | Unidad | Descripción |
|---|---|---|---|
ts | string | ISO en UTC | Hora de apertura de la vela (etiqueta de inicio del minuto); la vela de las 13:30 (hora de Taipéi) es la subasta de cierre |
open / high / low / close | float | TWD | Precios |
volume | int | lotes (張), no acciones | Volumen. Las velas de TAIEX siempre llevan volume: 0 en todos los schemas — el índice no tiene volumen, así que no se sirve ninguno; no debe leerse como una cifra de importe negociado |
| Estado | Cuerpo | Cuándo |
|---|---|---|
| 400 | {"error": "Invalid stock_id (must be a listed TWSE/TPEx security)"} | No cotiza |
| 400 | {"error": "Invalid schema. Allowed: [...]"} | Schema no válido |
| 400 | {"error": "Invalid adjust (use 0 or 1)"} | adjust no válido |
| 400 | {"error": "Invalid date format. Use YYYY-MM-DD"} | Fecha mal formada |
| 400 | {"error": "date_range_too_large", "message": "...", "max_days": <n>} | Rango por encima del máximo |
| 503 | {"error": "market data temporarily unavailable"} | Fallo de lectura del almacén |
| 503 | {"error": "adjust factors temporarily unavailable"} | adjust=1 y factores no disponibles — nunca se sirve sin ajustar de forma silenciosa |
minute/ohlcv/symbols antes de pedir años de historial.200 con []./quote/<stock_id> para el precio en vivo./studio/market/twstock/minute/ohlcv/symbols Ninguno.
covered = requests.get(f"{BASE_URL}/studio/market/twstock/minute/ohlcv/symbols" , headers=headers, timeout=30).json()["data"]{"data": ["2330", "TAIEX", ...]} — ids que ya tienen datos por minuto en disco (acciones cotizadas actualmente más TAIEX), ordenados.
Solo errores comunes.
/studio/market/twstock/kbar/<stock_id> | Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
stock_idstring · pathObligatorio | Código | Un código cotizado actualmente en TWSE/TPEx, o TAIEX | |
startstring · query | Primer día | YYYY-MM-DD | hoy − 6 días |
endstring · query | Último día; end − start ≤ 31 días | YYYY-MM-DD | hoy |
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"]{"stock_id", "data": [...]}, en orden ascendente.
| Campo | Tipo | Descripción |
|---|---|---|
date | string | YYYY-MM-DD (hora de Taipéi) |
minute | string | HH:MM:SS hora de Taipéi, inicio de la vela (09:00–13:30) |
stock_id | string | Código |
open / high / low / close | float | TWD |
volume | int | Lotes (張) |
| Estado | Cuerpo | Cuándo |
|---|---|---|
| 400 | {"error": "Invalid stock_id (must be a listed TWSE/TPEx security)"} | No cotiza |
| 400 | {"error": "Date range exceeds 31 days"} | Rango demasiado largo o fecha mal formada |
minute/ohlcv/<stock_id>/1m. Una acción cotizada sin datos por minuto devuelve [] (este endpoint no activa la carga inicial).minute/ohlcv/symbols./studio/market/twstock/market_value/<stock_id> | Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
stock_idstring · pathObligatorio | Código | Ver convenciones de la categoría | |
startstring · query | Primera fecha | YYYY-MM-DD | historial completo |
endstring · query | Última fecha | YYYY-MM-DD | la más reciente |
data = requests.get(f"{BASE_URL}/studio/market/twstock/market_value/2330" , headers=headers, params={"start": "2026-01-01"}, timeout=60).json()["data"]{"stock_id", "data": [{"date", "stock_id", "market_value"}]}; market_value en TWD.
400 stock_id no válido, 404 sin datos, 503 cuota del origen de datos.
/studio/market/twstock/market_value/all | Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
topint · query | Conserva solo los N primeros por capitalización de mercado | 1–3000 | todos (~2,400) |
body = requests.get(f"{BASE_URL}/studio/market/twstock/market_value/all" , headers=headers, params={"top": 10}, timeout=60).json()# {"date": "2026-09-17", "twse_ex_etf_market_value": 150952470507848,# "data": [{"is_etf": false, "market": "TWSE", "market_value": 62885997412475,# "name": "台積電", "rank": 1, "stock_id": "2330"},# ...,# {"is_etf": true, "market": "TWSE", "market_value": 2383312875000,# "name": "元大台灣50", "rank": 6, "stock_id": "0050"}, ...]}# Index weight of a 上市 non-ETF stock (works with any `top`, the denominator is whole-market)row = body["data"][0] # 2330 — market "TWSE", is_etf falseshare = row["market_value"] / body["twse_ex_etf_market_value"]# ETFs rank alongside stocks (0050 is rank 6) — filter them with `is_etf`. Never by code prefix,# and never by re-fetching a classification: `is_etf` already IS that classification.rows = requests.get(f"{BASE_URL}/studio/market/twstock/market_value/all" , headers=headers, params={"top": 100}, timeout=60).json()["data"]top50 = [r["stock_id"] for r in rows if not r["is_etf"]][:50] # ETF-free market-cap top 50tpex_only = [r["stock_id"] for r in rows if r["market"] == "TPEx"] # board filter is exact{"date": "YYYY-MM-DD", "twse_ex_etf_market_value": int, "data": [...]}, ordenado por capitalización de mercado descendente.
| Campo | Tipo | Descripción |
|---|---|---|
date | string | Día de referencia realmente usado (el último publicado; puede ir un día por detrás de hoy) |
twse_ex_etf_market_value | int | TWD. Suma de las filas con market == "TWSE" que no son ETF, en el mismo día de referencia. Denominador de todo el mercado — top no lo reduce |
data[].rank | int | Posición en el ranking (empieza en 1) |
data[].stock_id | string | Código |
data[].name | string | Nombre |
data[].market | string | "TWSE" (上市) o "TPEx" (上櫃), según los listados de valores cotizados de TWSE / TPEx |
data[].market_value | int | TWD |
data[].is_etf | bool | true para los ETF del ranking (359 de 2,370 filas el 2026-09-17). false significa «no está en el conjunto de ETF», no «confirmado que no es un ETF»: un valor sin categoría de industria — p. ej. el REIT 01010T — es false y permanece dentro de twse_ex_etf_market_value |
| Estado | Cuerpo | Cuándo |
|---|---|---|
| 400 | {"error": "top must be an integer between 1 and 3000"} | top no válido |
| 404 | {"error": "No recent market value data available"} (o un mensaje de listado incompleto) | Sin datos recientes |
| 503 | {"error": "Upstream rate limit. Please retry shortly."} | Cuota del origen de datos agotada |
/market_value/<stock_id> para preguntas del tipo «los N primeros por capitalización».rank y twse_ex_etf_market_value son universos distintos. rank abarca todo el mercado — 上市 + 上櫃, ETF incluidos — y no cambió cuando se añadió el denominador. El denominador es solo 上市 y excluye los ETF (industry_category en {ETF, 上櫃ETF, 上櫃指數股票型基金(ETF)}); los REIT y las acciones preferentes no se excluyen de él. Por tanto, market_value / twse_ex_etf_market_value es un peso en el índice solo para una fila cuyo market es "TWSE" y que no es un ETF — una fila de TPEx o de un ETF sobre este denominador no es un peso —, mientras que rank sigue siendo una posición en todo el mercado. No deben leerse como un único ranking.market es una etiqueta del segmento de cotización, no un indicador de ETF — el indicador de ETF es is_etf. Úselo para un universo sin ETF. No deduzca los ETF del prefijo de stock_id (00 es una convención del mercado y no un contrato, y deja fuera los REIT 01xxxT) ni vuelva a obtener una clasificación de otro lugar. is_etf es exactamente el criterio con el que se calcula twse_ex_etf_market_value, por lo que un filtro por is_etf y el denominador nunca pueden discrepar./studio/market/twstock/per/<stock_id> | Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
stock_idstring · pathObligatorio | Código | Ver convenciones de la categoría | |
startstring · query | Primera fecha | YYYY-MM-DD | historial completo |
endstring · query | Última fecha | YYYY-MM-DD | la más reciente |
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}, ...]{"stock_id", "data": [...]}.
| Campo | Tipo | Descripción |
|---|---|---|
date | string | Día de negociación |
stock_id | string | Código |
dividend_yield | float | Rentabilidad por dividendo (%) |
PER | float | Precio / beneficio |
PBR | float | Precio / valor en libros |
400 stock_id no válido, 404 sin datos, 503 cuota del origen de datos.
batch/per./studio/market/twstock/financials/<stock_id> | Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
stock_idstring · pathObligatorio | Código | Ver convenciones de la categoría | |
startstring · query | Primera fecha | YYYY-MM-DD | 2013-01-01 |
endstring · query | Última fecha | YYYY-MM-DD | hoy |
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"){"stock_id", "data": [...]}, formato largo (una fila por partida y por trimestre).
| Campo | Tipo | Descripción |
|---|---|---|
date | string | Fin de trimestre (03-31 / 06-30 / 09-30 / 12-31) |
stock_id | string | Código |
type | string | Código de la partida, p. ej. Revenue, GrossProfit, OperatingIncome, NetIncome, EPS, TAX, OtherComprehensiveIncome |
value | float | Valor (TWD; EPS en TWD por acción) |
origin_name | string | Etiqueta en chino — sirve para identificar códigos type desconocidos |
400 stock_id no válido, 404 sin datos, 503 cuota del origen de datos.
start es 2013-01-01 por defecto; si se envía un start anterior, se devuelven filas anteriores (2330 llega hasta 2000-06-30)./studio/market/twstock/balance_sheet/<stock_id> Igual que /financials/<stock_id>.
data = requests.get(f"{BASE_URL}/studio/market/twstock/balance_sheet/2330" , headers=headers, params={"start": "2022-01-01"}, timeout=30).json()["data"]Mismo formato largo que /financials/<stock_id>. Códigos type clave: CashAndCashEquivalents, TotalAssets, TotalLiabilities, TotalEquity. Los códigos que terminan en _per son % del activo total.
400 stock_id no válido, 404 sin datos, 503 cuota del origen de datos.
start es 2013-01-01 por defecto; si se envía un start anterior, se devuelven filas anteriores (2330 llega hasta 2012-12-31)./studio/market/twstock/cashflow/<stock_id> Igual que /financials/<stock_id>.
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 quarterMismo formato largo que /financials/<stock_id>. Principales códigos type (son los códigos propios del conjunto de datos de origen; el conjunto completo varía según la empresa y el año, así que la respuesta debe tomarse como la lista de referencia y origin_name usarse para todo lo que no figure abajo). Filtre por type, no por origin_name — la etiqueta de un mismo código alterna entre paréntesis de ancho completo y de medio ancho según el año:
type | origin_name |
|---|---|
CashFlowsFromOperatingActivities | 營業活動之淨現金流入(流出) |
CashProvidedByInvestingActivities | 投資活動之淨現金流入(流出) |
CashFlowsProvidedFromFinancingActivities | 籌資活動之淨現金流入(流出) |
PropertyAndPlantAndEquipment | 取得不動產、廠房及設備 (capex, negativo) |
Depreciation / AmortizationExpense | 折舊費用 / 攤銷費用 |
CashBalancesIncrease | 本期現金及約當現金增加(減少)數 |
CashBalancesBeginningOfPeriod / CashBalancesEndOfPeriod | 期初(年初)/ 期末現金及約當現金餘額 |
Las partidas de flujo son acumuladas en el año (YTD), tal como se presentan en Taiwán: la fila 03-31 es solo el T1, 06-30 es T1–T2, 09-30 es T1–T3, 12-31 es el año completo. Para un solo trimestre, reste el trimestre anterior del mismo año (el T1 no requiere resta) — por eso conviene pedir desde un start de enero. CashBalancesEndOfPeriod es el saldo al cierre del trimestre; CashBalancesBeginningOfPeriod es el saldo al inicio del año. Este endpoint no tiene parámetro period — siempre devuelve YTD (la vista de un solo trimestre ?period=quarter existe únicamente en la ruta del gráfico web, no aquí).
400 stock_id no válido, 404 sin datos, 503 cuota del origen de datos.
start es 2013-01-01 por defecto; si se envía un start anterior, se devuelven filas anteriores (2330 llega hasta 2012-03-31)./studio/market/twstock/monthly_revenue/<stock_id> | Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
stock_idstring · pathObligatorio | Código | Ver convenciones de la categoría | |
startstring · query | Primera fecha | YYYY-MM-DD | 2000-01-01 |
endstring · query | Última fecha | YYYY-MM-DD | hoy |
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}, ...]{"stock_id", "data": [...]}, una fila por mes.
| Campo | Tipo | Descripción |
|---|---|---|
date | string | Primer día del mes posterior al mes de los ingresos — 2024-01-01 lleva revenue_year: 2023, revenue_month: 12. No debe usarse nunca como periodo |
stock_id | string | Código |
country | string | Mercado — el valor observado es Taiwan |
create_time | string | Se observa vacío ("") en todas las filas |
revenue | int | Ingresos mensuales en TWD (元), no en miles — deducido de la magnitud, el origen de datos no lo indica: 2330 para revenue_year 2024, revenue_month 1 devolvió 215785127000, una cifra que solo tiene sentido leída en 元 (NT$215.8 mil millones) |
revenue_month | int | Mes de los ingresos (1–12) — use este campo, no date, como periodo |
revenue_year | int | Año de los ingresos |
400 stock_id no válido, 404 sin datos, 503 cuota del origen de datos.
/studio/market/twstock/ratios/<stock_id> | Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
stock_idstring · pathObligatorio | Código | Ver convenciones de la categoría | |
startstring · query | Primera fecha de fin de trimestre | YYYY-MM-DD | 2013-01-01 |
endstring · query | Última fecha de fin de trimestre | YYYY-MM-DD | hoy |
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"){"stock_id", "data": [...]}, formato ancho: una fila por trimestre, de la más antigua a la más reciente, una columna por ratio. Cada ratio es un float redondeado a 4 decimales, o null.
| Campo | Tipo | Descripción |
|---|---|---|
date | string | Fin de trimestre (03-31 / 06-30 / 09-30 / 12-31) — no el día en que el informe se hizo público |
stock_id | string | Código |
<ratio>_q | float | null | Solo el trimestre |
<ratio>_ttm | float | null | Últimos cuatro trimestres (suma de los cuatro trimestres individuales); null si falta alguno de los cuatro |
debt_ratio, current_ratio, quick_ratio, equity_multiplier | float | null | Ratios de balance al cierre del periodo — sin sufijo |
<ratio> (cada uno tiene _q y _ttm):
| Grupo | Ratios | Unidad |
|---|---|---|
| Rentabilidad | gross_margin, operating_margin, pretax_margin, net_margin, opex_ratio, roe, roa | % (ya ×100) |
| Por acción | eps, revenue_per_share, fcf_per_share | TWD por acción |
| Crecimiento | revenue_yoy, revenue_qoq, operating_income_yoy, net_income_yoy, eps_yoy | % (ya ×100) |
| Estructura | interest_coverage | veces |
| Eficiencia | receivable_turnover, inventory_turnover, asset_turnover, cfo_to_net_income | veces |
| Flujo de efectivo | fcf | TWD, importe bruto (no en miles) |
Columnas sin sufijo: debt_ratio, current_ratio, quick_ratio son % (ya ×100 — un ratio corriente de 2.46× se devuelve como 245.7635); equity_multiplier se expresa en veces.
| Estado | Cuerpo | Cuándo |
|---|---|---|
| 400 | {"error": "Invalid stock_id"} | El código no supera la validación de formato |
| 404 | {"error": "No data found for stock_id: <id>"} | No hay estados financieros para el código (ETF como 0050 / 00878), o no hay ninguna fila dentro de start–end |
| 503 | {"error": "Data temporarily unavailable. Please retry shortly."} | Almacén de ratios no disponible todavía — reintentar más tarde; nunca debe leerse como «sin datos» |
null (sin margen bruto, ratio corriente, etc.).date introduce sesgo de anticipación (lookahead).null, nunca 0.per/<stock_id>) ni los ratios de ingresos mensuales (calcule YoY / MoM a partir de monthly_revenue/<stock_id>).GET /studio/market/twstock/batch/ratios?stock_ids=... (≤ 50 ids por llamada).start anterior no devuelve nada más antiguo./studio/market/twstock/dividend/<stock_id> | Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
stock_idstring · pathObligatorio | Código | Código cotizado actualmente (4–6 dígitos, letra final opcional) | |
startstring · query | Primera fecha efectiva | YYYY-MM-DD estricto | historial completo |
endstring · query | Última fecha efectiva | YYYY-MM-DD estricto | historial completo |
El filtrado por rango usa una fecha efectiva de tres niveles: cash_ex_date si existe; si no, stock_ex_date; si no, record_date — los eventos recién anunciados que aún no tienen fecha ex también aparecen.
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}, ...]{"stock_id", "data": [...]}, una fila por cada fila del anuncio.
| Campo | Tipo | Descripción |
|---|---|---|
record_date | string | 權利分派基準日 YYYY-MM-DD |
period | string | 股利所屬期間 — una etiqueta opaca (114年第3季, 113, 不適用, …); compárela como cadena, no la convierta en un año |
announce_date | string | 公告日; "" cuando se desconoce |
cash_ex_date | string | 除息交易日; "" cuando no hay / no está decidida |
stock_ex_date | string | 除權交易日; "" cuando no hay |
pay_date | string | 現金股利發放日; "" cuando se desconoce |
cash | float | Dividendo en efectivo por acción (TWD, 盈餘 + 公積) |
stock | float | Dividendo en acciones por acción (元面額, 盈餘 + 公積) |
stock_ratio | float | stock / 10 — ratio de ajuste de tipo split |
| Estado | Cuerpo | Cuándo |
|---|---|---|
| 400 | {"error": "Invalid stock_id"} | Falló la validación de formato |
| 400 | {"error": "Invalid start date, expected YYYY-MM-DD"} (o end) | Fecha mal formada |
| 404 | {"error": "No data found for stock_id: <id>"} / {"error": "No dividend records for stock_id: <id>"} | No cotiza, o no tiene ningún historial de dividendos |
| 503 | {"error": "Upstream rate limit. Please retry shortly."} | Cuota del origen de datos agotada |
cash == 0 y stock == 0) se conservan — son una decisión anunciada de no repartir.2330 llegó hasta una fecha una semana posterior a la solicitud. Filtre por fecha por su cuenta si solo necesita eventos pasados.200 + [] (distinto de 404).404./studio/market/twstock/news/<stock_id> | Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
stock_idstring · pathObligatorio | Código | Ver convenciones de la categoría | |
startstring · query | Primer día | YYYY-MM-DD | hoy − 6 días |
endstring · query | Último día; end − start ≤ 31 días | YYYY-MM-DD | hoy |
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"]{"stock_id", "data": [...]}, varios artículos por día (fines de semana incluidos).
| Campo | Tipo | Descripción |
|---|---|---|
date | string | Fecha y hora de publicación |
stock_id | string | Código |
title | string | Titular |
source | string | Medio |
link | string | URL |
| Estado | Cuerpo | Cuándo |
|---|---|---|
| 400 | {"error": "Date range exceeds 31 days"} o un mensaje de error de análisis de fecha | Rango demasiado largo / fecha mal formada |
| 503 | {"error": "Upstream rate limit. Please retry shortly."} | Cuota del origen de datos agotada |
[]./studio/market/twstock/institutional/<stock_id> | Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
stock_idstring · pathObligatorio | Código | Ver convenciones de la categoría | |
startstring · query | Primera fecha | YYYY-MM-DD | historial completo |
endstring · query | Última fecha | YYYY-MM-DD | la más reciente |
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}, ...]{"stock_id", "data": [...]}, formato ancho, una fila por día. Todos los valores en acciones (股).
| Campo | Descripción |
|---|---|
date, stock_id | Día, código |
foreign_buy / foreign_sell | 外資 (sin operadores por cuenta propia extranjeros) |
trust_buy / trust_sell | 投信 |
dealer_self_buy / dealer_self_sell | 自營商自行買賣 |
dealer_hedge_buy / dealer_hedge_sell | 自營商避險 |
foreign_dealer_self_buy / foreign_dealer_self_sell | 外資自營商 (casi siempre 0) |
400 stock_id no válido, 404 sin datos, 503 cuota del origen de datos.
*_buy − *_sell.GET /studio/market/twmarket/institutional) lo agrupa de otra forma — véase esa sección./studio/market/twstock/margin/<stock_id> | Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
stock_idstring · pathObligatorio | Código | Ver convenciones de la categoría | |
startstring · query | Primera fecha | YYYY-MM-DD | historial completo |
endstring · query | Última fecha | YYYY-MM-DD | la más reciente |
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"] # 融資使用率{"stock_id", "data": [...]}, una fila por día. Los valores están en lotes (張), no en acciones — verificado con MI_MARGN de TWSE: para 2026-09-15, 2330 devolvió margin_balance: 29282 y margin_limit: 6483092, que coinciden exactamente con los valores 今日餘額 29,282 y 次一營業日限額 6,483,092 de TWSE para esa fecha. Cada fila lleva su propio date y stock_id; al leer el historial completo (sin start / end), tome todas las cifras de la misma fila — margin_limit se vuelve a publicar cada día y cambia con el tiempo.
| Campo | Descripción |
|---|---|
margin_buy / margin_sell | 融資買進 / 賣出 |
margin_cash_repay | 融資現金償還 |
margin_balance / margin_prev_balance | 融資今日 / 前日餘額 |
margin_limit | 融資限額 — el 次一營業日限額 de TWSE tal como se publicó en la fecha de esa fila, es decir, el límite que se aplica al SIGUIENTE día hábil (2330 el 2026-09-15: 6,483,092 lotes) |
short_buy / short_sell | 融券買進 / 賣出 |
short_cash_repay | 融券現金償還 |
short_balance / short_prev_balance | 融券今日 / 前日餘額 |
short_limit | 融券限額 |
offset_loan_short | 資券相抵 |
400 stock_id no válido, 404 sin datos, 503 cuota del origen de datos.
/studio/market/twstock/gov_bank/<stock_id> | Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
stock_idstring · pathObligatorio | Código | Ver convenciones de la categoría | |
startstring · query | Primer día | YYYY-MM-DD | hoy − 6 días |
endstring · query | Último día; end − start ≤ 31 días | YYYY-MM-DD | hoy |
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"]{"stock_id", "data": [...]}, hasta 8 filas por día (una por banco), ordenadas por fecha y banco.
| Campo | Descripción |
|---|---|
date, stock_id | Día, código |
bank_name | Banco |
buy / sell | Acciones compradas / vendidas |
buy_amount / sell_amount | TWD comprados / vendidos |
| Estado | Cuerpo | Cuándo |
|---|---|---|
| 400 | {"error": "Date range exceeds 31 days"} o un mensaje de error de análisis de fecha | Rango demasiado largo / fecha mal formada |
| 503 | {"error": "Upstream rate limit. Please retry shortly."} | Cuota del origen de datos agotada |
/studio/market/twstock/lending/<stock_id> | Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
stock_idstring · pathObligatorio | Código | Ver convenciones de la categoría | |
startstring · query | Primera fecha | YYYY-MM-DD | historial completo |
endstring · query | Última fecha | YYYY-MM-DD | la más reciente |
data = requests.get(f"{BASE_URL}/studio/market/twstock/lending/2330" , headers=headers, params={"start": "2026-08-01"}, timeout=60).json()["data"]{"stock_id", "data": [...]}, varias filas por día.
| Campo | Descripción |
|---|---|
date, stock_id | Día, código |
transaction_type | 競價 / 議借 |
volume | Volumen |
fee_rate | Tasa de comisión del préstamo |
close | Precio de cierre |
original_return_date | Fecha de devolución original |
original_lending_period | Plazo original del préstamo |
400 stock_id no válido, 404 sin datos, 503 cuota del origen de datos.
/studio/market/twstock/broker/search | Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
namestring · queryObligatorio | Nombre de la sucursal que se busca | Cualquier subcadena, sin distinguir mayúsculas de minúsculas |
rows = requests.get(f"{BASE_URL}/studio/market/twstock/broker/search" , headers=headers, params={"name": "松山"}, timeout=30).json()["data"]# [{"broker_id": "9217", "broker_name": "凱基-松山"}, ...]{"query": "<name>", "data": [{"broker_id", "broker_name"}]}.
| Estado | Cuerpo | Cuándo |
|---|---|---|
| 400 | {"error": "name parameter required"} | Falta |
| 503 | {"error": "Upstream rate limit. Please retry shortly."} | Cuota del origen de datos agotada |
/studio/market/twstock/broker/stock/<stock_id> Envíe date para un solo día, o start (+ end) para un rango; si se envían ambos, prevalece start.
| Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
stock_idstring · pathObligatorio | Código | Ver convenciones de la categoría | |
datestring · query | Un solo día | YYYY-MM-DD | hoy |
startstring · query | Inicio del rango | YYYY-MM-DD | |
endstring · query | Fin del rango; rango ≤ 366 días, inclusive | YYYY-MM-DD | start |
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"]{"stock_id", "data": [...]}, una fila por sucursal y por día.
| Campo | Tipo | Descripción |
|---|---|---|
date | string | Día de negociación |
stock_id | string | Código |
broker_id | string | Código de la sucursal (puede ser alfanumérico, p. ej. 920A) |
broker_name | string | Nombre de la sucursal |
price | float | Precio medio negociado ponderado por volumen |
buy / sell | int | Acciones compradas / vendidas |
| Estado | Cuerpo | Cuándo |
|---|---|---|
| 400 | {"error": "Invalid stock_id"} | Falló la validación de formato |
| 400 | {"error": "Invalid date (YYYY-MM-DD)"} | Fecha mal formada |
| 400 | {"error": "start/end range is <n> days; max 366 (inclusive) — split into smaller ranges"} | Rango demasiado largo |
| 503 | {"error": "Upstream rate limit. Please retry shortly."} | Datos no disponibles temporalmente — reintentar; no debe leerse como «sin operaciones» |
200 + [].start / end sobre 2026-09-06 → 2026-09-13 devolvió filas de cinco días de negociación distintos, tanto para broker/stock/<stock_id> como para broker/trader/<trader_id>./studio/market/twstock/broker/trader/<trader_id> Mismas reglas de date / start / end que /broker/stock/<stock_id>.
| Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
trader_idstring · pathObligatorio | Código de sucursal obtenido de /broker/search | Letras, dígitos, - (p. ej. 9217, 920A) | |
datestring · query | Un solo día | YYYY-MM-DD | hoy |
startstring · query | Inicio del rango | YYYY-MM-DD | |
endstring · query | Fin del rango; rango ≤ 366 días, inclusive | YYYY-MM-DD | start |
rows = requests.get(f"{BASE_URL}/studio/market/twstock/broker/trader/9217" , headers=headers, params={"date": "2026-08-07"}, timeout=60).json()["data"]{"trader_id", "data": [...]}, una fila por acción y por día; campos date, broker_id, broker_name, stock_id, price, buy, sell (como en /broker/stock).
Como en /broker/stock/<stock_id>, con 400 {"error": "Invalid trader_id"} para un código no válido.
/studio/market/twstock/alerts Indique date, o start / end, o ninguno (= hoy, hora de Taipéi).
| Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
datestring · query | Un día: disposiciones en vigor ese día, avisos de atención anunciados ese día | YYYY-MM-DD estricto; no se combina con start / end | |
startstring · query | Primer día del rango | YYYY-MM-DD estricto | 2001-01-01 con stock_id; si no, end − 365 días |
endstring · query | Último día del rango (inclusive) | YYYY-MM-DD estricto | hoy (o start si es posterior) |
kindstring · query | Tipo de anuncio | disposal (處置), notice (注意) | ambos |
marketstring · query | Bolsa | twse, tpex | ambos |
stock_idstring · query | Un código; elimina el límite de 366 días | Ver convenciones de la categoría | todos |
Semántica del rango: una disposición se devuelve cuando su periodo (start–end) se solapa con el rango; un aviso de atención, cuando su date de anuncio cae dentro de él. Sin stock_id, el rango es como máximo de 366 días, inclusive — divida los rangos más largos de todo el mercado (cabe un año natural por llamada). A diferencia de lo habitual en la categoría, start / end / date tienen validación estricta.
# disposals in force on one daydata = requests.get(f"{BASE_URL}/studio/market/twstock/alerts" , headers=headers, params={"date": "2026-10-13", "kind": "disposal"}, timeout=30).json()["data"]# [..., {"date": "2026-10-12", "stock_id": "5228", "stock_name": "鈺鎧", "market": "tpex",# "kind": "disposal", "count": 2, "start": "2026-10-13", "end": "2026-10-19",# "condition": "連續3個營業日", "measure": "第一次處置", "match_minutes": 2,# "reason": "* 公布日期:115/10/12\n* 證券代號:5228\n…", "close": 59.6, "pe": 29.22},# {"date": "2026-10-12", "stock_id": "2492", "stock_name": "華新科", "market": "twse",# "kind": "disposal", "count": 8, "start": "2026-10-13", "end": "2026-10-19",# "condition": "連續三次", "measure": "第一次處置", "match_minutes": 2,# "reason": "1處置原因:該有價證券之交易,連續三個營業日達本公司「公布注意交易資訊」標準。\n2處置期間:…", "close": None, "pe": None}, ...] # 36 rows# notices announced on one daynotes = requests.get(f"{BASE_URL}/studio/market/twstock/alerts" , headers=headers, params={"date": "2026-10-12", "kind": "notice"}, timeout=30).json()["data"]# [..., {"date": "2026-10-12", "stock_id": "5228", "stock_name": "鈺鎧", "market": "tpex",# "kind": "notice", "count": 15, "start": None, "end": None, "condition": "第一款、第三款、第四款",# "measure": None, "match_minutes": None,# "reason": "最近六個營業日(含當日)累積之最後成交價漲幅達32.63%(第一款)\n…", "close": 59.6, "pe": 29.22}, ...]# one stock's disposal history (no range cap with stock_id)hist = requests.get(f"{BASE_URL}/studio/market/twstock/alerts" , headers=headers, params={"stock_id": "2492", "kind": "disposal", "start": "2001-01-01"}, timeout=30).json()["data"]# 10 rows: 2001-11-23~2001-11-29 收足五成款券 (match_minutes None), 2018-05-08~2018-05-21 第一次處置 (5), …,# 2026-10-13~2026-10-19 第一次處置 (2){"start", "end", "data": [...]}; start / end devuelven el rango resuelto (ambos son iguales a date cuando se envió date). Filas ordenadas por date, market, kind, stock_id. Una fila por anuncio.
| Campo | Tipo | Descripción |
|---|---|---|
date | string | Día del anuncio YYYY-MM-DD (una disposición entra en vigor el siguiente día de negociación) |
stock_id | string | Código — incluye warrants, ETF y bonos convertibles (p. ej. 037060, 45402) |
stock_name | string | null | Nombre tal como se publicó |
market | string | twse (上市) o tpex (上櫃) |
kind | string | disposal o notice |
count | int | El recuento acumulado de la bolsa (累計 / 累計次數) |
start, end | string | null | Periodo de la disposición, ambos días incluidos; null en los avisos de atención |
condition | string | null | Disposición: el motivo tal como se publicó (連續三次, 連續3個營業日, 最近十個營業日已有六次 …). Aviso de atención: las cláusulas citadas (第一款、第五款); null si no hay ninguna |
measure | string | null | Tipo de disposición: 第一次處置, 第二次處置, 再次處置, 人工管制撮合, 收足五成款券 …; null en los avisos de atención y en las filas antiguas de TPEx |
match_minutes | int | null | Disposición: el N de 「約每N分鐘撮合一次」 leído del texto; null cuando el texto no indica ninguno (todas las disposiciones de TWSE anteriores a ~2011 — su texto está en blanco — y las medidas sin casación periódica) y en los avisos de atención |
reason | string | null | Texto completo del anuncio; null cuando la bolsa no publicó ninguno (todas las disposiciones de TWSE anteriores a 2011-03) (disposición 處置內容, aviso de atención 注意交易資訊) |
close, pe | float | null | Cierre y PER en el anuncio; siempre null en las disposiciones de TWSE |
| Estado | Cuerpo | Cuándo |
|---|---|---|
| 400 | {"error": "Invalid start date, expected YYYY-MM-DD"} (o end) | start / end mal formados |
| 400 | {"error": "start must not be after end"} | Rango invertido |
| 400 | {"error": "Invalid date, expected YYYY-MM-DD"} | date mal formado |
| 400 | {"error": "use either date or start/end"} | date enviado junto con start o end |
| 400 | {"error": "Invalid stock_id"} | Falló la validación de formato |
| 400 | {"error": "kind must be one of ['disposal', 'notice']"} / {"error": "market must be one of ['twse', 'tpex']"} | Valor desconocido |
| 400 | {"error": "start/end range is <n> days; max 366 (inclusive) without stock_id — split into smaller ranges"} | Rango de más de 366 días sin stock_id |
| 503 | {"error": "Data temporarily unavailable. Please retry shortly."} | Una tabla solicitada no se puede servir (sin actualizar desde hace 48 h, o ilegible) — reintentar más tarde; nunca debe leerse como «sin anuncios». Solo se comprueban las tablas de los kind / market solicitados |
date es el día del anuncio, start el primer día en vigor. Para preguntar «en disposición el día D», use date=D (o start=end=D), no la fecha del anuncio.start / end son el periodo anunciado; un festivo del mercado o una suspensión de la negociación dentro de él prolonga el final real, algo que los datos no registran.match_minutes sigue las reglas vigentes en cada momento (TPEx 5 / 20 minutos → 2 desde 2026-08-10) — léalo fila por fila, nunca dé por supuesto un valor./studio/market/twstock/batch/<data_type> | Nombre | Descripción | Valores permitidos | Predeterminado |
|---|---|---|---|
data_typestring · pathObligatorio | Conjunto de datos | price, price_adj, per, institutional, shareholding, foreign_shareholding, financials, balance_sheet, monthly_revenue, ratios, dividend | |
stock_idsstring · queryObligatorio | Códigos | Separados por comas, ≤ 50 | |
startstring · query | Primera fecha | YYYY-MM-DD (validación estricta solo para dividend) | según data_type |
endstring · query | Última fecha | YYYY-MM-DD | según data_type |
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": []}{"data_type", "data": {"<stock_id>": [...rows...]}, "failed": [...]}. Las filas son idénticas a las del endpoint de una sola acción correspondiente.
failed — ids cuya obtención en el servidor falló (rate limit o error del origen de datos); reintente esos.data ni en failed realmente no tiene datos.| Estado | Cuerpo | Cuándo |
|---|---|---|
| 400 | {"error": "Unsupported data_type '<x>'. Supported: [...]"} | data_type no válido |
| 400 | {"error": "stock_ids parameter required"} | Falta |
| 400 | {"error": "stock_ids exceeds max 50 per request"} | Demasiados |
| 400 | {"error": "Invalid stock_id in list"} | Un código no supera la validación de formato |
| 400 | {"error": "Invalid start date, expected YYYY-MM-DD"} | Solo dividend |
ratios (財務比率): las filas de cada id son las filas anchas por trimestre de /ratios/<stock_id> ("data": {"2330": [{"date": "2026-06-30", "stock_id": "2330", "roe_q": 11.3934, ...}], "2317": [...]}). Un código sin estados financieros (ETF) no aparece ni en data ni en failed (sin 404); un almacén de ratios que aún no está disponible pone todos los ids en failed (sin 503).dividend difiere de /dividend/<stock_id>: solo lee datos almacenados (sin actualización), un id desconocido o una acción sin historial de dividendos simplemente no aparece (sin 404), y un problema de cuota del origen de datos aparece en failed, no como un 503.