A Python client and Codex/Claude plugin for pulling Thai capital-market
data from the SEC OpenAPI (api.sec.or.th) — every SEC Open Data portal
category, including digital assets, funds, Licence Check, One Report, provident
funds, debt, equity, ESG, capital-market operators/professionals, investors,
and future /v1/... or /v2/... endpoints.
The SEC exposes both legacy products such as FundFactsheet and
FundDailyInfo, and current portal paths such as
/v1/one-report/fs/{report_year}/financial_statement/{unique_id} and
/v2/fund/factsheet/performance. The generic request command reaches any
endpoint once you have the matching subscription key.
git clone https://github.com/nutdnuy/sec-opendata-th
cd sec-opendata-th
pip install -e . # installs the `secopendata` package + CLI
# (or: pip install -r requirements.txt and run via `python -m secopendata`)Get keys from the SEC API Developer Portal. The portal is migrating: secopendata.sec.or.th/sec-open-apis is the new home; the old api-portal.sec.or.th is discontinued on 30 June 2026. Subscription keys carry over. Keys are resolved in this order:
- value passed in code
SEC_<PRODUCT_OR_CATEGORY>_KEYenv var, e.g.SEC_FUND_KEY,SEC_BOND_KEY,SEC_PVD_KEY,SEC_ONE_REPORT_KEY,SEC_DIGITAL_ASSET_KEYSEC_API_KEY(shared fallback)~/.config/secopendata/keys.toml
export SEC_FUNDFACTSHEET_KEY="your-factsheet-key"
export SEC_FUNDDAILYINFO_KEY="your-nav-key"
export SEC_FUND_KEY="your-v2-fund-key"Or ~/.config/secopendata/keys.toml:
[keys]
FundFactsheet = "your-factsheet-key"
FundDailyInfo = "your-nav-key"Keys are never committed (keys.toml and .env are git-ignored).
secopendata products # registered products
secopendata categories # current portal key scopes
secopendata catalog-info # bundled snapshot metadata
secopendata amc-search "กสิกร" # offline AMC lookup; no key
secopendata fund-search "K-CHANGE" --status Registered # offline fund lookup; no key
secopendata amcs # all AMCs (id + name)
secopendata funds --amc KASIKORN # funds of an AMC
secopendata nav --proj-id <id> --date 2026-06-16 # daily NAV
secopendata nav --amc KASIKORN --abbr <ABBR> --date 2026-06-16
secopendata fund-info --proj-id <id> --top5 <period> # policy + classes + top5
secopendata get --product <Name> --path <path> --param key=value [--paginate] # legacy
secopendata request --method GET --path /v1/one-report/fs/2021/financial_statement/C0000000013
secopendata request --method GET --path /v2/fund/factsheet/performance --cursor-paginate
secopendata request --method GET --path /v2/fund/factsheet/benchmarks --cursor-paginate
secopendata request --method POST --path /v1/digital-asset/profile/intermediary --json '{"IntermediaryName":""}'All subcommands print JSON. Exit codes: 3 missing key, 4 not subscribed,
5 other API error.
amc-search and fund-search use the bundled compact SEC profile snapshot and
do not make a network request. Run catalog-info to check its as-of date before
relying on current registration status.
from secopendata import SECClient, FundFactsheet, FundDailyInfo
client = SECClient()
ff = FundFactsheet(client)
amc_id = ff.resolve_amc_id("KASIKORN")
funds = ff.funds_by_amc(amc_id)
nav = FundDailyInfo(client).daily_nav(funds[0]["proj_id"], "2026-06-16")
# Any other product:
data = client.get("SomeOtherProduct", "some/path", params={"q": "x"})
one_report = client.request(
"GET",
"/v1/one-report/fs/2021/financial_statement/C0000000013",
)
digital_asset = client.request(
"POST",
"/v1/digital-asset/profile/intermediary",
json_body={"IntermediaryName": ""},
)
for row in client.request_cursor_paginated("/v2/fund/factsheet/performance"):
...The client handles the subscription-key header, a sliding-window rate limiter
(default 5000 calls / 300 s with >=16 ms between requests, matching the current
SEC portal), retries throttle/5xx responses with Retry-After (the SEC
gateway throttles with HTTP 421), and treats 204 / empty 200 as "no data".
The scripts/ folder includes resumable research utilities for SEC Fund v2
data. They read SEC_FUND_KEY from the environment and write local artifacts
under outputs/, which is intentionally git-ignored.
export SEC_FUND_KEY="your-v2-fund-key"
# 1) Export the full fund profile universe once.
python scripts/sec_fund_research_export.py profiles \
--out outputs/sec-active-passive/raw
# 2a) Build the latest factsheet active-vs-passive study for domestic Thai equity funds.
python scripts/sec_active_passive_study.py all \
--raw-dir outputs/sec-active-passive/raw \
--out outputs/sec-active-passive/study
# 2b) Or include every equity fund, including foreign/feeder/global equity funds.
python scripts/sec_active_passive_study.py all \
--raw-dir outputs/sec-active-passive/raw \
--out outputs/sec-active-passive/study-all-equity \
--include-foreign-equityBy default the study filters policy_desc = "ตราสารทุน" and
invest_country_flag = 3. Add --include-foreign-equity to include every fund
where policy_desc = "ตราสารทุน" across all invest_country_flag values. It
classifies active/passive from management_style, and compares SEC factsheet
ผลตอบแทนกองทุนรวม against ผลตอบแทนตัวชี้วัด for 1Y/3Y/5Y/10Y. Outputs
include latest_return_pairs.csv, summary_by_horizon_style.csv, and a
QuantSeras-style HTML report under the selected --out directory.
Defaults to https://api.sec.or.th. If the SEC moves the gateway during the
2026 portal migration, override it without touching code:
export SEC_API_BASE_URL="https://new-host.sec.or.th"This repo is also a Codex plugin (.codex-plugin/plugin.json) with a
sec-open-api skill. The skill includes endpoint references for bond, fund,
PVD, digital asset, license, and One Report APIs.
codex plugin marketplace add nutdnuy/sec-opendata-thThen enable/install sec-opendata from the Codex plugin UI. The repo includes
.agents/plugins/marketplace.json so Codex can read it as a marketplace, and
.codex-plugin/plugin.json so the plugin root is valid.
The repo is a Claude marketplace and plugin. Add this GitHub repository as a marketplace in Claude Code:
/plugin marketplace add https://github.com/nutdnuy/sec-opendata-th
/plugin install sec-opendata
The Claude UI "Add marketplace" field should use the same GitHub URL:
https://github.com/nutdnuy/sec-opendata-th. The marketplace manifest lives at
.claude-plugin/marketplace.json, and the plugin manifest lives at
.claude-plugin/plugin.json.
Then in Claude Code:
/sec-categories/sec-fund-search K-CHANGE/sec-request GET /v1/one-report/fs/2021/financial_statement/C0000000013/sec-funds KASIKORN/sec-nav <proj_id> 2026-06-16/sec-fund-info <proj_id> <top5_period>
Or just ask in natural language — the sec-open-api skill triggers on Thai
SEC Open Data requests and runs the CLI for you.
pip install -e ".[dev]"
pytest # fully mocked — no live subscription key neededMIT