lk-tax
Sri Lankan tax rules as code. APIT, income tax, withholding tax, VAT and SSCL, calculated from the rules in force on any date since 1 January 2023, with the Inland Revenue Department source for every answer.
>>> import lk_tax
>>> r = lk_tax.apit_monthly(350_000, on="2026-09-30")
>>> r.tax
Decimal('32500.00')
>>> r.rule.effective_from, r.rule.sources[0].title
(datetime.date(2025, 4, 1), 'Notice PN/IT/2025-01: revision of income tax rates from 01.04.2025')
>>> lk_tax.apit_monthly(350_000, on="2025-03-31").tax # the old rates
Decimal('52500.00')
Why
Sri Lankan tax rules change often, and usually mid-year. In 2026 alone the SSCL registration threshold dropped, VAT on financial services went to 20.5%, VAT on foreign digital services started, and the list of professionals subject to withholding tax grew. Payroll and accounting code that hard-codes "the current rate" is wrong for every past period, and it breaks again at the next change.
lk-tax stores each rule as versioned data with an effective date and an official source, so a question like "what was the tax on this salary in March 2024?" gets the right answer, and every answer shows the law it came from.
Checked against the IRD's own tables
The IRD publishes APIT Tax Table No. 01 as a 280-page PDF. lk-tax is tested against
every row of the official 2023/24 and 2025/26 tables, 75,590 rows in all
(tests/test_apit_official_tables.py), with both ends of every income range checked.
Doing that found something: the table rounds to whole rupees in a specific way
(each range ends where the formula reaches that rupee, rounded to the nearest rupee), and in
one place it departs from its own formula. For salaries of Rs. 275,005 and 275,006 the
2025/26 table deducts Rs. 12,501 where the formula gives Rs. 12,502. Employers are told
to use the table, so tax reproduces the table exactly, exact_tax gives the formula
amount, and the difference is recorded, with an explanation, in the rule data.
What it covers
| Tax | Functions | Rules since 1 Jan 2023 |
|---|---|---|
| APIT (monthly salary tax) | apit_monthly(income, on) |
Rs. 100k/month relief → Rs. 150k from 1 Apr 2025 |
| Personal income tax | income_tax_annual(income, "2025/26") |
Y/A 2023/24 onwards |
| Withholding tax | wht.calculate(type, amount, on, month_total=, payer_bears_tax=) |
11 payment types; interest 5% → 10% on 1 Apr 2025 |
| VAT | vat.add, vat.extract, vat.registration |
15% → 18% on 1 Jan 2024; financial services 20.5% from 1 Jul 2026 |
| SSCL | sscl.levy(turnover, activity, on), sscl.registration |
threshold Rs. 120M → 60M → 36M |
Every result is a dataclass carrying the amounts, a breakdown, and rule: the version
used, its effective_from / effective_to, and its IRD sources.
Some details handled from the IRD circulars:
- WHT on service fees and rent applies only when the month's total to that person exceeds Rs. 100,000, and then to the full payment, not just the excess.
- Grossing up: if the payer covers the WHT, the invoice is treated as the net amount.
- WHT is calculated excluding VAT. Lottery winnings up to Rs. 500,000 are exempt.
- The 2022 VAT threshold test was "Rs. 20 million or more"; later ones are "exceeds".
- Money is
Decimalthroughout; floats are converted via their decimal text, so200_000.10never becomes200000.0999….
Install
pip install lk-tax # the library, no dependencies
pip install "lk-tax[api]" # plus the HTTP API and web calculator
uvicorn lk_tax.api:app # → http://localhost:8000 (calculator), /docs (API)
Or run the calculator with Docker: docker build -t lk-tax . && docker run -p 8000:8000 lk-tax.
The rules are data
Rules live in src/lk_tax/rules/*.toml. A change in the law is a new version, not an edit:
[[versions]]
effective_from = 2026-07-01
note = "SSCL (Amendment) Act No. 10 of 2026."
rate = 0.025
quarter_threshold = 9_000_000
annual_threshold = 36_000_000
sources = [
{ title = "Notice PN/SSCL/2026-04/1: SSCL (Amendment) Act No. 10 of 2026", url = "https://www.ird.gov.lk/..." },
]
Tests enforce that versions are in date order, start at the coverage date, and cite an
ird.gov.lk source. How every number was researched is written up in
research/RATES.md.
Not covered (yet)
- APIT tables other than No. 01: lump sums, one-off payments, secondary employment, non-residents, joining or leaving mid-year.
- Deciding whether a particular supply is VAT-exempt or zero-rated (you pass the type).
- SSCL on financial services, which is based on value addition.
- Anything before 1 January 2023.
Disclaimer
lk-tax is not tax advice. Rules are transcribed from IRD publications, and each result links to the exact source so you can check it. If you find a rule that's wrong or missing, please open an issue with the IRD notice.
License
MIT
Metadata
Release files for lk-tax 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| lk_tax-0.1.0.tar.gz | 152.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| lk_tax-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 177.8 kB
Release files / lk_tax-0.1.0.tar.gz
| Download URL | lk_tax-0.1.0.tar.gz |
|---|---|
| Size | 152.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
aa29e61fbc175a947f5db48c36f886c5ac140fb1aac8e6fb9cd517c265c949a7
|
|
BLAKE2b-256 checksum How to use checksums |
1d441e0fc0bd76e51b4522cb61b4b68848de4dbdcf47b08dd0c669549e0ace6b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / lk_tax-0.1.0-py3-none-any.whl
| Download URL | lk_tax-0.1.0-py3-none-any.whl |
|---|---|
| Size | 25.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6d513f209736b2ad8b9daeb98c5a8cae99c5b136629e97af9e2af8a43d925b64
|
|
BLAKE2b-256 checksum How to use checksums |
2d88eaa27320db439eb2540ba85dd87105cea7fdb58454963980813534eda621
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|