Validate and parse South African identifiers in .NET and TypeScript/JavaScript with the same rules. Both libraries run against one shared fixture file, so a value that passes on the server also passes in the browser.
| Identifier | What is checked | Parsed details |
|---|---|---|
| ID number (13 digits) | Luhn check digit, real date of birth (leap years, not in the future), citizenship digit | Date of birth (century inferred), gender, citizen / permanent resident / refugee |
| VAT number | 10 digits starting with 4 | – |
| CIPC company registration | YYYY/NNNNNN/TT, plausible year |
Year, sequence, entity type (Pty Ltd, Ltd, NPC, CC, Inc, external) |
| Phone number | Any common format: 082 123 4567, +27 82…, 27…, (021) 555-1234 |
E.164 (+27821234567), national format, mobile / landline / toll-free / non-geographic |
| Bank branch code | Six digits | Universal branch codes for ABSA, Capitec, FNB, Nedbank, Standard Bank, Investec, TymeBank, Discovery, African Bank, Bidvest |
| Postal code | Four digits | – |
Neither library is published to NuGet or npm yet. Use them from source:
- .NET: add a project reference,
dotnet add <your-project> reference path/to/za-validators/dotnet/ZaValidators/ZaValidators.csproj, ordotnet pack dotnet/ZaValidatorsand install the local.nupkg. - TypeScript/JS:
cd js && npm install && npm run build, thennpm install path/to/za-validators/js(ornpm packand install the tarball) in your app. The import name below (za-validators) works once it's installed that way.
using ZaValidators;
if (IdNumber.TryParse("9001010123080", out var id))
Console.WriteLine($"{id.DateOfBirth} {id.Gender} {id.Citizenship}"); // 1990-01-01 Female Citizen
PhoneNumber.TryParse("082 123 4567", out var phone); // phone.E164 == "+27821234567", Kind == Mobile
CompanyRegistration.TryParse("2015/123456/07", out var co); // co.Type == PrivateCompany
VatNumber.IsValid("4123456789"); // true
BranchCodes.ForBank("Capitec"); // "470010"import { parseIdNumber, parsePhoneNumber, isValidVatNumber, branchCodeForBank } from "za-validators";
parseIdNumber("9001010123080"); // { dateOfBirth: "1990-01-01", gender: "Female", citizenship: "Citizen", ... }
parsePhoneNumber("(021) 555-1234"); // { e164: "+27215551234", kind: "Landline", ... }
isValidVatNumber("4123456789"); // true
branchCodeForBank("FNB"); // "250655"Zero runtime dependencies, ES modules, typings included. Works in Node 18+ and modern browsers.
fixtures/cases.json Shared test cases (valid and invalid) for both implementations
dotnet/ZaValidators .NET 8 library (packable, not published)
dotnet/ZaValidators.Tests xUnit tests driven by the fixtures
js/src/index.ts TypeScript implementation (packable, not published)
js/test/ Vitest tests driven by the same fixtures
# .NET (requires the .NET 8 SDK)
cd dotnet && dotnet test
# TypeScript (requires Node 18+)
cd js && npm install && npm run build && npm testCurrent results: 9 .NET tests (covering every fixture) and 55 Vitest cases, all passing.
To add a case, put it in fixtures/cases.json and both suites pick it up.
- A valid ID number is well formed. It doesn't prove the number was issued; only Home Affairs can confirm that.
- VAT numbers are checked for format only. SARS doesn't publish a checksum.
- CIPC entity types beyond the common ones are reported as
Other. - Mobile prefixes follow current numbering (06x, 07x, 081 to 084). The universal branch codes listed are the ones banks publish for EFTs.
MIT © 2026 Ohula Mulaudzi