> Inside against outside IR35 with the Check Take Home Pay API, POST /v1/ir35-compare: every field, an example request in cURL, Node.js and Python, and the response.

Web version: https://checktakehomepay.co.uk/docs/api/ir35-compare · Last updated: 2026-10-09

# Inside against outside IR35

`POST /v1/ir35-compare` Needs a key

The same contract income inside IR35 through an umbrella and outside IR35 through your own limited company, with the difference in take-home.

## Request

A JSON body. Fields not listed here are rejected.

| Field | Type | Description |
| --- | --- | --- |
| `region` required | string | Where they live: england, scotland, wales or northern\_ireland. One of `england`, `scotland`, `wales`, `northern_ireland`. |
| `income` required | object | Contract income: a day rate per day, or an amount a year, month or week. |
| `income.amount` required in income | number | Pounds and pence. Allowed: 0 or more, under 100,000,000. |
| `income.per` required in income | string | The period the amount is for. One of `year`, `month`, `four_weeks`, `two_weeks`, `week`, `day`, `hour`. |
| `margin` required | object | The umbrella's margin, for the umbrella side. |
| `margin.amount` required in margin | number | Pounds and pence. Allowed: 0 or more, under 100,000,000. |
| `margin.per` required in margin | string | The period the amount is for. One of `year`, `month`, `four_weeks`, `two_weeks`, `week`, `day`, `hour`. |
| `tax_year` optional | string | The tax year, such as 2026-27. The latest when not given. Default `"2026-27"`. |
| `pay_frequency` optional | string | How often they are paid: monthly (when not given), four\_weekly, two\_weekly or weekly. Each period runs through payroll. One of `monthly`, `four_weekly`, `two_weekly`, `weekly`. Default `"monthly"`. |
| `working_pattern` optional | object | Days and hours a week and weeks a year, for amounts per day or hour: 5 days, 37.5 hours and 52 weeks when not given. |
| `working_pattern.days_per_week` optional | number | Days worked a week: 5 when not given. Allowed: more than 0, up to 7. Default `5`. |
| `working_pattern.hours_per_week` optional | number | Hours worked a week: 37.5 when not given. Allowed: more than 0, up to 168. Default `37.5`. |
| `working_pattern.weeks_per_year` optional | number | Weeks paid a year: 52 when not given. Allowed: 1 to 53. Default `52`. |
| `expenses` optional | object | Business costs a limited company pays from the income (accountant, insurance, equipment). |
| `expenses.amount` required in expenses | number | Pounds and pence. Allowed: 0 or more, under 100,000,000. |
| `expenses.per` required in expenses | string | The period the amount is for. One of `year`, `month`, `four_weeks`, `two_weeks`, `week`, `day`, `hour`. |
| `student_loans` optional | array of string | Each loan they repay: plan\_1, plan\_2, plan\_4, plan\_5, postgraduate. One of `plan_1`, `plan_2`, `plan_4`, `plan_5`, `postgraduate`. Default `[]`. |
| `apprenticeship_levy` optional | boolean | Counts the Apprenticeship Levy in the umbrella's costs (0.5% of pay, for an umbrella whose pay bill is over £3 million). True when not given. Default `true`. |

## Example

cURL

```
curl https://api.checktakehomepay.co.uk/v1/ir35-compare \
  -H "Authorization: Bearer $CHECKTAKEHOMEPAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "region": "england",
  "income": {
    "amount": 500,
    "per": "day"
  },
  "margin": {
    "amount": 25,
    "per": "week"
  },
  "expenses": {
    "amount": 3000,
    "per": "year"
  }
}'
```

Node.js

```
const response = await fetch('https://api.checktakehomepay.co.uk/v1/ir35-compare', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CHECKTAKEHOMEPAY_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "region": "england",
    "income": {
      "amount": 500,
      "per": "day"
    },
    "margin": {
      "amount": 25,
      "per": "week"
    },
    "expenses": {
      "amount": 3000,
      "per": "year"
    }
  }),
});
const result = await response.json();
```

Python

```
import os
import requests

response = requests.post(
    "https://api.checktakehomepay.co.uk/v1/ir35-compare",
    headers={"Authorization": f"Bearer {os.environ['CHECKTAKEHOMEPAY_API_KEY']}"},
    json={
        "region": "england",
        "income": {"amount": 500, "per": "day"},
        "margin": {"amount": 25, "per": "week"},
        "expenses": {"amount": 3000, "per": "year"},
    },
)
result = response.json()
```

## Response

200, with the result and what produced it: `tax_year`, `engine`, `hmrc_test_data`, `assumptions` and `sources`. This is the example's real response. Every field's type is in the [OpenAPI document](https://api.checktakehomepay.co.uk/openapi.json).

200 OK

```
{
  "tax_year": "2026-27",
  "engine": "2026-27.1",
  "hmrc_test_data": "rest of UK and Welsh tax v1.0, Scottish tax v1.1, NI v1.0, directors NI v1.0, student loans v1.0",
  "income": 130000,
  "inside_ir35": {
    "route": "umbrella",
    "salary": 112078.45,
    "within_budget": true,
    "take_home": 73151.25
  },
  "outside_ir35": {
    "route": "limited_company",
    "salary": 12570,
    "dividends": 87021.59,
    "corporation_tax": 26272.91,
    "take_home": 77960.34
  },
  "difference": 4809.09,
  "assumptions": [
    "Inside IR35 is through an umbrella company; outside IR35 is through your own limited company with no other employees (so no Employment Allowance), a salary from the usual choices and the rest of the profit as dividends.",
    "Only what applies to both routes is asked for, so the difference is for the same income. Inside IR35 through your own company (the deemed payment) is not modelled.",
    "Corporation tax is for a 12-month accounting period with no associated companies, with marginal relief between the limits.",
    "Employment Allowance (employment_allowance) is not available to a company whose only employee paid above the secondary threshold is a director: the caller checks it applies.",
    "The director's NI is on the annual earnings period, category A. Income tax is on the year's salary and dividends together, as Self Assessment settles it."
  ],
  "sources": [
    "https://www.gov.uk/corporation-tax-rates",
    "https://www.gov.uk/guidance/corporation-tax-marginal-relief",
    "https://www.gov.uk/tax-on-dividends",
    "https://www.gov.uk/government/publications/payroll-technical-specifications-national-insurance",
    "https://www.gov.uk/guidance/rates-and-thresholds-for-employers-2026-to-2027",
    "https://www.thepensionsregulator.gov.uk/en/business-advisers/automatic-enrolment-guide-for-business-advisers/minimum-contribution-increases-planned-by-law-phasing",
    "https://www.gov.uk/government/publications/payroll-technical-specifications-income-tax"
  ]
}
```

## Errors

-   400 `INVALID_INPUT`: the message names each field that is wrong.
-   401 `UNAUTHENTICATED` (no key) or `INVALID_KEY` (a key we don’t know, or a revoked one).

Every code, and how to handle them: [Errors](https://checktakehomepay.co.uk/docs/errors).
