Colombo Stock Exchange annual-report retrieval research

Research run: 30 August 2026. The local stocks SQLite database was opened read-only; nothing was written.

Result

CSE has a usable, public, undocumented issuer-report index. The scalable official path is:

  1. Resolve the issuer in GET https://www.cse.lk/api/cntSecurity.
  2. POST its securityId, not the security ID returned by allSecurityCode, to POST https://www.cse.lk/api/getFinancialAnnouncement.
  3. Select annual-report candidates from fileText, then download each path from the CSE CDN.
  4. Read the report to establish fiscal year, reporting scope, and actual dividends paid. The index metadata is insufficient for those decisions.

The APIs are used by CSE’s public Financial Reports page. They require neither authentication nor an API key in these tests.

Endpoints and response contract

Issuer lookup

curl -sS https://www.cse.lk/api/cntSecurity

The response is an object with status, statusCode, and content. Each content item has:

{
  "securityId": 366,
  "name": "CENTRAL FINANCE COMPANY PLC",
  "symbol": "CFIN",
  "boardId": 0,
  "deleted": 0
}

For the three test tickers:

Local/CSE ticker Bare CSE symbol cntSecurity.securityId
CFIN.N0000 CFIN 366
DIAL.N0000 DIAL 389
CTC.N0000 CTC 460

GET https://www.cse.lk/api/allSecurityCode is useful to enumerate tradeable full ticker codes; its items are {id,name,symbol,active}. Its IDs are different: CFIN 447, DIAL 471, CTC 381. Passing those IDs to the financial-report API returned zero records. Do not join the two endpoints on id.

Filing lookup

curl -sS -X POST \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data 'companyIds=366&fromDate=2010-01-01&toDate=2026-12-31' \
  https://www.cse.lk/api/getFinancialAnnouncement

Parameters are:

  • companyIds: one cntSecurity.securityId; despite the plural name, a single decimal ID works.
  • fromDate, toDate: ISO YYYY-MM-DD. They are optional, but explicit broad windows are required for history.

Response shape:

{
  "reqFinancialAnnouncemnets": [{
    "id": 48232,
    "path": "cmt/upload_report_file/366_1749119928347.pdf",
    "manualDate": 1748370600000,
    "uploadedDate": "05 Jun 2025 04:08:48 PM",
    "fileText": "Annual Report 2024/25",
    "name": "CENTRAL FINANCE COMPANY PLC",
    "symbol": "CFIN",
    "logoUrl": "upload_logo/366_1601449552.jpeg",
    "authorizedDate": null
  }]
}

manualDate is Unix milliseconds, but is not reliable as either publication or fiscal-period date. Examples: DIAL 2018 has it null; CFIN has individual interim records with implausible dates; several reports share the same manual date. uploadedDate is a string, authorizedDate can be null, and fileText is free text. Derive the fiscal year from the PDF, not these fields.

PDF URL rule

Use this canonical construction; it handles both current and historic response paths:

https://cdn.cse.lk/cmt/ + path with a leading "cmt/" removed

Thus both cmt/upload_report_file/366_1749119928347.pdf and historic upload_report_file/366_1559875540282.pdf become valid CDN URLs. I verified the latter returns 200 application/pdf.

Examples tested:

Test coverage

One 2010-01-01 to 2026-12-31 request per issuer returned the following total filings / titles containing “annual report”:

Ticker Total filings “annual report” title matches Coverage observed
CFIN 85 19 2011/12–2025/26, including errata and an AGM notice falsely matched by title
DIAL 134 13 2012–2025 except the title pattern misses 2021
CTC 75 14 2012–2025

DIAL’s 2021 PDF is a 176-page document whose first page says Annual Report 2021, even though the CSE title calls it an “Annual Financial Report”. Conversely, CFIN title matches include an erratum and an annual-report notice, not just reports. Title regex is therefore only candidate generation.

Downloaded PDF checks:

PDF Pages Text extraction
CFIN 2024/25 396 good
DIAL 2024 268 good
CTC 2024 188 good
DIAL 2021 Annual Financial Report 176 good; identifies itself as Annual Report 2021

Are the reports enough for a 5/10-year fundamentals series?

Issuer Net assets / equity Parent-attributable earnings Paid dividends Verdict
CFIN Yes: Decade at a Glance, printed pp. 376–379, gives “Funds attributable to equity holders of the parent” for 2016–25. Yes: same table gives “Attributable to equity holders of the parent” for 2016–25. Not safely from the decade table alone. It says “Gross dividends paid”, but FY 2025 shows Rs. 1.478bn while the current-year cash-flow statement shows Rs. 0.958bn and the report separately calls the Rs. 3.75 final dividend proposed/to be paid in July 2025. Strong 10-year equity and owner-profit source; use each cash-flow statement/payment schedule for paid dividends.
DIAL Partly: printed p. 237 has a five-year Group “Shareholders funds” series, 2020–24. No for a clean five-year owner-attributable series: p. 237 reports Group profit after tax, while the audited statement separately reports profit attributable to owners for only 2024/23. No: p. 238 has dividend per share, not a paid-cash series. The audited cash-flow statement (p. 148) has current/previous paid amounts. Useful five-year shortcut for parent equity only; ingest annual audited statements for owner earnings and paid dividends.
CTC Only 2024/23 comparisons in Year in Numbers and audited balance sheet (printed pp. 24 and 129). It is a single-company report, so total equity is the shareholder equity. Only 2024/23; the EPS note identifies 2024 profit attributable to shareholders as Rs. 29.643bn. Current/previous audited cash flow (p. 147) gives cash Dividends paid; 2024 is Rs. 26.905bn. It differs from declared/appropriated dividends (Rs. 30.298bn). No five- or ten-year summary. Retrieve a report per year.

Important scope rule: CFIN’s ten-year dividend series is Company; its parent-attributable earnings and parent funds are Group. DIAL’s five-year table is Group, and CTC is Company. Preserve that reported scope rather than treating labels as interchangeable.

Limitations