Fetch company and fund disclosures from https://www.kap.org.tr
Reason this release was yanked:
buggy
Project description
kap-client
kap-client is a type-safe Python client for KAP (Kamuyu Aydınlatma Platformu) — Turkey's Public Disclosure Platform.
Fetch company and fund disclosures, browse investment fund lists, and download disclosure attachments — all through a clean, synchronous, Pythonic API. Perfect for building financial dashboards, compliance tools, and investment research apps.
v0.1 — Production-ready, fully tested, zero breaking changes.
✨ Features
- 📢 Company disclosures — search by BIST ticker or KAP OID, with optional subject filtering
- 📋 Fund disclosures — search across all KAP fund groups (YF, BYF, EYF, OKS, GMF, …)
- 🏢 Company & fund lists — fully enumerated and cached per session
- 📎 Attachment parsing — automatically extracts file links from disclosure HTML pages
- 🔎 Subject filtering — narrow results using
FundSubjectOID constants - ⏱️ Retry & back-off — 3 attempts with exponential back-off;
RateLimitErroron 429 - 🛡️ Type-safe — full Pydantic v2 validation, frozen domain models, mypy compatible
- ⚡ Minimal dependencies — only
httpxandpydantic - 🐍 Modern Python — 3.10+ with context manager support
📦 Installation
pip install kap-client
Other package managers
# uv
uv pip install kap-client
# Poetry
poetry add kap-client
🚀 Quick start
from kap_client import Kap, FundGroup
with Kap() as kap:
# Fetch THYAO disclosures for Q1 2024
disclosures = kap.fetch_disclosures("THYAO", "2024-01-01", "2024-03-31")
for d in disclosures:
print(d.publish_datetime, d.subject, d.url)
# Download attachments from the first disclosure
if disclosures and disclosures[0].has_attachment:
attachments = kap.fetch_attachments(disclosures[0].index)
for a in attachments:
print(a.filename, a.url)
# Browse active Yatırım Fonları
funds = kap.fetch_funds(FundGroup.YATIRIM_FONLARI)
print(f"{len(funds)} active YF funds found")
📖 Usage Examples
Basic: Fetch company disclosures by ticker
from kap_client import Kap
with Kap() as kap:
disclosures = kap.fetch_disclosures("THYAO", "2024-01-01", "2024-12-31")
for d in disclosures:
print(f"[{d.publish_datetime:%Y-%m-%d %H:%M}] {d.subject}")
print(f" → {d.url}")
Disclosures with attachment download
from kap_client import Kap
with Kap() as kap:
disclosures = kap.fetch_disclosures("EREGL", "2024-01-01", "2024-12-31")
for d in disclosures:
if d.has_attachment:
attachments = kap.fetch_attachments(d.index)
for a in attachments:
print(f" {a.filename} → {a.url}")
Resolve ticker to Company object
from kap_client import Kap
with Kap() as kap:
co = kap.find_company("TCELL")
print(f"{co.name} (OID: {co.oid})")
# Use OID directly for subsequent queries — no extra HTTP request
disclosures = kap.fetch_disclosures(co.oid, "2024-01-01", "2024-03-31")
print(f"{len(disclosures)} disclosures found")
Batch: Reuse one context manager for multiple companies
from kap_client import Kap
tickers = ["THYAO", "EREGL", "TCELL", "AKBNK"]
with Kap() as kap:
# Company list is fetched once and cached for all find_company() calls
for ticker in tickers:
co = kap.find_company(ticker)
disclosures = kap.fetch_disclosures(co.oid, "2024-01-01", "2024-12-31")
print(f"{ticker}: {len(disclosures)} disclosures")
Browse and filter investment funds
from kap_client import Kap, FundGroup
with Kap() as kap:
# List active Yatırım Fonları (YF)
funds = kap.fetch_funds(FundGroup.YATIRIM_FONLARI)
print(f"{len(funds)} active YF funds")
# Include liquidated funds too
all_funds = kap.fetch_funds(FundGroup.YATIRIM_FONLARI, include_liquidated=True)
inactive = [f for f in all_funds if not f.is_active]
print(f"{len(inactive)} liquidated funds")
# List portfolio management companies for BYF group
members = kap.fetch_fund_members(FundGroup.BORSA_YATIRIM_FONLARI)
for m in members:
print(m.name)
Fund disclosures
from kap_client import Kap, FundGroup
with Kap() as kap:
funds = kap.fetch_funds(FundGroup.YATIRIM_FONLARI)
target = next(f for f in funds if f.code == "AFA")
disclosures = kap.fetch_fund_disclosures(
fund=target,
fund_group=FundGroup.YATIRIM_FONLARI,
start_date="2024-01-01",
end_date="2024-12-31",
)
for d in disclosures:
print(f"[{d.publish_datetime:%Y-%m-%d}] {d.subject}")
Filter by subject using FundSubject
from kap_client import Kap, FundSubject
with Kap() as kap:
disclosures = kap.fetch_disclosures(
"THYAO",
"2024-01-01",
"2024-12-31",
subject_oids=[FundSubject.OZEL_DURUM_ACIKLAMASI.value],
)
print(f"{len(disclosures)} özel durum açıklaması")
Integration: Export to Pandas
import pandas as pd
from kap_client import Kap
with Kap() as kap:
disclosures = kap.fetch_disclosures("THYAO", "2024-01-01", "2024-12-31")
df = pd.DataFrame([
{
"date": d.publish_datetime.date(),
"subject": d.subject,
"type": d.disclosure_type,
"has_attachment": d.has_attachment,
"is_corrective": d.is_corrective,
"url": d.url,
}
for d in disclosures
])
print(df.head())
print(f"\nTotal disclosures: {len(df)}")
print(f"With attachments: {df['has_attachment'].sum()}")
Error handling
from kap_client import Kap, KapError, RateLimitError, EmptyResponseError, CompanyNotFoundError
try:
with Kap() as kap:
co = kap.find_company("XXXXXX")
disclosures = kap.fetch_disclosures(co.oid, "2024-01-01", "2024-12-31")
except CompanyNotFoundError as e:
print(f"Ticker not found: {e.ticker}")
except EmptyResponseError:
print("No disclosures in selected date range")
except RateLimitError as e:
print(f"Rate limited. Retry after {e.retry_after} seconds")
except KapError as e:
print(f"KAP error: {e}")
📚 API Reference
Kap(timeout: float = 30.0)
Context manager for managing HTTP connections and caches.
with Kap(timeout=15.0) as kap:
disclosures = kap.fetch_disclosures("THYAO", "2024-01-01", "2024-12-31")
| Parameter | Type | Default | Description |
|---|---|---|---|
timeout |
float |
30.0 |
Per-request HTTP timeout in seconds |
Kap.fetch_companies(*, refresh=False) -> list[Company]
Returns all KAP-registered companies. Results are cached per instance — the second call returns the cached list without a network request. Pass refresh=True to force a fresh fetch.
with Kap() as kap:
companies = kap.fetch_companies()
print(f"{len(companies)} companies")
| Parameter | Type | Default | Description |
|---|---|---|---|
refresh |
bool |
False |
Bypass cache and fetch fresh data |
Kap.find_company(ticker, *, refresh=False) -> Company
Resolve a BIST ticker to a Company object. Ticker matching is case-insensitive.
with Kap() as kap:
co = kap.find_company("thyao") # same as "THYAO"
print(co.oid, co.name)
Raises CompanyNotFoundError if the ticker is not in the KAP member list.
Kap.fetch_funds(fund_group, *, include_liquidated=False, refresh=False) -> list[Fund]
Returns the fund list for a given group. Results are cached per (group, include_liquidated) combination.
with Kap() as kap:
funds = kap.fetch_funds(FundGroup.YATIRIM_FONLARI)
funds = kap.fetch_funds("YF") # string value accepted
all_funds = kap.fetch_funds("EYF", include_liquidated=True)
| Parameter | Type | Default | Description |
|---|---|---|---|
fund_group |
FundGroup | str |
— | Fund group enum or string value ("YF", "BYF", …) |
include_liquidated |
bool |
False |
Also return liquidated / tasfiye funds |
refresh |
bool |
False |
Bypass cache and fetch fresh data |
Kap.fetch_fund_members(fund_group, *, refresh=False) -> list[Company]
Returns portfolio management companies (kurucu/yönetici) for the given fund group.
Kap.fetch_disclosures(company, start_date, end_date, *, subject_oids=None) -> list[Disclosure]
Fetch company disclosures for a date range. Returns results sorted newest first.
with Kap() as kap:
# By ticker
disclosures = kap.fetch_disclosures("THYAO", "2024-01-01", "2024-12-31")
# By OID (no extra company lookup)
disclosures = kap.fetch_disclosures(co.oid, "2024-01-01", "2024-12-31")
# With subject filter
disclosures = kap.fetch_disclosures(
"THYAO", "2024-01-01", "2024-12-31",
subject_oids=[FundSubject.OZEL_DURUM_ACIKLAMASI.value],
)
| Parameter | Type | Default | Description |
|---|---|---|---|
company |
str |
— | BIST ticker ("THYAO") or raw KAP OID hex string |
start_date |
str | date | datetime |
— | Range start, inclusive ("YYYY-MM-DD" or date/datetime) |
end_date |
str | date | datetime |
— | Range end, inclusive |
subject_oids |
list[str] | None |
None |
Optional FundSubject OID values to filter results |
Kap.fetch_fund_disclosures(fund, fund_group, start_date, end_date, *, subject_oids=None) -> list[Disclosure]
Fetch fund disclosures for a date range. Returns results sorted newest first.
| Parameter | Type | Default | Description |
|---|---|---|---|
fund |
Fund | str |
— | Fund instance or raw fund OID hex string |
fund_group |
FundGroup | str |
— | Required even when passing a Fund object |
start_date |
str | date | datetime |
— | Range start, inclusive |
end_date |
str | date | datetime |
— | Range end, inclusive |
subject_oids |
list[str] | None |
None |
Optional subject filter |
Kap.fetch_attachments(disclosure_index: int) -> list[Attachment]
Fetches the HTML detail page for a disclosure and parses all file attachment links. Returns an empty list if the disclosure has no attachments.
with Kap() as kap:
disclosures = kap.fetch_disclosures("THYAO", "2024-01-01", "2024-12-31")
for d in disclosures:
if d.has_attachment:
for a in kap.fetch_attachments(d.index):
print(a.filename, a.url)
🗂️ Domain Models
Disclosure
| Field | Type | Description |
|---|---|---|
index |
int |
Unique KAP disclosure number |
publish_datetime |
datetime |
Publication timestamp |
company_name |
str |
Issuer name |
stock_codes |
str |
BIST ticker(s); empty for non-listed issuers |
subject |
str |
Disclosure topic |
disclosure_type |
str |
Type classification |
has_attachment |
bool |
Whether file attachments are available |
is_late |
bool |
Filed after deadline |
is_corrective |
bool |
Correction of a prior disclosure |
is_english |
bool |
English-language disclosure |
url |
str |
Full URL to the KAP disclosure page |
Attachment
| Field | Type | Description |
|---|---|---|
filename |
str |
Original file name |
url |
str |
Direct download URL |
Company
| Field | Type | Description |
|---|---|---|
oid |
str |
KAP hex OID (32 chars) |
name |
str |
Official registered name |
ticker |
str |
BIST stock code; empty for non-listed entities |
Fund
| Field | Type | Description |
|---|---|---|
oid |
str |
KAP hex OID (32 chars) |
code |
str |
Short fund code (e.g. "AFA") |
title |
str |
Full fund name |
fund_type |
str |
e.g. "Hisse Senedi Fonu" |
fund_group |
FundGroup |
Enum value |
is_active |
bool |
False for liquidated funds |
🏷️ FundGroup Enum
| Value | Label |
|---|---|
FundGroup.BORSA_YATIRIM_FONLARI |
"BYF" — Borsa Yatırım Fonları |
FundGroup.YATIRIM_FONLARI |
"YF" — Yatırım Fonları |
FundGroup.EMEKLILIK_YATIRIM_FONLARI |
"EYF" — Emeklilik Yatırım Fonları |
FundGroup.OKS_EMEKLILIK_YATIRIM_FONLARI |
"OKS" — OKS Emeklilik Yatırım Fonları |
FundGroup.YABANCI_YATIRIM_FONLARI |
"YYF" — Yabancı Yatırım Fonları |
FundGroup.VARLIK_FINANSMAN_FONLARI |
"VFF" — Varlık Finansman Fonları |
FundGroup.KONUT_FINANSMAN_FONLARI |
"KFF" — Konut Finansman Fonları |
FundGroup.GAYRIMENKUL_YATIRIM_FONLARI |
"GMF" — Gayrimenkul Yatırım Fonları |
String values (e.g. "YF") are accepted everywhere a FundGroup is expected.
⚠️ Exceptions
| Exception | When raised |
|---|---|
KapError |
Base exception for all kap-client errors |
RateLimitError |
HTTP 429 after all retries exhausted; has .retry_after: float | None |
EmptyResponseError |
Successful response but empty data list |
CompanyNotFoundError |
Ticker not found in KAP member list; has .ticker: str |
🛠️ Development
git clone https://github.com/semudu/kap-client
cd kap-client
pip install -e ".[dev]"
make test # run tests
make lint # ruff lint + format check
make typecheck # mypy strict
License
MIT
Project details
Release history Release notifications | RSS feed
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 kap_client-1.0.0.tar.gz.
File metadata
- Download URL: kap_client-1.0.0.tar.gz
- Upload date:
- Size: 62.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2773db1b2b2647c47989454dd80cb4017c79568a10ea139780b08211d8b36263
|
|
| MD5 |
a845213ab5ea2796bc7784edd617f5be
|
|
| BLAKE2b-256 |
d683c205558b0bf0ca3913eae613bd1ef7934130f1608eac4b92b4b2c62bb0a1
|
Provenance
The following attestation bundles were made for kap_client-1.0.0.tar.gz:
Publisher:
release.yml on semudu/kap-client
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
kap_client-1.0.0.tar.gz -
Subject digest:
2773db1b2b2647c47989454dd80cb4017c79568a10ea139780b08211d8b36263 - Sigstore transparency entry: 1422079212
- Sigstore integration time:
-
Permalink:
semudu/kap-client@5150ce7e0f589860da5a5d3b7b76ba215050f463 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/semudu
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5150ce7e0f589860da5a5d3b7b76ba215050f463 -
Trigger Event:
push
-
Statement type:
File details
Details for the file kap_client-1.0.0-py3-none-any.whl.
File metadata
- Download URL: kap_client-1.0.0-py3-none-any.whl
- Upload date:
- Size: 18.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
779951b82c3db5b20fb1a6836efab15fc50ac770e0e823900d18c8a37455ae52
|
|
| MD5 |
f67a1d0fdd0ec209f9ed9a39a0e20f5c
|
|
| BLAKE2b-256 |
437e81d9b46c202df624a9ccc5f8ee3113b3892a2c119449625967023a4b2ec2
|
Provenance
The following attestation bundles were made for kap_client-1.0.0-py3-none-any.whl:
Publisher:
release.yml on semudu/kap-client
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
kap_client-1.0.0-py3-none-any.whl -
Subject digest:
779951b82c3db5b20fb1a6836efab15fc50ac770e0e823900d18c8a37455ae52 - Sigstore transparency entry: 1422079291
- Sigstore integration time:
-
Permalink:
semudu/kap-client@5150ce7e0f589860da5a5d3b7b76ba215050f463 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/semudu
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5150ce7e0f589860da5a5d3b7b76ba215050f463 -
Trigger Event:
push
-
Statement type: