How do I validate an Australian Medicare number?

The regex supplied by Jeffrey Kemp (March 11) would help to validate the allowed characters, but the check algorithm below should be enough to validate that the number conforms to Medicare's rules.

The Medicare card number comprises:

  • Eight digits;
  • A check digit (one digit); and
  • An issue number (one digit).

Note: the first digit of the Medicare card number should be in the range 2 to 6.

Medicare card number check digit calculation

  1. Calculate the sum of: ((digit 1) + (digit 2 * 3) + (digit 3 * 7) + (digit 4 * 9) + (digit 5) + (digit 6 * 3) + (digit 7 * 7) + (digit 8 * 9))

where digit 1 is the highest place value digit of the Medicare card number and digit 8 is the lowest place value digit of the Medicare card number.

Example: for Medicare card number '2123 45670 1', digit 1 is 2 and digit 8 is 7.

  1. Divide the calculated sum by 10.
  2. The check digit is the remainder.

Example: For Medicare card number 2123 4567.

  1. (2) + (1 * 3) + (2 * 7) + (3 * 9) + (4) + (5 * 3) + (6 * 7) + (7 * 9) = 170
  2. Divide 170 by 10. The remainder is 0.
  3. The check digit for this Medicare number is 0.

Source: "Use of Healthcare Identifiers in Health Software Systems - Software Conformance Requirements, Version 1.4", NEHTA, 3/05/2011


If you are looking for a C# version, give this a try:

using System.Linq;

//...

public bool IsMedicareFormatValid(string medicareNumber)
{
    if (!(medicareNumber?.Length >= 10 && medicareNumber.Length <12) || !medicareNumber.All(char.IsDigit))
        return false;

    var digits = medicareNumber.Select(c => (int) char.GetNumericValue(c)).ToArray();
    return digits[8] == GetMedicareChecksum(digits.Take(8).ToArray());
}

private int GetMedicareChecksum(int[] digits)
{
    return digits.Zip(new[] { 1, 3, 7, 9, 1, 3, 7, 9 }, (m, d) => m*d).Sum() % 10;
}

Note: This will return false for null values, you might want to throw an exception.

To clarify:

  1. The first 9 Numbers in the medicare card would correspond to the actual medicare number (used in the check).
  2. The 9th digit is a check digit calculated in the GetMedicareChecksum method.
  3. The 10th digit identifies the number of the card, so if you've been issued 3 cards (because you've lost it or whatever), the number would be 3
  4. The 11th digit would identify the family member inside the group.

Hope someone finds this useful.


Here's a Typescript or modern Javascript solution:

  validateMedicare(medicare) {
    let isValid = false;

    if (medicare && medicare.length === 10) {
      const matches = medicare.match(/^(\d{8})(\d)/);

      if (!matches) {
        return { invalid: true };
      }

      const base = matches[1];
      const checkDigit = matches[2];
      const weights = [1, 3, 7, 9, 1, 3, 7, 9];

      let sum = 0;
      for (let i = 0; i < weights.length; i++) {
        sum += parseInt(base[i], 10) * weights[i];
      }

      isValid = sum % 10 === parseInt(checkDigit, 10);
    }

    return isValid;
  }

Please refer to http://clearwater.com.au/code/medicare for an explanation.

To test, generate medicare number here: https://precedencehealthcare.com/rmig/


Added Swift version

class func isMedicareValid(input : String, validateWithIrn : Bool) -> Bool {
    let multipliers = [1, 3, 7, 9, 1, 3, 7, 9]

    let pattern = "^(\\d{8})(\\d)"
    let medicareNumber = input.removeWhitespace()
    let length = validateWithIrn ? 11 : 10

    if medicareNumber.characters.count != length {return false}

    let expression = try! NSRegularExpression(pattern: pattern, options: NSRegularExpressionOptions.CaseInsensitive)

    let matches = expression.matchesInString(medicareNumber, options: NSMatchingOptions.ReportProgress, range: NSMakeRange(0, length))

    if (matches.count > 0 && matches[0].numberOfRanges > 2) {
        let base = medicareNumber.substringWithRange(medicareNumber.startIndex...medicareNumber.startIndex.advancedBy(matches[0].rangeAtIndex(1).length))
        let checkDigitStartIndex = medicareNumber.startIndex.advancedBy(matches[0].rangeAtIndex(2).location )
        let checkDigitEndIndex = checkDigitStartIndex.advancedBy(matches[0].rangeAtIndex(2).length)
        let checkDigit = medicareNumber.substringWithRange(checkDigitStartIndex..<checkDigitEndIndex)
        var total = 0

        for i in 0..<multipliers.count {
            total += Int(base.charAtIndex(i))! * multipliers[i]
        }

         return (total % 10) == Int(checkDigit)
    }
    return false
}

I use some String extensions as well to simplify some operations.

extension String {

func charAtIndex (index: Int) -> String{
    var character = ""
    if (index < self.characters.count){
        let locationStart = self.startIndex.advancedBy(index)
        let locationEnd = self.startIndex.advancedBy(index + 1 )
        character = self.substringWithRange(locationStart..<locationEnd)
    }
    return character
}

func replace(string:String, replacement:String) -> String {
    return self.stringByReplacingOccurrencesOfString(string, withString: replacement, options: NSStringCompareOptions.LiteralSearch, range: nil)
}

func removeWhitespace() -> String {
    return self.replace(" ", replacement: "")
}
}