Skip to main content

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:

  1. 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.
  2. Fund it with the USDC you intend to spend (a few dollars covers hundreds of summaries).
  3. 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:

  1. GET the endpoint without payment → server returns 402 with an x402 v2 accepts block describing exact amount, USDC contract, network, recipient, and expiry.
  2. SDK signs an EIP-3009 TransferWithAuthorization message locally — your private key never leaves the process.
  3. SDK base64-encodes the signed payload into the PAYMENT-SIGNATURE header (x402 v2 transport spec) and retries the GET.
  4. 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-RESPONSE header. If the facilitator's /settle call rejects, the server fails closed per the x402 v2 spec (HTTP 402 + PAYMENT-RESPONSE failure header + empty body) so the data is never delivered unpaid; the SDK surfaces that as a PaymentError.

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 .env checked 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-3009 nonce. 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

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

koreafilings-0.3.2.tar.gz (15.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

koreafilings-0.3.2-py3-none-any.whl (17.8 kB view details)

Uploaded Python 3

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

Hashes for koreafilings-0.3.2.tar.gz
Algorithm Hash digest
SHA256 6936a38fa67f0810c6c9ec1ce29d124610d9caa99d8fa94576e59ec6d95b7508
MD5 f0e857662f6d3b0088d2316e1ce44cc7
BLAKE2b-256 d13227d9f29d6896c085468929c54f8731185ca1408da23c1f3ed19e64f6b694

See more details on using hashes here.

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

Hashes for koreafilings-0.3.2-py3-none-any.whl
Algorithm Hash digest
SHA256 31d35d729b28628b42249acd5da645315f1b634ecc96bf250ffad4698f765b27
MD5 70ab1eff67dc45fbb97ce46cb303e65c
BLAKE2b-256 114615ffecfaf494d28e76e7350e3879a70fa0dcb950c394f320611f4039ba03

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page