Validating UK National Insurance (NI) numbers

How to programmatically check the format of NI numbers, with example code for SQL Server, Visual FoxPro and JavaScript.

By Mike Lewis

If you maintain any kind of UK payroll or employee data, you'll know how important it is for your National Insurance (NI) numbers to be correct. This is especially true if you submit PAYE returns electronically. If your NI numbers are inaccurate, your returns will be rejected, leading to hassles and delays all round.

In this article, I'll explain the format of NI numbers, and show some sample code for validating them. The examples are in T-SQL (for SQL Server), VFP (Visual FoxPro) and JavaScript, but they can easily be adapted for other programming languages.

Note that what follows applies equally to NI numbers issued in Britain, Northern Ireland, Guernsey, Jersey and the Isle of Man. Although each of these jurisdictions has its own National Insurance system, the syntax of the numbers follows the same rules in each case.

Format rules

A National Insurance number (also called a NINO) consists of exactly nine characters, made up of the following components:

For example: AB123456C.

The number is usually printed or displayed with embedded spaces, like this: AB 12 34 56 C. However, this is done purely for presentation purposes. The spaces do not form part of the number, and would normally be omitted when the number is stored.

The number is usually shown in upper case, but this is not mandatory.

Note that there's no check digit within the number. That means that there's no automatic way of detecting data-entry errors, such as transposed digits.

The prefix

The prefix can be any two letters, except that the first letter cannot be D, F, I, Q, U or V; and the second letter cannot be D, F, I, O, Q, U or V. In addition, there are seven two-letter combinations that are specifically disallowed, these being BG, GB, KN, NK, NT, TN and ZZ.

Any other pair of letters is syntactically correct, although only about 240 of the 3,800 possible combinations are currently in use. An up-to-date list of these can be found here. If you want to be particularly rigorous in your validation, you could check the prefix against this list, but this is not normally done (probably because the list will grow over time).

The six-digit number

The second part of the NI number is a straightforward string of exactly six digits, that is, a number between 000000 and 999999. It never contains leading spaces or other extraneous symbols.

The suffix

The suffix is either a single letter, in the range A to D, or a space. Although it's always present (if it's a space, the NI number is not trimmed), it is not required for uniqueness. So, if AB123456C is a valid number, then no other number will begin with AB123456.

Temporary numbers

In the past, employers sometimes used a special format to record a NINO for an employee whose actual number was unknown. These temporary numbers had the prefix TN, the suffix F or M (for female or male), with the numeric part being the employee's date of birth, in DDMMYY format. Since these numbers don't follow the above format rules, your validation routine will reject them. This is correct, as the numbers are no longer accepted in electronic submissions.

Another unusual format you might come across is one containing two digits, followed by the letter T, followed by a further five digits. These too are now no longer accepted, and your validation code will correctly reject them.

Let's look now at some program code for validating an NI number. In all the examples that follow, I'll assume that the string being tested does not contain embedded spaces.


In T-SQL (and most other SQL dialects), we can use pattern matching to check the number. The following is a SQL Server function that accepts an NI number as a parameter, and returns 1 if it's valid, or 0 otherwise:

CREATE FUNCTION [dbo].[CheckNino] (@Nino varchar(MAX))
    DECLARE @Result bit

    IF upper(@nino) LIKE 
       [0-9][0-9][0-9][0-9][0-9][0-9][A-D ]'
    AND upper(left(@nino,2)) NOT IN 
      -- Valid
      SET @Result = 1
      -- Invalid
      SET @Result = 0	

    RETURN @Result

With the above function in place, the following statement will retrieve all invalid numbers from a table:

SELECT * FROM dbo.MyTable WHERE dbo.CheckNino(NiNumber) = 0

Visual FoxPro

VFP also supports SQL-style pattern matching, but it's more limited than in SQL Server. And, unlike in T- SQL, you can't use it as part of a conditional expression (in other words, you can't write IF <character variable> LIKE <pattern> . . .).

So we'll adopt the alternative approach of testing each character of the NI number individually:


LOCAL lcTest, llValid

tcNino = UPPER(tcNino)

llValid = ;
  LEN(tcNino) = 9 AND ;
  ISALPHA(SUBSTR(tcNino, 1, 1)) AND ;
  NOT INLIST(SUBSTR(tcNino, 1, 1),  "D","F","I","Q","U","V") AND ;
  ISALPHA(SUBSTR(tcNino, 2, 1)) AND ;
  NOT INLIST(SUBSTR(tcNino,2,1), "D","F","I","O","Q","U","V") AND ;
  NOT INLIST(SUBSTR(tcNino,1,2), "BG","GB","KN","NK","NT","TN","ZZ") AND ;
  ISDIGIT(SUBSTR(tcNino, 3, 1)) AND ;
  ISDIGIT(SUBSTR(tcNino, 4, 1)) AND ;
  ISDIGIT(SUBSTR(tcNino, 5, 1)) AND ;
  ISDIGIT(SUBSTR(tcNino, 6, 1)) AND ;
  ISDIGIT(SUBSTR(tcNino, 7, 1)) AND ;
  ISDIGIT(SUBSTR(tcNino, 8, 1)) AND ;
  INLIST(SUBSTR(tcNino, 9, 1), "A", "B", "C", "D", " ")

RETURN llValid


Things are easier in JavaScript, thanks to that language's support for full-blown regular expressions. (Note that the regexp in the following code is split over two lines so as to fit the width of the page; you should of course code it without the line break.)

function CheckNino(Nino)

var regNI = 
  ([0-9]){2}([0-9]){2}([0-9]){2}([A-D ]){1}?$/;

return regNI.test(Nino);

This function doesn't check for the seven disallowed prefixes, such as BG and KN. I'll leave that as an exercise for the reader.

In summary

Given the vital role that NINOs play in Britain's tax and welfare systems, it's important to record them accurately. I hope that the information I've given in this article will help you do just that.

October 2012

Please note: The information given on this site has been carefully checked and is believed to be correct, but no legal liability can be accepted for its use. Do not use code, components or techniques unless you are satisfied that they will work correctly with your sites or applications.

If you found this article useful, please tell your friends: