Regex and cron

Regex for Credit Card Numbers

Identify the card network and length of a card number, then confirm it with the Luhn check.

  • Free foreverNo trial, no paywall, no usage limits.
  • No sign-upOpen the page and start. We never ask for an email to use a tool.
  • PrivateEverything runs in your browser. Your input is never uploaded or stored on our servers.
  • Works offlineOnce the page has loaded, the tool keeps working without a connection.

Pattern

/^(?:4\d{12}(?:\d{3}){0,2}|5[1-5]\d{14}|2(?:22[1-9]|2[3-9]\d|[3-6]\d{2}|7[01]\d|720)\d{12}|3[47]\d{13}|6(?:011|5\d{2}|4[4-9]\d)\d{12,15}|3(?:0[0-5]|[68]\d)\d{11}|35(?:2[89]|[3-8]\d)\d{12,15})$/

Regular expression

JavaScript syntax, run by your browser's own engine in a background worker. Press / anywhere to jump here.
Reset to the original pattern and examplesShow cheat sheet: click any token to insert itCopy pattern
Find every match instead of stopping at the first. Needed for matchAll() and for replacing all occurrences.Case-insensitive: /abc/i also matches "ABC" and "aBc".^ and $ match at the start and end of every line, not just the whole string. Use it to test one value per line.Lets . match line breaks too. Without it, . stops at \n and \r.Treats the pattern as Unicode code points: enables \p{…} property escapes and \u{…}, makes emoji count as one character, and rejects sloppy escapes.Only matches starting exactly at lastIndex (position 0 here), never further along. Useful for tokenizers that consume input piece by piece.Records the start and end index of every capture group (match.indices). The tester always shows group positions, so this only changes your code.The upgraded unicode mode (ES2024): set operations like [\p{L}--[a-z]] and [A&&B], string properties like \p{RGI_Emoji}. Cannot be combined with u.
9 matches9 matches

Test string

Matches are highlighted as you type, in alternating colors. Click inside a match (or hover its row below) to color its capture groups.
9 matchesClear test string

Explanation

Every part of the pattern in plain English. Hover a row to highlight that part of the pattern.
Copy explanation

Loading the explainer…

Every match with its position and capture groups.Preview a replacement with $1, $<name> and $& references, then copy the result.Copy a ready-to-run snippet for JavaScript, Python, Go, C#, PHP or Dart.

Matches

Named groups are shown by name. undefined means the group did not take part in that match.
Copy matches as JSONDownload matches.csv
#MatchPosition
141111111111111110–16
2422222222222217–30
3555555555555444431–47
4222300312200322248–64
537828224631000565–80
6601111111111111781–97
73056930902590498–112
83530111333300000113–129
94111111111111112130–146

Need a secure web app built around this? Talk to our team

Quick answer

This credit card regex checks the issuer prefix and length of a card number with spaces and dashes removed: Visa starts with 4 (13, 16 or 19 digits), Mastercard with 51–55 or 2221–2720 (16), American Express with 34 or 37 (15), Discover with 6011, 644–649 or 65, Diners Club with 300–305, 36 or 38, and JCB with 3528–3589. A matching number can still be mistyped, so always run the Luhn checksum too.

  • Tested examples
  • Token-by-token breakdown
  • Code in 5 languages
  • Live tester

How to use the credit card number regex

  1. 1Copy the pattern with Copy pattern in the tester, or take a ready-made snippet for your language from the code section below.
  2. 2Check the sample lines in Test string: every line the credit card number regex accepts is highlighted, rejected lines stay plain.
  3. 3Paste your own values one per line to see which pass, and read Explanation for what each part of the pattern does.
  4. 4Open the Code tab for JavaScript, Python, Go, C#, PHP or Dart with the pattern translated and escaped.
  5. 5Validate again on the server: client-side checks give users quick feedback, not security.

How does the credit card number regex work?

Read it left to right. Each piece below matches one part of the input:

  • 4\d{12}(?:\d{3}){0,2} Visa: starts with 4, 13, 16 or 19 digits.
  • |5[1-5]\d{14} Mastercard: 51–55, 16 digits.
  • |2(?:22[1-9]|2[3-9]\d|[3-6]\d{2}|7[01]\d|720)\d{12} Mastercard 2-series: 2221–2720, 16 digits.
  • |3[47]\d{13} American Express: 34 or 37, 15 digits.
  • |6(?:011|5\d{2}|4[4-9]\d)\d{12,15} Discover: 6011, 644–649 or 65, 16 to 19 digits.
  • |3(?:0[0-5]|[68]\d)\d{11} Diners Club: 300–305, 36 or 38, 14 digits.
  • |35(?:2[89]|[3-8]\d)\d{12,15} JCB: 3528–3589, 16 to 19 digits.
  • ^(?: … )$ The whole string must be one of these: digits only, no spaces.

Which strings does the credit card number regex match?

The whole input must match, from the first character to the last. Every row below is checked against the pattern by our automated test suite before each release.

InputMatches?Why
4111111111111111YesVisa test number, 16 digits.
4222222222222YesVisa test number, 13 digits.
5555555555554444YesMastercard test number.
2223003122003222YesMastercard 2-series test number.
378282246310005YesAmerican Express test number, 15 digits.
6011111111111117YesDiscover test number.
30569309025904YesDiners Club test number, 14 digits.
3530111333300000YesJCB test number.
4111111111111112YesRight shape, wrong checksum: only the Luhn check catches this.
4111 1111 1111 1111NoSpaces: strip them before validating.
1234567890123456NoNo network uses the prefix 1.
411111111111No12 digits: too short for Visa.
5655555555554444No56 is not a Mastercard prefix.
37828224631000NoAmex prefix but only 14 digits.

How do I add the Luhn checksum?

The Luhn algorithm catches every single-digit typo and most swapped neighbors. Run it after the regex:

function luhn(digits) {
  let sum = 0;
  for (let i = 0; i < digits.length; i++) {
    let d = Number(digits[digits.length - 1 - i]);
    if (i % 2 === 1) {
      d *= 2;
      if (d > 9) d -= 9;
    }
    sum += d;
  }
  return sum % 10 === 0;
}

const digits = input.replace(/[\s-]/g, "");
const valid = CARD.test(digits) && luhn(digits);

4111111111111112 in the table above has the right prefix and length, but its last digit breaks the checksum. That is exactly the kind of typo users make.

Should I validate card numbers myself at all?

Only for instant feedback. If you take payments, card data should go straight from the browser to your payment provider's hosted fields or SDK (Stripe Elements, Adyen, Braintree and others), so the number never touches your server. That keeps you in the lightest PCI DSS scope, and the provider runs the authoritative checks, including whether the card exists and has funds.

Never log card numbers, and if you must show one, mask all but the last four digits. Card networks also add new prefix ranges over time (Mastercard's 2-series arrived in 2017), so treat the brand detection as a hint for showing the right logo, not as a gate that rejects payments.

How do I use the credit card number regex in JavaScript, Python, Go, C# and PHP?

The pattern is translated for each engine rather than pasted as is: named groups, anchors, escapes, shorthand classes and flags are adjusted so each snippet behaves as closely to the JavaScript version as its engine allows. Differences that remain (Go's RE2 has no lookarounds or backreferences, for example) are called out under each one. The Code tab in the tester above also exports Dart, and updates as you edit the pattern.

JavaScript and TypeScript

const regex = /^(?:4\d{12}(?:\d{3}){0,2}|5[1-5]\d{14}|2(?:22[1-9]|2[3-9]\d|[3-6]\d{2}|7[01]\d|720)\d{12}|3[47]\d{13}|6(?:011|5\d{2}|4[4-9]\d)\d{12,15}|3(?:0[0-5]|[68]\d)\d{11}|35(?:2[89]|[3-8]\d)\d{12,15})$/;

function isValid(value) {
  return regex.test(value);
}

console.log(isValid("4111111111111111"));

Python

import re

pattern = re.compile(r"^(?:4[0-9]{12}(?:[0-9]{3}){0,2}|5[1-5][0-9]{14}|2(?:22[1-9]|2[3-9][0-9]|[3-6][0-9]{2}|7[01][0-9]|720)[0-9]{12}|3[47][0-9]{13}|6(?:011|5[0-9]{2}|4[4-9][0-9])[0-9]{12,15}|3(?:0[0-5]|[68][0-9])[0-9]{11}|35(?:2[89]|[3-8][0-9])[0-9]{12,15})\Z")

def is_valid(value: str) -> bool:
    return pattern.search(value) is not None

print(is_valid("4111111111111111"))
  • \d and \w are written as [0-9] and [A-Za-z0-9_] because in Python they also match digits and letters from other scripts.
  • JavaScript's $ only matches at the very end of the string. Python's $ also matches before a trailing newline, so the pattern uses \Z instead.

Go

package main

import (
	"fmt"
	"regexp"
)

func main() {
	re := regexp.MustCompile(`^(?:4\d{12}(?:\d{3}){0,2}|5[1-5]\d{14}|2(?:22[1-9]|2[3-9]\d|[3-6]\d{2}|7[01]\d|720)\d{12}|3[47]\d{13}|6(?:011|5\d{2}|4[4-9]\d)\d{12,15}|3(?:0[0-5]|[68]\d)\d{11}|35(?:2[89]|[3-8]\d)\d{12,15})$`)
	fmt.Println(re.MatchString("4111111111111111"))
}

C#

using System;
using System.Text.RegularExpressions;

// The timeout stops a pathological input from hanging the thread (ReDoS).
var regex = new Regex(@"^(?:4[0-9]{12}(?:[0-9]{3}){0,2}|5[1-5][0-9]{14}|2(?:22[1-9]|2[3-9][0-9]|[3-6][0-9]{2}|7[01][0-9]|720)[0-9]{12}|3[47][0-9]{13}|6(?:011|5[0-9]{2}|4[4-9][0-9])[0-9]{12,15}|3(?:0[0-5]|[68][0-9])[0-9]{11}|35(?:2[89]|[3-8][0-9])[0-9]{12,15})\z", RegexOptions.None, TimeSpan.FromSeconds(1));
Console.WriteLine(regex.IsMatch("4111111111111111"));
  • \d and \w are written as [0-9] and [A-Za-z0-9_] because in .NET they also match digits and letters from other scripts.
  • JavaScript's $ only matches at the very end of the string. .NET's $ also matches before a trailing newline, so the pattern uses \z instead.

PHP

<?php

$pattern = '/^(?:4\d{12}(?:\d{3}){0,2}|5[1-5]\d{14}|2(?:22[1-9]|2[3-9]\d|[3-6]\d{2}|7[01]\d|720)\d{12}|3[47]\d{13}|6(?:011|5\d{2}|4[4-9]\d)\d{12,15}|3(?:0[0-5]|[68]\d)\d{11}|35(?:2[89]|[3-8]\d)\d{12,15})$/D';

function isValid(string $value, string $pattern): bool
{
    return preg_match($pattern, $value) === 1;
}

var_dump(isValid('4111111111111111', $pattern));
  • The D modifier makes $ match only at the very end, like JavaScript (PCRE's $ also matches before a trailing newline).

Browse all 25 patterns in the regex pattern library, or build your own in the regex tester.

Frequently asked questions

Check the prefix: 4 is Visa, 51 to 55 and 2221 to 2720 are Mastercard, 34 and 37 are American Express, 6011, 644 to 649 and 65 are Discover. Splitting the regex into one pattern per network lets you show the right logo as the user types.
No. A regex checks the prefix and length; the Luhn checksum needs arithmetic, so run it in code after the regex passes.
They are the public test numbers that payment providers publish for sandboxes. They pass the format and Luhn checks but cannot be charged.

Last updated by the Playxoft engineering team.

From the engineers who built this tool

Building something bigger than a quick fix?

Auth, payments, dashboards and security are where web apps get hard. We have shipped them many times. Share your idea and we will map out a realistic plan and estimate, free and with no obligation.

More free tools

See all tools

Regex Tester

Test regex live with highlighted matches, plain-English explanations and code export.

Open tool

Cron Expression Generator

New

Build and explain cron schedules, see the next runs in any time zone, copy platform snippets.

Open tool

SQL Formatter

Beautify or minify SQL in 12 dialects, with keyword case, indent and line:column errors.

Open tool

cURL to Python Converter

New

Convert cURL commands into Python requests or httpx code with params, json, files and auth.

Open tool

cURL to Fetch Converter

New

Turn any cURL command into JavaScript fetch() code with headers, JSON, forms and auth.

Open tool

cURL to Axios Converter

New

Convert cURL commands into an Axios request config with params, data, auth and timeouts.

Open tool