Learn how to automatically detect and redact credit card numbers (PANs) from text, logs, and documents. Ensure PCI-DSS compliance with Luhn-validated detection and secure masking techniques for payment card data.
Credit card numbers, also known as Primary Account Numbers (PANs), are highly sensitive financial data that require stringent protection under PCI-DSS (Payment Card Industry Data Security Standard).
Why it matters: These 13-19 digit numbers can be used to make fraudulent purchases, and their exposure constitutes a significant security incident that can result in substantial fines, legal liability, and reputational damage.
Anonymization API uses Luhn algorithm validation combined with BIN (Bank Identification Number) pattern recognition to detect credit card numbers with 99.9% accuracy.
Our system identifies cards from all major networks including Visa, Mastercard, American Express, Discover, JCB, and regional networks. We handle various formatting styles including spaces, dashes, and continuous digits.
Whether you're sanitizing log files, processing customer support transcripts, or anonymizing transaction records, our API ensures that credit card numbers are consistently detected and redacted according to PCI-DSS requirements.
We support multiple masking formats to meet different compliance and business needs while ensuring sensitive data is never exposed.
Only valid card numbers are detected, reducing false positives
Visa, Mastercard, Amex, Discover, JCB, and more
Compliant masking formats for audit requirements
The Payment Card Industry Data Security Standard (PCI-DSS) establishes comprehensive requirements for protecting cardholder data. Understanding these requirements is essential for implementing proper credit card redaction strategies.
Critical: PCI-DSS violations can result in fines of $5,000 to $100,000 per month, increased transaction fees, and even loss of the ability to accept card payments. Proper card number redaction is not optional - it's a fundamental security requirement.
PCI-DSS defines cardholder data as:
SAD must NEVER be stored after authorization, even if encrypted:
Best Practice: Our API can detect and redact all cardholder data elements in a single pass. Enable comprehensive protection with: entity_types=["CREDIT_CARD", "CVV", "CARD_EXPIRY", "PERSON"]
PCI-DSS allows displaying maximum of first 6 and last 4 digits. Common compliant formats include:
************8901 (Last 4 only - most common) 453201******8901 (First 6 + Last 4) 4532-01**-****-8901 (Preserving format) XXXX-XXXX-XXXX-8901 (Alternative mask character)
Get started with credit card redaction in just a few lines of code. This example demonstrates the simplest way to detect and redact card numbers from text using our API.
from anonymization import Client client = Client(api_key="your_api_key") result = client.anonymize( text="Payment received: card 4532015112830366", entity_types=["CREDIT_CARD"] ) print(result.anonymized_text) # Output: Payment received: card [CREDIT_CARD]
const { AnonymizationClient } = require('@anonymization/api'); const client = new AnonymizationClient('your_api_key'); const result = await client.anonymize({ text: "Payment received: card 4532015112830366", entityTypes: ["CREDIT_CARD"] }); console.log(result.anonymizedText); // Output: Payment received: card [CREDIT_CARD]
curl -X POST https://api.anonymizationapi.com/v2/anonymize \ -H "Authorization: Bearer your_api_key" \ -H "Content-Type: application/json" \ -d '{ "text": "Payment received: card 4532015112830366", "entity_types": ["CREDIT_CARD"] }'
The API response includes details about each detected card number:
{
"anonymized_text": "Payment received: card [CREDIT_CARD]",
"entities": [
{
"type": "CREDIT_CARD",
"text": "4532015112830366",
"start": 23,
"end": 39,
"confidence": 0.99,
"metadata": {
"card_brand": "VISA",
"luhn_valid": true,
"bin": "453201"
}
}
]
}
Credit card numbers follow specific patterns based on the card network (brand). Our API recognizes all major card networks and their varying formats.
Visa: 4xxx xxxx xxxx xxxx (16 digits, starts with 4)
Mastercard: 5[1-5]xx xxxx xxxx xxxx (16 digits, starts with 51-55)
2[2-7]xx xxxx xxxx xxxx (16 digits, starts with 22-27)
American Express: 3[47]xx xxxxxx xxxxx (15 digits, starts with 34 or 37)
Discover: 6011 xxxx xxxx xxxx (16 digits, starts with 6011, 65, 644-649)
JCB: 35xx xxxx xxxx xxxx (16 digits, starts with 35)
Diners Club: 3[068]xx xxxx xxxx xx (14 digits)
UnionPay: 62xx xxxx xxxx xxxx (16-19 digits, starts with 62)
Card numbers appear in various formats across different systems:
4532015112830366 (Continuous) 4532 0151 1283 0366 (Space-separated groups of 4) 4532-0151-1283-0366 (Dash-separated) 4532.0151.1283.0366 (Dot-separated) 3782 822463 10005 (Amex format: 4-6-5 grouping)
All credit card numbers must pass the Luhn check digit algorithm. Our API validates this to dramatically reduce false positives:
# Luhn validation is enabled by default result = client.anonymize( text="Card: 4532015112830366", # Valid Luhn entity_types=["CREDIT_CARD"] ) # Detected and redacted # Invalid card numbers are not detected result = client.anonymize( text="Card: 4532015112830367", # Invalid Luhn entity_types=["CREDIT_CARD"] ) # NOT detected - Luhn check fails # Optionally disable Luhn validation for edge cases result = client.anonymize( text=text, entity_types=["CREDIT_CARD"], options={"card_luhn_validate": False} )
The API recognizes well-known test card numbers used in development:
# Test cards are detected by default result = client.anonymize( text="Test: 4111111111111111", # Visa test card entity_types=["CREDIT_CARD"] ) # Optionally skip test cards (not recommended for security) result = client.anonymize( text=text, entity_types=["CREDIT_CARD"], options={"card_skip_test_numbers": True} )
Choose the appropriate credit card anonymization technique based on your PCI compliance requirements and business needs.
Completely replaces the card number with a placeholder tag. Maximum security, suitable for logs and data exports.
Shows only the last 4 digits, the most common PCI-compliant format for receipts and customer-facing displays.
Preserves the first 6 (BIN) and last 4 digits. Useful for fraud analysis while maintaining compliance.
Replaces with a fake but valid-looking card number. Same input always produces same output within a session. Perfect for test data.
Generates a fake card number of the same brand. The replacement is Luhn-valid and maintains the same network prefix.
# Full redaction (default) result = client.anonymize(text, entity_types=["CREDIT_CARD"], mode="redact") # PCI-compliant last 4 masking result = client.anonymize(text, entity_types=["CREDIT_CARD"], mode="mask", options={"card_show_last": 4}) # BIN + Last 4 masking result = client.anonymize(text, entity_types=["CREDIT_CARD"], mode="mask", options={"card_show_first": 6, "card_show_last": 4}) # Format-preserving tokenization result = client.anonymize(text, entity_types=["CREDIT_CARD"], mode="pseudonymize") # Brand-preserving pseudonymization result = client.anonymize(text, entity_types=["CREDIT_CARD"], mode="pseudonymize", options={"card_preserve_brand": True})
Process application logs to remove any accidentally logged card numbers:
log_entries = [
"2024-01-15 14:32:01 INFO Payment processed: 4532015112830366",
"2024-01-15 14:32:02 ERROR Card declined: 5425233430109903",
"2024-01-15 14:32:03 DEBUG Request body: {card: '378282246310005'}"
]
results = client.batch_anonymize(
items=[{"text": log} for log in log_entries],
entity_types=["CREDIT_CARD"]
)
for r in results:
print(r.anonymized_text)
# 2024-01-15 14:32:01 INFO Payment processed: [CREDIT_CARD]
# 2024-01-15 14:32:02 ERROR Card declined: [CREDIT_CARD]
# 2024-01-15 14:32:03 DEBUG Request body: {card: '[CREDIT_CARD]'}
Redact all payment-related information including CVV and expiration dates:
payment_data = """ Transaction Details: Card: 4532-0151-1283-0366 Cardholder: John Smith CVV: 123 Expiry: 12/25 Amount: $150.00 """ result = client.anonymize( text=payment_data, entity_types=["CREDIT_CARD", "CVV", "CARD_EXPIRY", "PERSON"] ) print(result.anonymized_text) # Transaction Details: # Card: [CREDIT_CARD] # Cardholder: [PERSON] # CVV: [CVV] # Expiry: [CARD_EXPIRY] # Amount: $150.00
Process logs in real-time to prevent card data from being persisted:
import logging class PCILogFilter(logging.Filter): def __init__(self, api_client): super().__init__() self.client = api_client def filter(self, record): # Redact any card numbers before logging result = self.client.anonymize( text=record.msg, entity_types=["CREDIT_CARD", "CVV"] ) record.msg = result.anonymized_text return True # Apply filter to logger logger = logging.getLogger("payment") logger.addFilter(PCILogFilter(client))
Scan documents for card numbers to identify PCI scope:
def scan_for_card_data(file_path): with open(file_path, 'r') as f: content = f.read() result = client.detect( text=content, entity_types=["CREDIT_CARD", "CVV"] ) if result.entities: print(f"ALERT: Found {len(result.entities)} card data items in {file_path}") for entity in result.entities: print(f" - {entity.type} at line {entity.line}, confidence: {entity.confidence}") if entity.metadata.get('card_brand'): print(f" Brand: {entity.metadata['card_brand']}") return True return False
Luhn validation dramatically reduces false positives while ensuring all valid card numbers are caught:
# Default: Luhn validation enabled (recommended) result = client.anonymize( text=text, entity_types=["CREDIT_CARD"] ) # Luhn validation prevents false positives like: # - Order numbers: 1234567890123456 # - Account IDs: 9876543210987654 # - Random 16-digit numbers
Card numbers alone don't constitute full payment data. Always redact CVV, expiry, and cardholder name together:
# Comprehensive payment data redaction result = client.anonymize( text=payment_text, entity_types=[ "CREDIT_CARD", # Primary Account Number "CVV", # Security code (MUST never be stored) "CARD_EXPIRY", # Expiration date "PERSON" # Cardholder name ] )
Defense in depth: apply card redaction at multiple points in your system:
Different contexts require different masking levels:
# Customer receipts: Show last 4 receipt_result = client.anonymize(text, entity_types=["CREDIT_CARD"], mode="mask", options={"card_show_last": 4}) # Internal logs: Full redaction log_result = client.anonymize(text, entity_types=["CREDIT_CARD"], mode="redact") # Fraud analysis: BIN + Last 4 fraud_result = client.anonymize(text, entity_types=["CREDIT_CARD"], mode="mask", options={"card_show_first": 6, "card_show_last": 4})
Set up monitoring to alert when card numbers are found in unexpected places:
PCI Requirement: You must have processes to detect and alert on potential cardholder data exposures. Consider setting up automated scanning of logs, databases, and file systems with alerts when card numbers are detected.
Our detection achieves 99.9% accuracy with Luhn validation enabled. The combination of pattern matching, Luhn checksum verification, and BIN validation ensures extremely high precision. False positives from random 16-digit numbers are virtually eliminated.
Yes, our format-preserving tokenization can help reduce PCI scope by replacing real card numbers with tokens that maintain the same format but cannot be reversed without the tokenization key. However, consult with your QSA about your specific compliance requirements.
The API detects card numbers in plain text. Encrypted card data (which looks like random characters) will not be detected, which is the expected behavior - encrypted data doesn't need additional redaction. If you're storing encrypted cards, ensure the encryption is done before any logging or data export.
By default, the API looks for complete, valid card numbers. To detect partial numbers (like the last 4 digits in "ending in 1234"), use: options={"card_detect_partial": True}. Note this may increase false positives.
The API processes text input. For images or scanned documents, use OCR first to extract text, then pass to our API. For PDFs with embedded text, extract the text layer first. We can recommend OCR partners if needed.
Track 1 and Track 2 data from magnetic stripes are detected with: entity_types=["CREDIT_CARD", "MAGNETIC_STRIPE"]. This sensitive authentication data (SAD) must never be stored after authorization per PCI-DSS.
Our API is designed to help you achieve PCI compliance by redacting card data. We do not store any card numbers that pass through our API. For your own compliance, review our security documentation and consider whether card data transit through our API fits your PCI scope.
Achieve PCI-DSS compliance with 99.9% accurate card detection and compliant masking options.