> One pay period with the Check Take Home Pay API, POST /v1/payroll/period: every field, an example request in cURL, Node.js and Python, and the response.

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

# One pay period

`POST /v1/payroll/period` Needs a key

One pay period the way payroll software runs it: tax on a cumulative or week 1/month 1 code, employee and employer NI by category letter (directors on the annual earnings period), student loans and the NI earnings bands. Every case in HMRC's payroll test data runs through this endpoint.

## Request

A JSON body. Fields not listed here are rejected.

| Field | Type | Description |
| --- | --- | --- |
| `pay_frequency` required | string | How often the employee is paid: monthly, four\_weekly, two\_weekly or weekly. One of `monthly`, `four_weekly`, `two_weekly`, `weekly`. |
| `period` required | integer | The tax week or month for weekly and monthly pay, else the pay period number. Allowed: 1 to 52. |
| `tax_code` required | string | The tax code on the payroll record, with W1 or M1 for the week 1/month 1 basis. |
| `gross_pay` required | number | Pay this period subject to NI, which student loans are also worked on. Allowed: 0 or more, under 100,000,000. |
| `tax_year` optional | string | The tax year, such as 2026-27. The latest when not given. Default `"2026-27"`. |
| `taxable_pay` optional | number | Pay this period subject to tax, payrolled benefits included; gross\_pay when not given. Allowed: more than -100,000,000, under 100,000,000. |
| `payrolled_benefits` optional | number | Payrolled benefits in kind in taxable\_pay: taxed, but outside the 50% limit. Allowed: 0 or more, under 100,000,000. |
| `taxable_pay_to_date` optional | number | Taxable pay in this employment in earlier periods of the year. Allowed: more than -100,000,000, under 100,000,000. Default `0`. |
| `tax_to_date` optional | number | Tax deducted in this employment in earlier periods of the year. Allowed: more than -100,000,000, under 100,000,000. Default `0`. |
| `ni_category` optional | string | National Insurance category letter: A when not given. One of `A`, `B`, `C`, `D`, `E`, `F`, `H`, `I`, `J`, `K`, `L`, `M`, `N`, `S`, `V`, `Z`. Default `"A"`. |
| `student_loans` optional | array of string | Each loan the employee repays: plan\_1, plan\_2, plan\_4, plan\_5, postgraduate. One of `plan_1`, `plan_2`, `plan_4`, `plan_5`, `postgraduate`. Default `[]`. |
| `director` optional | object | A company director on the annual earnings period: NI this period is the year's NI on earnings to date less what earlier periods paid. |
| `director.earnings_to_date` required in director | number | NI-able earnings as a director in earlier periods. Allowed: 0 or more, under 100,000,000. |
| `director.employee_ni_to_date` required in director | number | Employee NI paid in earlier periods as a director. Allowed: 0 or more, under 100,000,000. |
| `director.employer_ni_to_date` required in director | number | Employer NI paid in earlier periods as a director. Allowed: 0 or more, under 100,000,000. |
| `director.weeks_as_director` optional | integer | Weeks as a director this year, when appointed during it (pro rata thresholds). Allowed: 1 to 52. |

## Example

cURL

```
curl https://api.checktakehomepay.co.uk/v1/payroll/period \
  -H "Authorization: Bearer $CHECKTAKEHOMEPAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "pay_frequency": "monthly",
  "period": 1,
  "tax_code": "1257L",
  "gross_pay": 3000,
  "student_loans": [
    "plan_2"
  ]
}'
```

Node.js

```
const response = await fetch('https://api.checktakehomepay.co.uk/v1/payroll/period', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CHECKTAKEHOMEPAY_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "pay_frequency": "monthly",
    "period": 1,
    "tax_code": "1257L",
    "gross_pay": 3000,
    "student_loans": [
      "plan_2"
    ]
  }),
});
const result = await response.json();
```

Python

```
import os
import requests

response = requests.post(
    "https://api.checktakehomepay.co.uk/v1/payroll/period",
    headers={"Authorization": f"Bearer {os.environ['CHECKTAKEHOMEPAY_API_KEY']}"},
    json={
        "pay_frequency": "monthly",
        "period": 1,
        "tax_code": "1257L",
        "gross_pay": 3000,
        "student_loans": ["plan_2"],
    },
)
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",
  "tax_code": "1257L",
  "tax": 390.2,
  "tax_to_date": 390.2,
  "regulatory_limit_applied": false,
  "employee_ni": 156.16,
  "employer_ni": 387.45,
  "ni_earnings": {
    "at_lower_earnings_limit": 559,
    "lower_earnings_limit_to_primary_threshold": 489,
    "primary_threshold_to_upper_earnings_limit": 1952
  },
  "student_loans": {
    "plan_2": 49
  },
  "assumptions": [
    "taxable_pay_to_date and tax_to_date are for this employment only, before this period."
  ],
  "sources": [
    "https://www.gov.uk/government/publications/payroll-technical-specifications-income-tax",
    "https://www.gov.uk/government/publications/payroll-technical-specifications-national-insurance",
    "https://www.gov.uk/government/publications/payroll-technical-specifications-student-loans/collection-of-student-loans-from-6-april-2026",
    "https://www.gov.uk/government/publications/software-developers-payroll-test-data-2026-to-2027"
  ]
}
```

## 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).
