API Documentation
The calculator's REST API is public, requires no authentication, and returns the same numbers the web form would. Anonymous traffic is throttled at 60 requests per minute per IP. A higher-volume authenticated tier will arrive with the B2B widget rollout.
Endpoints
Choose an endpoint that matches the calculator you'd use on the website:
| Endpoint | Calculator |
|---|---|
POST /api/calculate/paye/ |
Standard PAYE (employee salary) |
POST /api/calculate/limited_co/ |
Limited Company — director's salary + dividends |
POST /api/calculate/umbrella/ |
Umbrella Company — day rate to PAYE |
POST /api/calculate/ir35/ |
IR35 inside vs outside comparison |
POST /api/calculate/sole_trader/ |
Sole Trader / Self-Employed — Class 4 NI + Self Assessment |
GET /api/tax-rates/ |
Read-only inventory of seeded tax-year rate snapshots |
Status codes
200— calculation successful, JSON body matches the result table for that calculator.400— validation error (unknown mode, invalid tax code, missing required field, zero day rate, etc.). Body:{"error": "…"}.404— the requestedtax_yearisn't seeded in this deploy.429— throttle limit hit (60 req/min per IP).
Examples
PAYE
curl -X POST https://tax.log7.uk/api/calculate/paye/ \
-H "Content-Type: application/json" \
-d '{
"tax_year": 2025,
"income": 45000,
"income_type": "yearly",
"workweek_hours": 40,
"tax_code": "1257L",
"pension_scheme": "net_pay",
"pension_percent": 5
}'
Limited Company
curl -X POST https://tax.log7.uk/api/calculate/limited_co/ \
-H "Content-Type: application/json" \
-d '{
"tax_year": 2025,
"lc_revenue": 100000,
"lc_expenses": 0,
"lc_director_salary": 12570,
"lc_dividends": 30000
}'
Umbrella
curl -X POST https://tax.log7.uk/api/calculate/umbrella/ \
-H "Content-Type: application/json" \
-d '{
"tax_year": 2025,
"umb_day_rate": 500,
"umb_working_days": 220,
"umb_margin_per_week": 25,
"umb_tax_code": "1257L"
}'
IR35 inside vs outside
curl -X POST https://tax.log7.uk/api/calculate/ir35/ \
-H "Content-Type: application/json" \
-d '{
"tax_year": 2025,
"ir35_day_rate": 500,
"ir35_working_days": 220,
"ir35_agency_margin_per_week": 0,
"ir35_outside_expenses": 0,
"ir35_outside_director_salary": 12570,
"ir35_small_client": false,
"ir35_tax_code": "1257L"
}'
Sole Trader
curl -X POST https://tax.log7.uk/api/calculate/sole_trader/ \
-H "Content-Type: application/json" \
-d '{
"tax_year": 2025,
"st_revenue": 30000,
"st_expenses": 0,
"st_paye_income": 0,
"st_pay_class_2_voluntarily": false,
"st_is_scotland": false,
"st_tax_code": "1257L"
}'
Field naming
Request body field names mirror the corresponding HTML form field names. Each calculator uses a short prefix to keep them disambiguated:
- PAYE — no prefix (
income,tax_code, …) - Limited Co —
lc_(lc_revenue,lc_director_salary, …) - Umbrella —
umb_ - IR35 —
ir35_ - Sole Trader —
st_
Notes & gotchas
- The API does not apply web-form defaults (e.g. PAYE pension defaulting to "Net Pay + 5%"). API consumers pass exactly the values they want.
-
Unauthenticated traffic is rate-limited; build clients
that respect
Retry-Afterheaders when they hit a429. -
Tax-year dataset is seeded on deploy.
GET /api/tax-rates/tells you which years are available. Asking for a non-seeded year returns404.
Caveats
The API is not for production traffic in its current state — no SLA, no guaranteed uptime beyond best-effort, no notice on breaking changes. See the Terms of Use for the liability disclaimer that applies to API output as well.