National Insurance#

National Insurance is a key component of the UK tax system. It primarily funds certain types of welfare and state benefits, including the State Pension. Here’s an overview of National Insurance:

NI has four main classes:

  • Class 1: Paid by employees and their employers. This is based on the employee’s earnings and is deducted directly from wages.

  • Class 2: Paid by self-employed people with profits above a certain threshold.

  • Class 3: Voluntary contributions, often made by people who want to fill gaps in their National Insurance record.

  • Class 4: Paid by self-employed people with profits above a certain threshold, in addition to Class 2 NICs.

Modelling#

PolicyEngine models each class separately, according to the table below.

Hide code cell source
# @title
import pandas as pd
from tabulate import tabulate


data = {
    "Class": ["Class 1", "Class 2", "Class 4"],
    "Methodology & Basis": [
        "Based on employment income. \n- Monthly and annual calculations.",
        "Based on self-employment income. \n- Weekly flat rate.",
        "Derived from self-employment income minus Class 1 employee NI.",
    ],
    "Thresholds & Limits": [
        "Primary Threshold, Upper Earnings Limit",
        "Small Profits Threshold",
        "Lower Profits Limit, Upper Profits Limit",
    ],
    "Rate Application": [
        "Main and Additional rates",
        "Flat rate",
        "Main and Additional rates",
    ],
    "Reference": [
        "Social Security Contributions and Benefits Act 1992 s. 8",
        "Social Security and Benefits Act 1992 s. 11",
        "Social Security and Benefits Act 1992 s. 15",
    ],
}

df = pd.DataFrame(data)
df
Class Methodology & Basis Thresholds & Limits Rate Application Reference
0 Class 1 Based on employment income. \n- Monthly and an... Primary Threshold, Upper Earnings Limit Main and Additional rates Social Security Contributions and Benefits Act...
1 Class 2 Based on self-employment income. \n- Weekly fl... Small Profits Threshold Flat rate Social Security and Benefits Act 1992 s. 11
2 Class 4 Derived from self-employment income minus Clas... Lower Profits Limit, Upper Profits Limit Main and Additional rates Social Security and Benefits Act 1992 s. 15

Appendix#

The chart below shows all threshold parameters for each class of National Insurance.

from policyengine_uk.system import system
import plotly.express as px
from policyengine_core.charts import format_fig
from policyengine_core.parameters import Parameter

ni = system.parameters.gov.hmrc.national_insurance

threshold_parameters = [
    ni.class_1.thresholds.lower_earnings_limit,
    ni.class_1.thresholds.primary_threshold,
    ni.class_1.thresholds.upper_earnings_limit,
    ni.class_1.thresholds.secondary_threshold,
    ni.class_2.small_profits_threshold,
    ni.class_4.thresholds.lower_profits_limit,
    ni.class_4.thresholds.upper_profits_limit,
]
rate_parameters = [
    param
    for param in ni.get_descendants()
    if param not in threshold_parameters and isinstance(param, Parameter)
]

instants = [f"{2020 + i}-04-06" for i in range(0, 14)]

instant_values = []
values = []
is_thresholds = []
labels = []

for param in threshold_parameters:
    for instant in instants:
        instant_values.append(instant)
        values.append(param(instant))
        is_thresholds.append(True)
        labels.append(param.metadata.get("label"))

for param in rate_parameters:
    for instant in instants:
        instant_values.append(instant)
        values.append(param(instant))
        is_thresholds.append(False)
        labels.append(param.metadata.get("label"))

df = pd.DataFrame(
    {
        "Instant": instant_values,
        "Value": values,
        "Threshold": is_thresholds,
        "Label": labels,
    }
)

fig = px.line(
    df[df["Threshold"] == True],
    x="Instant",
    y="Value",
    color="Label",
)

fig.update_layout(
    title="National Insurance thresholds over time",
    # Put legend at bottom
    legend=dict(
        orientation="h",
        yanchor="bottom",
        y=-0.3,
        xanchor="right",
        x=1,
        title="",
    ),
)

fig.update_traces(
    line_shape="hv",
)

fig = format_fig(fig)
fig