Credit Card Number Regex
Check that a card number has the right shape, 13 to 19 digits with optional single spaces or hyphens, before the Luhn check and the payment provider do the rest.
One case per line; the m flag makes ^ and the end anchor work line by line.
Link optionspattern only
The address bar holds the pattern, flags and replacement, so Copy link shares them. Your test text stays out of it unless you include it (up to 2,000 characters), because it may be private. Nothing is sent to a server.
Matches
7matches
The first is “4111111111111111”, at position 0.
- Groups
- none
- Characters matched
- 115 of 236
What the pattern meansHover or tap a part to see it in the pattern and what it matched
How this works: Method, 4 sources, Checked against 1 worked example,
How this works
Method
Your pattern runs in your own browser’s JavaScript engine, in a background worker that is stopped after 1.5 seconds, so a pattern that backtracks catastrophically can’t freeze the page. The explanation comes from our own parser of the ECMAScript pattern grammar, checked against the engine; what each part matched is found by wrapping that part in one more group and running the pattern again. Conversions to other flavours only rewrite the syntax and list what their documentation says works differently.
Sources
How it’s tested
One worked example for this page is checked by automated tests before every release: given the inputs, the tool must show the expected answer.
Changes
- First published, with a library of 19 common patterns and their test cases.
Worked example
Take 4111111111111111, the first case in the tester. Reading the pattern left to right, each part takes its share of the text:
^The start of the text (matches a position, no characters)(?:\d[ -]?){12,18}A group (not captured), 12 to 18 times: matches 411111111111111\dA digit (0–9) matches 1$The end of the text (matches a position, no characters)
How the card number regex works
The pattern treats a card number as a run of digits where each digit may be followed by one space or one hyphen. \(?:\d[ -]?)\ is that unit: a digit and an optional separator. Repeating it \{12,18}\ times and finishing with a final \\d\ gives 13 to 19 digits in all, with no separator allowed at either end. Since the separator is optional after every digit, it accepts groups of four (Visa, Mastercard), the 4-6-5 grouping of American Express, and no grouping at all.
It does not try to name the card brand. Brand rules change: Mastercard now also uses the 2221–2720 range, and Discover covers several ranges. If you want a brand icon, look at the first digits after stripping separators (4 for Visa, 34 or 37 for Amex) in code, where the rules are easy to update.
Nor can a regex check the last digit. Every card number ends in a Luhn check digit, computed from the others by doubling every second digit from the right and summing. 4111111111111112 has the right shape but a wrong check digit, so it passes here and fails Luhn. Run a short Luhn function on the digits after the regex, and treat both checks as typo catchers: only the card issuer can say whether a number is real.
- Payment card numbers are 8 to 19 digits: an issuer identification number of 6 or 8 digits, the account number and a final Luhn check digit. Source: Payment card number (Wikipedia).
- American Express numbers have 15 digits and start with 34 or 37; Visa numbers start with 4 and have 13, 16 or 19 digits. Source: Payment card number (Wikipedia).
Test cases
Every case runs as an automated test of this page’s pattern, so the table can’t drift from what the pattern really does.
| Text | Result | Why |
|---|---|---|
| 4111111111111111 | Passes | the well-known Visa test number, 16 digits |
| 4111 1111 1111 1111 | Passes | the same number in groups of four |
| 5555-5555-5555-4444 | Passes | a Mastercard test number with hyphens |
| 378282246310005 | Passes | an American Express test number, 15 digits |
| 3782 822463 10005 | Passes | Amex grouped 4-6-5 as printed on the card |
| 4222222222222 | Passes | a 13-digit Visa test number |
| 4111111111111112 | Passes | right shape, wrong Luhn check digit: the regex can’t tell |
| 4111 1111 1111 | Fails | only 12 digits |
| 41111111111111111111 | Fails | 20 digits, one more than any card |
| 4111--1111-1111-1111 | Fails | two separators in a row |
| 4111_1111_1111_1111 | Fails | underscores are not separators |
| 4111 1111 1111 111a | Fails | a letter |
| -4111111111111111 | Fails | starts with a separator |
What it doesn’t check
- It accepts mixed separators such as 4111 1111-1111 1111; strip all separators before storing.
- It cannot check the Luhn checksum, the issuer or whether the card exists.
- ISO/IEC 7812 allows card numbers from 8 digits; this pattern starts at 13, which covers the major brands; widen it if you accept cards with shorter numbers.
- Don’t log or keep raw card numbers yourself: card data is covered by the PCI DSS rules, so let your payment provider collect it where you can.
The same pattern in other languages
Converted automatically from the JavaScript version; the tester above always runs JavaScript.
| Flavour | Pattern | Notes |
|---|---|---|
| Python | (?a)^(?:\d[ \-]?){12,18}\d\Z | |
| PCRE | ^(?:\d[ \-]?){12,18}\d\z | |
| Go | ^(?:\d[ \-]?){12,18}\d$ | |
| Java | ^(?:\d[ \-]?){12,18}\d\z | |
| .NET | ^(?:\d[ \-]?){12,18}\d\z | In .NET, \d and \w match any Unicode digit or letter. Pass RegexOptions.ECMAScript, or write [0-9] and [A-Za-z0-9_], for JavaScript’s ASCII-only meaning. |
Sources
Frequently Asked Questions
Can a regex validate a credit card number?
It can check the length and characters, which catches most typing slips. It can’t check the Luhn check digit or whether the card exists, so add a Luhn function and let the payment provider make the final call.
How do I allow spaces in the card number?
This pattern already allows one space or hyphen after any digit. Strip them with a replace before running the Luhn check or sending the number on.
How can I tell Visa from Mastercard?
Visa numbers start with 4. Mastercard numbers start with 51 to 55 or 2221 to 2720. Check the leading digits in code after cleaning the input, rather than building one regex per brand.