Python SDK for koreafilings.com — AI-summarized Korean DART disclosures, paid per call in USDC via x402.
Project description
koreafilings
Python SDK for koreafilings.com — AI-summarized Korean DART (금융감독원 전자공시) corporate disclosures, paid per call in USDC on Base via the x402 payment protocol.
No API keys. No monthly fee. No signup. The wallet that signs the payment is the identity. Free company-directory and recent-filings feeds let an agent browse before paying; paid endpoints settle on Base mainnet via Coinbase's CDP facilitator. The first call for a disclosure pays the LLM cost; every subsequent call hits the server-side cache for the same flat fee.
Install
pip install koreafilings
Quickstart — name to summary in two calls
The natural agent flow is one free call to resolve a company by name, one paid call to fetch summaries by ticker:
from koreafilings import Client
with Client(private_key="0x...", network="base") as client:
# 1. Free name → ticker resolution. Matches Korean and English
# names with trigram fuzzy search across 3,961 KRX-listed
# companies.
matches = client.find_company("Samsung Electronics")
ticker = matches[0].ticker # "005930"
# 2. Paid batch summary fetch. Price is declared dynamically in
# the 402 challenge as 0.005 × limit USDC, so the SDK signs
# the exact amount before settling.
filings = client.get_recent_filings(ticker, limit=5)
for f in filings:
print(f"[{f.importance_score}/10] {f.event_type}: {f.summary_en}")
print("paid:", client.last_settlement.tx_hash)
Example output (one filing returned verbatim from a live Base mainnet
paid call against rcpt_no=20260430800106, Samsung Electronics
quarterly dividend):
[7/10] DIVIDEND_DECISION: Samsung Electronics decided on a quarterly
cash dividend of KRW 372 per common share and KRW 372 per preferred
share, totaling KRW 2,453,315,636,604. The dividend yield is 0.2%
for common shares and 0.3% for preferred shares. The record date is
March 31, 2026, with payment scheduled for May 29, 2026.
paid: 0x<base-mainnet-tx-hash>...
The summary is built from the filing body itself (fetched lazily via
DART's /document.xml ZIP, parsed and capped at 20,000 chars) rather
than the title metadata only — quantitative events surface concrete
amounts, percentages, dates, and counterparty names directly in
summary_en. For routine governance filings with little body
content, the same field falls back to a shorter title-derived
summary; the field is always present and never empty.
If you already know the receipt number (e.g. you stored one yesterday and want to re-fetch on the same flat 0.005 USDC tier):
summary = client.get_summary("20260424900874")
Free discovery endpoints
Three endpoints carry no payment challenge so an agent can plan before spending USDC:
# 1. Search by name or ticker (Korean and English, trigram fuzzy):
matches = client.find_company("삼성전자") # Korean
matches = client.find_company("Samsung Electronics") # English
matches = client.find_company("005930") # ticker
for m in matches:
print(m.ticker, m.name_kr, m.name_en, m.market)
# 2. Direct ticker lookup:
co = client.get_company("005930")
# 3. Browse the market-wide recent feed (metadata only — no LLM
# summary, but lets the agent see what's hot before paying):
recent = client.list_recent_filings(limit=20)
for r in recent:
print(r.rcpt_no, r.ticker, r.corp_name, r.report_nm, r.rcept_dt)
Pricing
| Endpoint | Method | Price (USDC) |
|---|---|---|
client.find_company(q) |
GET /v1/companies |
free |
client.get_company(ticker) |
GET /v1/companies/{ticker} |
free |
client.list_recent_filings(limit) |
GET /v1/disclosures/recent |
free |
client.get_recent_filings(ticker, limit) |
GET /v1/disclosures/by-ticker?ticker=…&limit=… |
0.005 × limit |
client.get_summary(rcpt_no) |
GET /v1/disclosures/summary?rcptNo=… |
0.005 |
Per-result pricing on the by-ticker endpoint is declared dynamically
in the 402 response: the SDK reads accepts[0].amount from the
challenge and signs the exact charge for the requested limit.
Direct receipt-number lookup stays at a flat 0.005 USDC.
client.get_pricing() returns the live machine-readable pricing
descriptor including the current recipient wallet, network, and
USDC contract address.
What you get
get_recent_filings and get_summary return structured Summary
objects (or a list[Summary] for the batch endpoint). Each carries:
| field | type | description |
|---|---|---|
rcpt_no |
str |
DART receipt number |
summary_en |
str |
Paraphrased English summary (never verbatim) |
importance_score |
int (1–10) |
10 = M&A / insolvency, 1 = routine admin |
event_type |
str |
Canonical event taxonomy |
sector_tags |
list[str] |
GICS-style sector labels |
ticker_tags |
list[str] |
Affected KRX tickers (.KS / .KQ) |
actionable_for |
list[str] |
Audience hints (traders, long_term_investors, …) |
generated_at |
datetime |
When the summary was produced |
find_company / get_company return Company records (ticker,
corp_code, name_kr, name_en, market). list_recent_filings
returns RecentFiling records (rcpt_no, ticker, corp_name,
report_nm, rcept_dt) — metadata only, no summary.
Getting a wallet and USDC
Production use on Base mainnet:
- Create a fresh burner wallet (MetaMask → new account → export private key). Do not reuse an existing personal wallet — the private key signs real-money authorizations.
- Fund it with the USDC you intend to spend (a few dollars covers hundreds of summaries).
- Pass the 0x-prefixed key to
Client(private_key=..., network="base").
For local development against the public testnet facilitator, point the SDK at Base Sepolia:
client = Client(private_key="0x...", network="base-sepolia")
Get free Base Sepolia ETH from the Coinbase faucet and free test USDC from the Circle faucet.
How payment works
Under the hood, each paid call:
- GET the endpoint without payment → server returns 402 with
an x402 v2
acceptsblock describing exact amount, USDC contract, network, recipient, and expiry. - SDK signs an
EIP-3009
TransferWithAuthorizationmessage locally — your private key never leaves the process. - SDK base64-encodes the signed payload into the
PAYMENT-SIGNATUREheader (x402 v2 transport spec) and retries the GET. - Server verifies the signature via the x402 facilitator, submits it
on-chain, streams the JSON body back, and attaches the settlement
proof to the
PAYMENT-RESPONSEheader. If the facilitator's/settlecall rejects, the server fails closed per the x402 v2 spec (HTTP 402 +PAYMENT-RESPONSEfailure header + empty body) so the data is never delivered unpaid; the SDK surfaces that as aPaymentError.
The SDK exposes the proof as client.last_settlement so you can log
transaction hashes for your accounting system.
Error handling
from koreafilings import Client, ApiError, PaymentError, ConfigurationError
try:
matches = client.find_company("Samsung Electronics")
filings = client.get_recent_filings(matches[0].ticker, limit=5)
except PaymentError as e:
# Facilitator rejected the signed payment (bad sig, low balance,
# expired auth, network mismatch).
print("payment failed:", e.reason, e.detail)
except ApiError as e:
# Non-payment HTTP failure: 404 unknown ticker / rcpt_no, 429
# rate limit, 5xx upstream outage.
print("api error:", e.status_code, e.body)
except ConfigurationError as e:
# Bad private_key format or unknown network alias.
print("config:", e)
PaymentError.reason vocabulary
A stable string field an agent can branch on without parsing the human message. Same set as the TypeScript SDK uses:
reason |
Where it fires | Recoverable? | Recommendation |
|---|---|---|---|
empty_accepts |
402 body has no accepts[] entries — server bug |
No, terminal | Refuse this server |
network_mismatch |
402 advertises a chain ≠ client's network config |
Maybe | Reconfigure client to match server, or refuse if hostile |
payment_rejected |
Facilitator's /verify rejected the signature |
Sometimes | Check wallet key / clock skew, retry with fresh nonce |
settle_failed |
Facilitator's /settle rejected after a successful verify |
Sometimes | Wait for facilitator to clear, retry with fresh nonce |
| (facilitator-supplied) | A 402 retry returned success:false with a custom errorReason |
Depends | Inspect the message — usually transient |
The TypeScript SDK 0.1.2+ adds two more reasons (invalid_network,
asset_mismatch) tied to its KNOWN_DOMAINS allowlist; the Python
SDK soft-warns on the same conditions today and may add equivalent
hard-fail behaviour in 0.4.0. If you write cross-language error
handling, branch on reason rather than on exception subclass —
the subclass tree is identical, only the reason set differs.
Security notes
- The private key signs real-money authorizations. Do not ship it
in client-side code, do not put it in a
.envchecked into git, do not paste it in chat. Prefer a dedicated burner wallet funded with only the USDC you plan to spend. - The SDK signs locally; the key is never transmitted to koreafilings.com or to the facilitator.
- Every payment carries an
EIP-3009nonce. The facilitator refuses replays. - For Base Sepolia testing only, a fresh wallet with faucet funds is the safest pattern — nothing on that wallet has production value.
TypeScript port
A TypeScript SDK with the same surface ships at
koreafilings on npm.
If you are porting code line-by-line between Python and TypeScript,
watch for these naming differences (defaults, server caps, payment
headers, EIP-712 domain handling, and 402 → sign → retry behaviour
are byte-identical between the two — only the language-idiomatic
surface differs):
| Concept | Python | TypeScript |
|---|---|---|
| Constructor | Client(private_key=, network=) |
new KoreaFilings({ privateKey, network }) |
| Method names | find_company, list_recent_filings, get_recent_filings, get_summary, get_pricing |
findCompany, listRecentFilings, getRecentFilings, getSummary, getPricing |
| HTTP error status | ApiError.status_code |
ApiError.status |
| Settlement tx hash | client.last_settlement.tx_hash |
client.lastSettlement?.transaction |
| Settlement error reason | last_settlement.error_reason |
lastSettlement?.errorReason |
list_recent_filings arguments |
kwargs: limit=20, since_hours=24 |
options object: { limit: 20, sinceHours: 24 } |
Source & feedback
- Repo: https://github.com/OldTemple91/korea-filings-api
- Issues: https://github.com/OldTemple91/korea-filings-api/issues
- Landing: https://koreafilings.com
MIT-licensed.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file koreafilings-0.3.2.tar.gz.
File metadata
- Download URL: koreafilings-0.3.2.tar.gz
- Upload date:
- Size: 15.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6936a38fa67f0810c6c9ec1ce29d124610d9caa99d8fa94576e59ec6d95b7508
|
|
| MD5 |
f0e857662f6d3b0088d2316e1ce44cc7
|
|
| BLAKE2b-256 |
d13227d9f29d6896c085468929c54f8731185ca1408da23c1f3ed19e64f6b694
|
File details
Details for the file koreafilings-0.3.2-py3-none-any.whl.
File metadata
- Download URL: koreafilings-0.3.2-py3-none-any.whl
- Upload date:
- Size: 17.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
31d35d729b28628b42249acd5da645315f1b634ecc96bf250ffad4698f765b27
|
|
| MD5 |
70ab1eff67dc45fbb97ce46cb303e65c
|
|
| BLAKE2b-256 |
114615ffecfaf494d28e76e7350e3879a70fa0dcb950c394f320611f4039ba03
|