taxlane
Official Python SDK for TaxLane's free
Developer API — PAYE, VAT, Withholding Tax,
Companies Income Tax, Capital Gains Tax, and 5 more Nigeria tax calculators,
as typed function calls instead of hand-written urllib/curl.
- Zero runtime dependencies — stdlib
urllib.request, norequests. snake_casekeyword arguments, hand-mapped from the same field list taxlane.ng/developers documents, so a call can never drift from the live API's request shape.- No API key, no account, no rate-limit tiers to configure — the API itself has none.
Every function returns the parsed JSON response as a plain dict, not a
generated type — see Response shape below for why.
Install
pip install taxlane
If this package hasn't been published to the public PyPI registry yet (see Distribution below), install it straight from GitHub instead:
pip install git+https://github.com/quineworks/tax-lane#subdirectory=sdk-python
Quickstart
from taxlane import calculate_paye
result = calculate_paye(gross_annual_income=6_000_000, annual_rent_paid=1_200_000)
# {'taxableIncome': 5760000, 'payeTax': 826800, 'annualTax': 826800, 'monthlyTax': 68900,
# 'reliefs': {...}, 'bandBreakdown': [...]}
Every function raises TaxLaneApiError (with .message and .status) on
a non-200 response, and accepts an optional trailing base_url keyword to
point at a local/staging API instead of the production default:
from taxlane import calculate_paye, TaxLaneApiError
try:
calculate_paye(gross_annual_income='not a number', base_url='http://localhost:3000')
except TaxLaneApiError as err:
print(err.status, err.message) # 400 "grossAnnualIncome is required and must be a number"
Endpoints
calculate_paye
from taxlane import calculate_paye
calculate_paye(gross_annual_income=6_000_000, annual_rent_paid=1_200_000)
# {
# "taxableIncome": 5760000,
# "payeTax": 826800,
# "annualTax": 826800,
# "monthlyTax": 68900,
# "reliefs": { "rentRelief": 240000, "pensionRelief": 0, "nhfRelief": 0, "nhisRelief": 0, "lifeAssurancePremiumRelief": 0, "minimumWageExempt": false, "taxableTerminationBenefit": 0 },
# "bandBreakdown": [
# { "from": 800000, "to": 3000000, "rate": 0.15, "taxInBand": 330000 },
# { "from": 3000000, "to": 5760000, "rate": 0.18, "taxInBand": 496800 }
# ]
# }
Only gross_annual_income is required — every other field defaults to 0.
calculate_payroll_batch
from taxlane import calculate_payroll_batch
calculate_payroll_batch([
{"grossAnnualIncome": 6_000_000},
{"grossAnnualIncome": 900_000, "annualRentPaid": 400_000},
])
# { "results": [ { "taxableIncome": 6000000, ... }, { "taxableIncome": 820000, ... } ] }
Calculates PAYE for up to 1,000 employees in one request — results[i] is exactly what calculate_paye(**employees[i]) would return, in the same order. An invalid item raises with an employees[i].-prefixed message; there's no partial-success mode.
calculate_vat
from taxlane import calculate_vat
calculate_vat(amount=100_000, mode="exclusive")
# { "net": 100000, "vat": 7500, "gross": 107500 }
calculate_presumptive
from taxlane import calculate_presumptive
calculate_presumptive(turnover=20_000_000)
# { "turnover": 20000000, "taxOwed": 200000, "belowFloor": false, "monthlySetAside": 16666.666666666668, "turnoverToFloor": 0 }
calculate_direct_assessment
from taxlane import calculate_direct_assessment
calculate_direct_assessment(
turnover=10_000_000,
allowable_expenses=3_000_000,
qualifying_capital_expenditure=1_000_000,
capital_allowance_asset_class="plantAgriculturalEquipmentFurniture",
)
# { "turnover": 10000000, "allowableExpenses": 3000000, "capitalAllowance": 200000, "taxableProfit": 6800000, "taxOwed": 1014000, "effectiveRate": 0.14911764705882352 }
calculate_cit
from taxlane import calculate_cit
calculate_cit(
turnover=30_000_000,
fixed_assets=100_000_000,
is_professional_services=False,
taxable_profit=5_000_000,
assessable_profit=8_000_000,
)
# {
# "eligibility": { "turnover": 30000000, "fixedAssets": 100000000, "isProfessionalServices": false, "turnoverEligible": true, "fixedAssetsEligible": true, "eligible": true, "rate": 0 },
# "standardRate": { "taxableProfit": 5000000, "annualTax": 1500000 },
# "developmentLevy": { "assessableProfit": 8000000, "developmentLevy": 320000 }
# }
eligibility is always returned. standardRate is included only when
taxable_profit is sent; developmentLevy is included only when
assessable_profit is sent — the three are independently computed.
calculate_wht
from taxlane import calculate_wht
calculate_wht(transaction_type="professionalFees", amount=1_000_000, mode="gross")
# { "transactionType": "professionalFees", "mode": "gross", "amount": 1000000, "rate": 0.05, "gross": 1000000, "withheld": 50000, "net": 950000 }
calculate_employer_cost
from taxlane import calculate_employer_cost
calculate_employer_cost(
annual_basic_salary=6_000_000,
annual_housing_allowance=1_000_000,
annual_transport_allowance=500_000,
)
# { "annualGrossPay": 7500000, "pensionablePay": 7500000, "employerPensionContribution": 750000, ..., "employeePaye": { ... } }
calculate_cgt
from taxlane import calculate_cgt
calculate_cgt(
taxpayer_type="individual",
asset_type="property",
disposal_proceeds=50_000_000,
cost_base=30_000_000,
)
# { "chargeableGain": 20000000, "exempt": false, "cgtLiability": 3630000 }
digital_asset_category is required when asset_type is "digital-asset",
ignored otherwise.
calculate_stamp_duty
from taxlane import calculate_stamp_duty
calculate_stamp_duty(instrument_type="tenancy", amount=12_000_000)
# { "instrumentType": "tenancy", "amount": 12000000, "rate": 0.0078, "duty": 93600, "exempt": false }
calculate_import_duty
from taxlane import calculate_import_duty
calculate_import_duty(fob=5_000_000, freight=300_000, insurance=100_000, band="20")
# { "fob": 5000000, "freight": 300000, "insurance": 100000, "cif": 5400000, "band": "20", "dutyRate": 0.2, "duty": 1080000, "surcharge": 75600, "etls": 27000, "fcs": 200000, "vat": 508695, "totalLandedCost": 7291295 }
Response shape
Every function returns the parsed JSON response body as a plain dict,
not a generated dataclass/TypedDict/pydantic model. The JS/TS SDK's
fully-typed responses come free by importing the real TS type across a
same-language package boundary — Python has no equivalent import, and
hand-duplicating every response shape in a second, Python-only schema would
be exactly the kind of extra place to maintain and drift from that SDK
avoided in the first place. A plain dict is also the established pattern
Paystack's (paystack-python)
and Flutterwave's (rave-python)
own Python SDKs use for response bodies. Ships py.typed (PEP 561 marker)
so type checkers (mypy/pyright) still pick up every function's request
signature in consuming projects — only the response is untyped.
Errors
A missing or non-numeric/non-boolean required field, or an invalid enum
value, raises TaxLaneApiError with a .status of 400 and a .message
describing the offending field. See the full field-by-field reference at
taxlane.ng/developers or the
OpenAPI spec.
Limits
No API key required, no daily cap. The underlying API is limited to 50 requests/second (100 burst) at the shared Gateway level to keep the service available for everyone — this SDK does no client-side retry/backoff, since there's no rate limit to work around on a per-caller basis.
Distribution
Published to the public PyPI registry as taxlane, when a fleet PyPI
publisher account exists to publish it under. Until then, install directly
from this repo's sdk-python/ subdirectory:
pip install git+https://github.com/quineworks/tax-lane#subdirectory=sdk-python
License
MIT — see the repo root LICENSE.
Metadata
Release files for taxlane 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 | Provenance | Actions |
|---|---|---|---|---|
| taxlane-0.1.0.tar.gz | 15.8 kB | Without provenance | Details for taxlane-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| taxlane-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for taxlane-0.1.0-py3-none-any.whl |
Total release size: 31.3 kB
Release files / taxlane-0.1.0.tar.gz
| Download URL | taxlane-0.1.0.tar.gz |
|---|---|
| Size | 15.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bf06120743577f5c3a361debf2a84506ec6513fa6a91fffd389a8c6aff61b054
|
|
BLAKE2b-256 checksum How to use checksums |
a388757fc7ca83e8fde1959928ea539d104c6610dbbb3e3365c14f8a8a1d0425
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Release files / taxlane-0.1.0-py3-none-any.whl
| Download URL | taxlane-0.1.0-py3-none-any.whl |
|---|---|
| Size | 15.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6d880f25b2ba903c2dc9b0d07aa5d76f5e9c37c1acd6924f1a379ada79a83e15
|
|
BLAKE2b-256 checksum How to use checksums |
bf9bd817d531e3061497a15631b66fc1acb335a71a2aea72c664c68a6cc5fdbd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Security for taxlane 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.