Carrier Intelligence: Understanding Mobile Network Information

Leverage carrier intelligence for enhanced phone validation, fraud detection, and user experience optimization.

Carrier Intelligence: Understanding Mobile Network Information
8 de agosto de 2025
Actualizado el 5 de mayo de 2026
45 min read
Phone Validation

Carrier Intelligence: Understanding Mobile Network Information


Carrier intelligence provides valuable insights into mobile network characteristics, user behavior, and service capabilities. Leveraging this information enhances validation accuracy and enables sophisticated fraud detection.


Carrier Intelligence Overview

Carrier Intelligence Overview


Business Impact

  • User experience enhancement: By identifying a user's carrier and network type (e.g., 5G vs. LTE), businesses can optimize content delivery, such as adjusting video bitrates or pre-loading assets based on expected bandwidth.
  • Operational efficiency gains: Automated carrier lookups reduce the need for manual verification of customer data and streamline communication workflows by ensuring messages are sent via the most compatible network routes.
  • Cost optimization opportunities: Identifying "landline" vs. "mobile" numbers or "inactive" status before sending SMS campaigns prevents wasted spend on undeliverable messages.
  • Risk mitigation strategies: Real-time detection of SIM-swapping or recent porting activity allows security teams to trigger additional authentication steps during high-risk transactions.

Carrier Intelligence Overview


Carrier intelligence systems analyze mobile network data to provide real-time insights about phone numbers, carriers, and network characteristics. This technology enables businesses to:


  • Validate phone numbers with carrier-level accuracy by checking the actual network status rather than just relying on syntax or regex patterns.
  • Detect fraud through network behavior analysis, such as identifying numbers that have been recently ported or are currently in a roaming state, which are often markers for identity theft.
  • Optimize user experience by understanding network capabilities, allowing for specialized routing or customized UI elements based on the carrier's specific services (like RCS support).
  • Reduce costs by preventing invalid transactions and avoiding the high fees associated with sending messages to international or premium-rate numbers incorrectly identified as local.

Key Components


HLR (Home Location Register) Lookups

The HLR is the central database that contains details of each mobile phone subscriber authorized to use a GSM core network. Lookups provide:

  • Real-time carrier identification: Confirms which network currently "owns" the subscriber.
  • Network status verification: Determines if the phone is currently switched on and registered.
  • Roaming and portability detection: Checks if the user is outside their home country or has moved their number to a different provider.

MCC/MNC Database

Mobile Country Codes (MCC) and Mobile Network Codes (MNC) are the foundation of global telephony:

  • MCC/MNC Mapping: Essential for identifying the geographical origin and specific operator of a device.
  • Carrier Name Resolution: Converts technical codes into human-readable brand names (e.g., 310-260 becomes Verizon).
  • Network Technology Detection: Identifies whether the carrier supports specific protocols like 5G NR or VoLTE.

Fraud Detection Engine

A sophisticated layer that sits atop raw data to interpret risk:

  • Velocity analysis: Tracks how many times a number is queried across a timeframe to spot botnets or automated account creation.
  • Pattern recognition: Identifies clusters of numbers being activated or used in a way that suggests a coordinated attack.
  • Risk scoring algorithms: Aggregates data points (location, roaming, porting history) into a single actionable number.

HLR Lookup System for Real-Time Carrier Intelligence


// Production-ready HLR lookup system for carrier intelligence and phone validation
interface HLRRequest {
  phoneNumber: string
  countryCode?: string
  includeLocation?: boolean
  includeRoaming?: boolean
  timeout?: number
}

interface HLRResponse {
  phoneNumber: string
  imsi?: string
  mcc?: string
  mnc?: string
  msisdn?: string
  status: 'active' | 'inactive' | 'invalid' | 'ported' | 'unknown'
  carrier: {
    name: string
    country: string
    network: string
    mcc: string
    mnc: string
  }
  location?: {
    country: string
    region: string
    city: string
    coordinates?: {
      latitude: number
      longitude: number
    }
  }
  roaming?: {
    isRoaming: boolean
    originalCarrier?: string
    currentCarrier?: string
  }
  ported?: {
    isPorted: boolean
    originalCarrier?: string
    portedDate?: number
  }
  lastSeen?: number
  firstSeen?: number
}

interface CarrierDatabaseEntry {
  mcc: string
  mnc: string
  country: string
  carrier: string
  network: string
  bands: string[]
  technologies: string[]
  roamingAgreements: string[]
  lastUpdated: number
}

class HLRIntelligenceSystem {
  private carrierDatabase: Map<string, CarrierDatabaseEntry> = new Map()
  private cache: Map<string, { response: HLRResponse; timestamp: number }> = new Map()
  private requestQueue: Array<{ request: HLRRequest; resolve: Function; reject: Function }> = []
  private isProcessingQueue = false
  private rateLimitDelay = 100 // ms between requests
  private maxConcurrentRequests = 5

  constructor() {
    this.initializeCarrierDatabase()
    this.startQueueProcessor()
  }

  async performHLRLookup(request: HLRRequest): Promise<HLRResponse> {
    const cacheKey = this.generateCacheKey(request)

    // Check cache first (5 minutes TTL)
    const cached = this.getCachedResponse(cacheKey)
    if (cached) {
      return cached
    }

    return new Promise((resolve, reject) => {
      this.requestQueue.push({ request, resolve, reject })
      this.processQueue()
    })
  }

  private generateCacheKey(request: HLRRequest): string {
    return Buffer.from(`${request.phoneNumber}-${request.countryCode || ''}`).toString('base64')
  }

  private getCachedResponse(cacheKey: string): HLRResponse | null {
    const cached = this.cache.get(cacheKey)
    if (cached && Date.now() - cached.timestamp < 5 * 60 * 1000) {
      return cached.response
    }

    if (cached) {
      this.cache.delete(cacheKey)
    }

    return null
  }

  private async processQueue(): Promise<void> {
    if (this.isProcessingQueue || this.requestQueue.length === 0) {
      return
    }

    this.isProcessingQueue = true

    while (this.requestQueue.length > 0) {
      const batch = this.requestQueue.splice(0, this.maxConcurrentRequests)

      const batchPromises = batch.map(async ({ request, resolve, reject }) => {
        try {
          const response = await this.executeHLRRequest(request)
          this.cacheResponse(request, response)
          resolve(response)
        } catch (error) {
          reject(error)
        }
      })

      await Promise.allSettled(batchPromises)

      // Rate limiting delay
      if (this.requestQueue.length > 0) {
        await new Promise(resolve => setTimeout(resolve, this.rateLimitDelay))
      }
    }

    this.isProcessingQueue = false
  }

  private async executeHLRRequest(request: HLRRequest): Promise<HLRResponse> {
    // In production, this would connect to HLR providers like:
    // - TMT Analysis, OpenCellID, Combain, etc.
    // For demo, simulate HLR response

    const phoneNumber = this.normalizePhoneNumber(request.phoneNumber, request.countryCode)

    if (!this.isValidPhoneNumber(phoneNumber)) {
      throw new Error('Invalid phone number format')
    }

    // Simulate HLR lookup delay
    await new Promise(resolve => setTimeout(resolve, 200 + Math.random() * 300))

    return this.generateMockHLRResponse(phoneNumber, request)
  }

  private normalizePhoneNumber(phoneNumber: string, countryCode?: string): string {
    // Remove all non-digit characters
    let normalized = phoneNumber.replace(/D/g, '')

    // Add country code if not present
    if (countryCode && !normalized.startsWith(countryCode)) {
      normalized = countryCode + normalized
    }

    return normalized
  }

  private isValidPhoneNumber(phoneNumber: string): boolean {
    // Basic validation - should be 10-15 digits
    return phoneNumber.length >= 10 && phoneNumber.length <= 15 && /^d+$/.test(phoneNumber)
  }

  private generateMockHLRResponse(phoneNumber: string, request: HLRRequest): HLRResponse {
    const mockCarriers = [
      { mcc: '250', mnc: '01', country: 'RU', carrier: 'MTS', network: 'MTS Russia' },
      { mcc: '250', mnc: '02', country: 'RU', carrier: 'MegaFon', network: 'MegaFon Russia' },
      { mcc: '250', mnc: '20', country: 'RU', carrier: 'Tele2', network: 'Tele2 Russia' },
      { mcc: '250', mnc: '99', country: 'RU', carrier: 'Beeline', network: 'Beeline Russia' },
      { mcc: '310', mnc: '120', country: 'US', carrier: 'T-Mobile', network: 'T-Mobile USA' },
      { mcc: '310', mnc: '260', country: 'US', carrier: 'Verizon', network: 'Verizon Wireless' }
    ]

    // Select carrier based on phone number prefix (simplified)
    const prefix = phoneNumber.substring(0, 3)
    let selectedCarrier = mockCarriers[0]

    if (prefix === '791' || prefix === '792') selectedCarrier = mockCarriers[0] // MTS
    else if (prefix === '793' || prefix === '798') selectedCarrier = mockCarriers[1] // MegaFon
    else if (prefix === '795') selectedCarrier = mockCarriers[2] // Tele2
    else if (prefix === '796' || prefix === '790') selectedCarrier = mockCarriers[3] // Beeline
    else if (phoneNumber.startsWith('1')) selectedCarrier = mockCarriers[4] // T-Mobile
    else selectedCarrier = mockCarriers[5] // Verizon

    const isRoaming = Math.random() < 0.1 // 10% chance of roaming
    const isPorted = Math.random() < 0.15 // 15% chance of ported number

    const response: HLRResponse = {
      phoneNumber: request.phoneNumber,
      imsi: `310150${Math.random().toString().substring(2, 15)}`,
      mcc: selectedCarrier.mcc,
      mnc: selectedCarrier.mnc,
      status: Math.random() < 0.85 ? 'active' : 'inactive', // 85% active
      carrier: {
        name: selectedCarrier.carrier,
        country: selectedCarrier.country,
        network: selectedCarrier.network,
        mcc: selectedCarrier.mcc,
        mnc: selectedCarrier.mnc
      }
    }

    // Add location data if requested
    if (request.includeLocation) {
      response.location = {
        country: selectedCarrier.country === 'RU' ? 'Russia' : 'United States',
        region: selectedCarrier.country === 'RU' ? 'Moscow' : 'California',
        city: selectedCarrier.country === 'RU' ? 'Moscow' : 'Los Angeles',
        coordinates: selectedCarrier.country === 'RU'
          ? { latitude: 55.7558, longitude: 37.6176 }
          : { latitude: 34.0522, longitude: -118.2437 }
      }
    }

    // Add roaming data if requested
    if (request.includeRoaming) {
      response.roaming = {
        isRoaming,
        originalCarrier: isRoaming ? selectedCarrier.carrier : undefined,
        currentCarrier: isRoaming ? 'Roaming Partner' : selectedCarrier.carrier
      }
    }

    // Add ported information
    if (isPorted) {
      response.ported = {
        isPorted: true,
        originalCarrier: 'Original Carrier',
        portedDate: Date.now() - Math.random() * 365 * 24 * 60 * 60 * 1000
      }
    }

    // Add timestamps
    response.lastSeen = Date.now() - Math.random() * 7 * 24 * 60 * 60 * 1000 // Within last week
    response.firstSeen = Date.now() - Math.random() * 365 * 24 * 60 * 60 * 1000 // Within last year

    return response
  }

  private initializeCarrierDatabase(): void {
    // In production, load comprehensive carrier database
    // For demo, populate with sample data

    const carriers: CarrierDatabaseEntry[] = [
      {
        mcc: '250',
        mnc: '01',
        country: 'RU',
        carrier: 'MTS',
        network: 'MTS Russia',
        bands: ['GSM 900', 'GSM 1800', 'UMTS 2100', 'LTE 1800', 'LTE 2600'],
        technologies: ['2G', '3G', '4G', 'VoLTE'],
        roamingAgreements: ['US', 'EU', 'CN', 'IN'],
        lastUpdated: Date.now()
      },
      {
        mcc: '310',
        mnc: '120',
        country: 'US',
        carrier: 'T-Mobile',
        network: 'T-Mobile USA',
        bands: ['GSM 1900', 'UMTS 1900', 'LTE 700', 'LTE 1900'],
        technologies: ['3G', '4G', '5G'],
        roamingAgreements: ['CA', 'MX', 'EU'],
        lastUpdated: Date.now()
      }
    ]

    carriers.forEach(carrier => {
      const key = `${carrier.mcc}-${carrier.mnc}`
      this.carrierDatabase.set(key, carrier)
    })

    console.log(`Carrier database initialized with ${carriers.length} entries`)
  }

  private startQueueProcessor(): void {
    // Start background queue processor
    setInterval(() => {
      if (this.requestQueue.length > 0 && !this.isProcessingQueue) {
        this.processQueue()
      }
    }, 100)
  }

  private cacheResponse(request: HLRRequest, response: HLRResponse): void {
    const cacheKey = this.generateCacheKey(request)
    this.cache.set(cacheKey, {
      response,
      timestamp: Date.now()
    })

    // Clean old cache entries (keep only last 10000)
    if (this.cache.size > 10000) {
      const entries = Array.from(this.cache.entries())
      const oldestKey = entries.sort(([, a], [, b]) => a.timestamp - b.timestamp)[0][0]
      this.cache.delete(oldestKey)
    }
  }

  // Batch HLR lookups for multiple numbers
  async performBatchHLRLookup(requests: HLRRequest[]): Promise<Map<string, HLRResponse>> {
    const results = new Map<string, HLRResponse>()

    // Process in batches of 10
    const batchSize = 10
    for (let i = 0; i < requests.length; i += batchSize) {
      const batch = requests.slice(i, i + batchSize)

      const batchPromises = batch.map(async (request) => {
        try {
          const response = await this.performHLRLookup(request)
          return { phoneNumber: request.phoneNumber, response }
        } catch (error) {
          console.error(`Batch HLR lookup failed for ${request.phoneNumber}:`, error)
          return { phoneNumber: request.phoneNumber, response: null }
        }
      })

      const batchResults = await Promise.all(batchPromises)

      batchResults.forEach(({ phoneNumber, response }) => {
        if (response) {
          results.set(phoneNumber, response)
        }
      })

      // Small delay between batches
      if (i + batchSize < requests.length) {
        await new Promise(resolve => setTimeout(resolve, 500))
      }
    }

    return results
  }

  // Get carrier statistics
  getCarrierStatistics(): {
    totalLookups: number
    uniqueCarriers: number
    activeSubscribers: number
    roamingSubscribers: number
    portedNumbers: number
    topCarriers: Array<{ carrier: string; count: number }>
  } {
    const responses = Array.from(this.cache.values()).map(c => c.response)
    const total = responses.length

    if (total === 0) {
      return {
        totalLookups: 0,
        uniqueCarriers: 0,
        activeSubscribers: 0,
        roamingSubscribers: 0,
        portedNumbers: 0,
        topCarriers: []
      }
    }

    const carriers = responses.map(r => r.carrier.name)
    const uniqueCarriers = new Set(carriers).size
    const activeSubscribers = responses.filter(r => r.status === 'active').length
    const roamingSubscribers = responses.filter(r => r.roaming?.isRoaming).length
    const portedNumbers = responses.filter(r => r.ported?.isPorted).length

    // Count carrier occurrences
    const carrierCounts = new Map<string, number>()
    carriers.forEach(carrier => {
      const count = carrierCounts.get(carrier) || 0
      carrierCounts.set(carrier, count + 1)
    })

    const topCarriers = Array.from(carrierCounts.entries())
      .sort(([, a], [, b]) => b - a)
      .slice(0, 10)
      .map(([carrier, count]) => ({ carrier, count }))

    return {
      totalLookups: total,
      uniqueCarriers,
      activeSubscribers,
      roamingSubscribers,
      portedNumbers,
      topCarriers
    }
  }
}

// Initialize HLR intelligence system
const hlrIntelligenceSystem = new HLRIntelligenceSystem()

// HLR lookup endpoint
app.post('/api/hlr/lookup', async (req, res) => {
  try {
    const { phoneNumber, countryCode, includeLocation, includeRoaming } = req.body

    if (!phoneNumber) {
      return res.status(400).json({ error: 'phoneNumber required' })
    }

    const request: HLRRequest = {
      phoneNumber,
      countryCode,
      includeLocation,
      includeRoaming,
      timeout: 10000
    }

    const response = await hlrIntelligenceSystem.performHLRLookup(request)

    res.json({
      hlr: response,
      timestamp: new Date().toISOString()
    })

  } catch (error) {
    console.error('HLR lookup error:', error)
    res.status(500).json({ error: 'HLR lookup failed' })
  }
})

// Batch HLR lookup endpoint
app.post('/api/hlr/lookup-batch', async (req, res) => {
  try {
    const { phoneNumbers, countryCode, includeLocation, includeRoaming } = req.body

    if (!Array.isArray(phoneNumbers)) {
      return res.status(400).json({ error: 'phoneNumbers array required' })
    }

    const requests: HLRRequest[] = phoneNumbers.map(phoneNumber => ({
      phoneNumber,
      countryCode,
      includeLocation,
      includeRoaming,
      timeout: 10000
    }))

    const results = await hlrIntelligenceSystem.performBatchHLRLookup(requests)

    res.json({
      results: Object.fromEntries(results.entries()),
      count: results.size,
      timestamp: new Date().toISOString()
    })

  } catch (error) {
    console.error('Batch HLR lookup error:', error)
    res.status(500).json({ error: 'Batch HLR lookup failed' })
  }
})

// HLR statistics endpoint
app.get('/api/hlr/statistics', (req, res) => {
  try {
    const stats = hlrIntelligenceSystem.getCarrierStatistics()

    res.json({
      statistics: stats,
      timestamp: new Date().toISOString()
    })

  } catch (error) {
    console.error('HLR statistics error:', error)
    res.status(500).json({ error: 'Statistics retrieval failed' })
  }
})

console.log('HLR Intelligence System initialized')

Carrier Detection and Intelligence Engine


// Advanced carrier intelligence engine for phone number analysis
interface CarrierIntelligenceConfig {
  enableHLRLookup: boolean
  enableNetworkAnalysis: boolean
  enableFraudDetection: boolean
  cacheResults: boolean
  cacheDuration: number
  maxRetries: number
}

interface PhoneNumberAnalysis {
  phoneNumber: string
  normalizedNumber: string
  country: {
    code: string
    name: string
    region: string
  }
  carrier: {
    name: string
    mcc: string
    mnc: string
    networkType: 'gsm' | 'cdma' | 'lte' | '5g' | 'unknown'
    bands: string[]
  }
  validation: {
    isValid: boolean
    format: 'e164' | 'national' | 'international' | 'invalid'
    checksumValid: boolean
    lengthValid: boolean
  }
  intelligence: {
    isActive: boolean
    isRoaming: boolean
    isPorted: boolean
    lastSeen: number
    riskScore: number
    confidence: number
  }
  network: {
    technology: string[]
    bands: string[]
    roamingAgreements: string[]
    coverage: {
      area: string[]
      quality: 'excellent' | 'good' | 'fair' | 'poor'
    }
  }
  fraudIndicators: {
    velocityCheck: boolean
    patternAnalysis: boolean
    blacklisted: boolean
    suspiciousActivity: boolean
    riskFactors: string[]
  }
}

interface FraudDetectionResult {
  phoneNumber: string
  riskScore: number // 0-100
  riskLevel: 'low' | 'medium' | 'high' | 'critical'
  indicators: string[]
  recommendations: string[]
  blocked: boolean
  requiresReview: boolean
}

class CarrierIntelligenceEngine {
  private config: CarrierIntelligenceConfig
  private hlrSystem: HLRIntelligenceSystem
  private numberCache: Map<string, PhoneNumberAnalysis> = new Map()
  private fraudCache: Map<string, FraudDetectionResult> = new Map()

  constructor(config: CarrierIntelligenceConfig) {
    this.config = config
    this.hlrSystem = new HLRIntelligenceSystem()
  }

  async analyzePhoneNumber(phoneNumber: string, countryCode?: string): Promise<PhoneNumberAnalysis> {
    const normalizedNumber = this.normalizePhoneNumber(phoneNumber, countryCode)

    // Check cache first
    if (this.config.cacheResults) {
      const cached = this.getCachedAnalysis(normalizedNumber)
      if (cached) {
        return cached
      }
    }

    try {
      // Perform comprehensive phone number analysis
      const [basicInfo, carrierInfo, validationInfo, intelligenceInfo] = await Promise.all([
        this.getBasicNumberInfo(normalizedNumber),
        this.getCarrierInformation(normalizedNumber),
        this.validatePhoneNumberFormat(normalizedNumber),
        this.gatherIntelligenceData(normalizedNumber)
      ])

      const analysis: PhoneNumberAnalysis = {
        phoneNumber,
        normalizedNumber,
        ...basicInfo,
        carrier: carrierInfo,
        validation: validationInfo,
        intelligence: intelligenceInfo.intelligence,
        network: intelligenceInfo.network,
        fraudIndicators: intelligenceInfo.fraudIndicators
      }

      // Cache result
      if (this.config.cacheResults) {
        this.cacheAnalysis(normalizedNumber, analysis)
      }

      return analysis

    } catch (error) {
      console.error('Phone number analysis failed:', error)

      // Return basic analysis on error
      return {
        phoneNumber,
        normalizedNumber,
        country: { code: countryCode || 'US', name: 'Unknown', region: 'Unknown' },
        carrier: {
          name: 'Unknown',
          mcc: '000',
          mnc: '00',
          networkType: 'unknown',
          bands: []
        },
        validation: {
          isValid: false,
          format: 'invalid',
          checksumValid: false,
          lengthValid: false
        },
        intelligence: {
          isActive: false,
          isRoaming: false,
          isPorted: false,
          lastSeen: 0,
          riskScore: 100,
          confidence: 0
        },
        network: {
          technology: [],
          bands: [],
          roamingAgreements: [],
          coverage: {
            area: [],
            quality: 'poor'
          }
        },
        fraudIndicators: {
          velocityCheck: false,
          patternAnalysis: false,
          blacklisted: true,
          suspiciousActivity: true,
          riskFactors: ['Analysis failed']
        }
      }
    }
  }

  private normalizePhoneNumber(phoneNumber: string, countryCode?: string): string {
    let normalized = phoneNumber.replace(/D/g, '')

    // Add country code if not present and we have it
    if (countryCode && !normalized.startsWith(countryCode)) {
      normalized = countryCode + normalized
    }

    return normalized
  }

  private async getBasicNumberInfo(phoneNumber: string): Promise<{
    country: PhoneNumberAnalysis['country']
  }> {
    // In production, use comprehensive number database
    // For demo, use simple country detection based on prefixes

    const countryPrefixes: Record<string, { code: string; name: string; region: string }> = {
      '1': { code: 'US', name: 'United States', region: 'North America' },
      '7': { code: 'RU', name: 'Russia', region: 'Europe' },
      '44': { code: 'GB', name: 'United Kingdom', region: 'Europe' },
      '33': { code: 'FR', name: 'France', region: 'Europe' },
      '49': { code: 'DE', name: 'Germany', region: 'Europe' },
      '39': { code: 'IT', name: 'Italy', region: 'Europe' },
      '34': { code: 'ES', name: 'Spain', region: 'Europe' },
      '31': { code: 'NL', name: 'Netherlands', region: 'Europe' }
    }

    let countryCode = '1' // Default to US
    for (let i = 1; i <= 3; i++) {
      const prefix = phoneNumber.substring(0, i)
      if (countryPrefixes[prefix]) {
        countryCode = prefix
        break
      }
    }

    return {
      country: countryPrefixes[countryCode] || countryPrefixes['1']
    }
  }

  private async getCarrierInformation(phoneNumber: string): Promise<PhoneNumberAnalysis['carrier']> {
    if (!this.config.enableHLRLookup) {
      return {
        name: 'Unknown',
        mcc: '000',
        mnc: '00',
        networkType: 'unknown',
        bands: []
      }
    }

    try {
      const hlrResponse = await this.hlrSystem.performHLRLookup({
        phoneNumber,
        includeLocation: false,
        includeRoaming: false
      })

      return {
        name: hlrResponse.carrier.name,
        mcc: hlrResponse.carrier.mcc,
        mnc: hlrResponse.carrier.mnc,
        networkType: this.determineNetworkType(hlrResponse.carrier.mcc, hlrResponse.carrier.mnc),
        bands: this.getCarrierBands(hlrResponse.carrier.mcc, hlrResponse.carrier.mnc)
      }
    } catch (error) {
      console.error('Carrier lookup failed:', error)
      return {
        name: 'Unknown',
        mcc: '000',
        mnc: '00',
        networkType: 'unknown',
        bands: []
      }
    }
  }

  private validatePhoneNumberFormat(phoneNumber: string): PhoneNumberAnalysis['validation'] {
    const length = phoneNumber.length
    const isValidLength = length >= 10 && length <= 15

    // Basic checksum validation (simplified)
    const checksumValid = this.validateChecksum(phoneNumber)

    return {
      isValid: isValidLength && checksumValid,
      format: length > 10 ? 'international' : 'national',
      checksumValid,
      lengthValid: isValidLength
    }
  }

  private async gatherIntelligenceData(phoneNumber: string): Promise<{
    intelligence: PhoneNumberAnalysis['intelligence']
    network: PhoneNumberAnalysis['network']
    fraudIndicators: PhoneNumberAnalysis['fraudIndicators']
  }> {
    const intelligence: PhoneNumberAnalysis['intelligence'] = {
      isActive: Math.random() > 0.1, // 90% active
      isRoaming: Math.random() > 0.9, // 10% roaming
      isPorted: Math.random() > 0.85, // 15% ported
      lastSeen: Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000, // Within last 30 days
      riskScore: Math.floor(Math.random() * 100),
      confidence: 75 + Math.floor(Math.random() * 25) // 75-100%
    }

    const network: PhoneNumberAnalysis['network'] = {
      technology: ['4G', 'VoLTE'],
      bands: ['LTE 1800', 'LTE 2600'],
      roamingAgreements: ['US', 'EU', 'APAC'],
      coverage: {
        area: ['Nationwide', 'Urban', 'Suburban'],
        quality: Math.random() > 0.5 ? 'excellent' : 'good'
      }
    }

    const fraudIndicators: PhoneNumberAnalysis['fraudIndicators'] = {
      velocityCheck: Math.random() > 0.95, // 5% suspicious velocity
      patternAnalysis: Math.random() > 0.98, // 2% suspicious pattern
      blacklisted: Math.random() > 0.99, // 1% blacklisted
      suspiciousActivity: Math.random() > 0.97, // 3% suspicious
      riskFactors: intelligence.riskScore > 70 ? ['High risk score'] : []
    }

    return { intelligence, network, fraudIndicators }
  }

  private determineNetworkType(mcc: string, mnc: string): PhoneNumberAnalysis['carrier']['networkType'] {
    // In production, use carrier database to determine network type
    // For demo, return based on MCC
    if (mcc === '310' || mcc === '311' || mcc === '312') {
      return 'cdma'
    }
    return 'gsm'
  }

  private getCarrierBands(mcc: string, mnc: string): string[] {
    // In production, use carrier database to get supported bands
    // For demo, return common bands
    return ['GSM 900', 'GSM 1800', 'UMTS 2100', 'LTE 1800', 'LTE 2600']
  }

  private validateChecksum(phoneNumber: string): boolean {
    // Simplified checksum validation
    // In production, implement proper checksum algorithms per country
    if (phoneNumber.length < 10) return false

    // Simple modulo check (not real checksum)
    const digits = phoneNumber.split('').map(Number)
    const sum = digits.reduce((acc, digit, index) => acc + digit * (index + 1), 0)

    return sum % 10 === 0
  }

  private getCachedAnalysis(phoneNumber: string): PhoneNumberAnalysis | null {
    const cached = this.numberCache.get(phoneNumber)
    if (cached && Date.now() - this.config.cacheDuration < cached.intelligence.lastSeen) {
      return cached
    }

    if (cached) {
      this.numberCache.delete(phoneNumber)
    }

    return null
  }

  private cacheAnalysis(phoneNumber: string, analysis: PhoneNumberAnalysis): void {
    this.numberCache.set(phoneNumber, analysis)

    // Maintain cache size
    if (this.numberCache.size > 50000) {
      const entries = Array.from(this.numberCache.entries())
      const oldestKey = entries.sort(([, a], [, b]) => a.intelligence.lastSeen - b.intelligence.lastSeen)[0][0]
      this.numberCache.delete(oldestKey)
    }
  }

  // Fraud detection analysis
  async detectFraud(phoneNumber: string): Promise<FraudDetectionResult> {
    const cached = this.fraudCache.get(phoneNumber)
    if (cached && Date.now() - 24 * 60 * 60 * 1000 < Date.now()) { // 24 hour cache
      return cached
    }

    try {
      const analysis = await this.analyzePhoneNumber(phoneNumber)

      const indicators: string[] = []
      const recommendations: string[] = []

      // Analyze risk indicators
      if (analysis.fraudIndicators.blacklisted) {
        indicators.push('Number is blacklisted')
        recommendations.push('Block transaction immediately')
      }

      if (analysis.fraudIndicators.velocityCheck) {
        indicators.push('Suspicious velocity pattern')
        recommendations.push('Require additional verification')
      }

      if (analysis.fraudIndicators.suspiciousActivity) {
        indicators.push('Suspicious activity detected')
        recommendations.push('Manual review required')
      }

      if (analysis.intelligence.riskScore > 80) {
        indicators.push('High risk score')
        recommendations.push('Enhanced security measures required')
      }

      if (analysis.intelligence.isRoaming) {
        indicators.push('International roaming detected')
        recommendations.push('Verify location consistency')
      }

      const riskScore = Math.max(
        analysis.intelligence.riskScore,
        analysis.fraudIndicators.blacklisted ? 100 : 0,
        analysis.fraudIndicators.velocityCheck ? 85 : 0,
        analysis.fraudIndicators.suspiciousActivity ? 90 : 0
      )

      const riskLevel = riskScore > 90 ? 'critical' :
                       riskScore > 70 ? 'high' :
                       riskScore > 40 ? 'medium' : 'low'

      const result: FraudDetectionResult = {
        phoneNumber,
        riskScore,
        riskLevel,
        indicators,
        recommendations,
        blocked: riskScore > 95,
        requiresReview: riskScore > 70
      }

      this.fraudCache.set(phoneNumber, result)

      return result

    } catch (error) {
      console.error('Fraud detection failed:', error)

      return {
        phoneNumber,
        riskScore: 100,
        riskLevel: 'critical',
        indicators: ['Analysis failed'],
        recommendations: ['Block transaction'],
        blocked: true,
        requiresReview: true
      }
    }
  }

  // Batch analysis for multiple numbers
  async analyzePhoneNumbersBatch(phoneNumbers: string[]): Promise<Map<string, PhoneNumberAnalysis>> {
    const results = new Map<string, PhoneNumberAnalysis>()

    // Process in batches of 20
    const batchSize = 20
    for (let i = 0; i < phoneNumbers.length; i += batchSize) {
      const batch = phoneNumbers.slice(i, i + batchSize)

      const batchPromises = batch.map(async (phoneNumber) => {
        try {
          const analysis = await this.analyzePhoneNumber(phoneNumber)
          return { phoneNumber, analysis }
        } catch (error) {
          console.error(`Batch analysis failed for ${phoneNumber}:`, error)
          return { phoneNumber, analysis: null }
        }
      })

      const batchResults = await Promise.all(batchPromises)

      batchResults.forEach(({ phoneNumber, analysis }) => {
        if (analysis) {
          results.set(phoneNumber, analysis)
        }
      })

      // Small delay between batches
      if (i + batchSize < phoneNumbers.length) {
        await new Promise(resolve => setTimeout(resolve, 200))
      }
    }

    return results
  }

  // Get intelligence statistics
  getIntelligenceStats(): {
    totalAnalyses: number
    validNumbers: number
    activeSubscribers: number
    roamingSubscribers: number
    averageRiskScore: number
    topRiskFactors: Array<{ factor: string; count: number }>
  } {
    const analyses = Array.from(this.numberCache.values())
    const total = analyses.length

    if (total === 0) {
      return {
        totalAnalyses: 0,
        validNumbers: 0,
        activeSubscribers: 0,
        roamingSubscribers: 0,
        averageRiskScore: 0,
        topRiskFactors: []
      }
    }

    const validNumbers = analyses.filter(a => a.validation.isValid).length
    const activeSubscribers = analyses.filter(a => a.intelligence.isActive).length
    const roamingSubscribers = analyses.filter(a => a.intelligence.isRoaming).length
    const averageRiskScore = analyses.reduce((sum, a) => sum + a.intelligence.riskScore, 0) / total

    // Count risk factors
    const riskFactors = new Map<string, number>()
    analyses.forEach(analysis => {
      analysis.fraudIndicators.riskFactors.forEach(factor => {
        const count = riskFactors.get(factor) || 0
        riskFactors.set(factor, count + 1)
      })
    })

    const topRiskFactors = Array.from(riskFactors.entries())
      .sort(([, a], [, b]) => b - a)
      .slice(0, 10)
      .map(([factor, count]) => ({ factor, count }))

    return {
      totalAnalyses: total,
      validNumbers,
      activeSubscribers,
      roamingSubscribers,
      averageRiskScore,
      topRiskFactors
    }
  }
}

// Enhanced phone number validation with carrier intelligence
class EnhancedPhoneValidator {
  private carrierEngine: CarrierIntelligenceEngine
  private validationCache: Map<string, {
    result: PhoneNumberAnalysis
    expires: number
  }> = new Map()

  constructor() {
    const config: CarrierIntelligenceConfig = {
      enableHLRLookup: true,
      enableNetworkAnalysis: true,
      enableFraudDetection: true,
      cacheResults: true,
      cacheDuration: 24 * 60 * 60 * 1000, // 24 hours
      maxRetries: 3
    }

    this.carrierEngine = new CarrierIntelligenceEngine(config)
  }

  async validatePhoneNumber(phoneNumber: string, countryCode?: string): Promise<{
    isValid: boolean
    analysis: PhoneNumberAnalysis
    fraudCheck?: FraudDetectionResult
    recommendation: string
  }> {
    // Check cache first
    const cached = this.getCachedValidation(phoneNumber)
    if (cached) {
      return {
        isValid: cached.result.validation.isValid,
        analysis: cached.result,
        recommendation: this.generateRecommendation(cached.result)
      }
    }

    try {
      // Perform comprehensive analysis
      const analysis = await this.carrierEngine.analyzePhoneNumber(phoneNumber, countryCode)

      // Perform fraud detection if enabled
      let fraudCheck: FraudDetectionResult | undefined
      if (this.carrierEngine['config'].enableFraudDetection) {
        fraudCheck = await this.carrierEngine.detectFraud(phoneNumber)
      }

      const result = {
        isValid: analysis.validation.isValid && analysis.intelligence.confidence > 50,
        analysis,
        fraudCheck,
        recommendation: this.generateRecommendation(analysis, fraudCheck)
      }

      // Cache result for 24 hours
      this.cacheValidation(phoneNumber, result)

      return result

    } catch (error) {
      console.error('Enhanced phone validation failed:', error)

      return {
        isValid: false,
        analysis: {} as PhoneNumberAnalysis,
        recommendation: 'Validation failed - manual review required'
      }
    }
  }

  private getCachedValidation(phoneNumber: string): PhoneNumberAnalysis | null {
    const cached = this.validationCache.get(phoneNumber)
    if (cached && cached.expires > Date.now()) {
      return cached.result
    }

    if (cached) {
      this.validationCache.delete(phoneNumber)
    }

    return null
  }

  private cacheValidation(phoneNumber: string, result: any): void {
    this.validationCache.set(phoneNumber, {
      result: result.analysis,
      expires: Date.now() + 24 * 60 * 60 * 1000 // 24 hours
    })

    // Maintain cache size
    if (this.validationCache.size > 100000) {
      const entries = Array.from(this.validationCache.entries())
      const oldestKey = entries.sort(([, a], [, b]) => a.expires - b.expires)[0][0]
      this.validationCache.delete(oldestKey)
    }
  }

  private generateRecommendation(
    analysis: PhoneNumberAnalysis,
    fraudCheck?: FraudDetectionResult
  ): string {
    if (!analysis.validation.isValid) {
      return 'Invalid phone number format - please check and retry'
    }

    if (analysis.intelligence.riskScore > 80) {
      return 'High risk number detected - enhanced verification required'
    }

    if (analysis.intelligence.isRoaming) {
      return 'Roaming number detected - verify international compatibility'
    }

    if (analysis.intelligence.isPorted) {
      return 'Ported number detected - carrier information may be outdated'
    }

    if (fraudCheck?.blocked) {
      return 'Number blocked due to fraud indicators - transaction denied'
    }

    if (fraudCheck?.requiresReview) {
      return 'Number requires manual review before processing'
    }

    return 'Phone number validated successfully'
  }

  // Batch validation for multiple numbers
  async validatePhoneNumbersBatch(phoneNumbers: string[]): Promise<Map<string, any>> {
    return this.carrierEngine.analyzePhoneNumbersBatch(phoneNumbers)
  }

  getValidationStats(): {
    totalValidations: number
    validNumbers: number
    highRiskNumbers: number
    roamingNumbers: number
    averageConfidence: number
  } {
    return this.carrierEngine.getIntelligenceStats()
  }
}

// Initialize enhanced phone validator
const enhancedPhoneValidator = new EnhancedPhoneValidator()

// Enhanced phone validation endpoints
app.post('/api/validate-phone-enhanced', async (req, res) => {
  try {
    const { phoneNumber, countryCode } = req.body

    if (!phoneNumber) {
      return res.status(400).json({ error: 'phoneNumber required' })
    }

    const result = await enhancedPhoneValidator.validatePhoneNumber(phoneNumber, countryCode)

    res.json({
      validation: result,
      timestamp: new Date().toISOString()
    })

  } catch (error) {
    console.error('Enhanced phone validation error:', error)
    res.status(500).json({ error: 'Enhanced validation failed' })
  }
})

// Batch phone validation endpoint
app.post('/api/validate-phones-batch', async (req, res) => {
  try {
    const { phoneNumbers } = req.body

    if (!Array.isArray(phoneNumbers)) {
      return res.status(400).json({ error: 'phoneNumbers array required' })
    }

    const results = await enhancedPhoneValidator.validatePhoneNumbersBatch(phoneNumbers)

    res.json({
      results: Object.fromEntries(results.entries()),
      count: results.size,
      timestamp: new Date().toISOString()
    })

  } catch (error) {
    console.error('Batch phone validation error:', error)
    res.status(500).json({ error: 'Batch validation failed' })
  }
})

// Phone validation statistics endpoint
app.get('/api/phone-validation-stats', (req, res) => {
  try {
    const stats = enhancedPhoneValidator.getValidationStats()

    res.json({
      statistics: stats,
      timestamp: new Date().toISOString()
    })

  } catch (error) {
    console.error('Statistics retrieval error:', error)
    res.status(500).json({ error: 'Statistics retrieval failed' })
  }
})

console.log('Enhanced Phone Validation with Carrier Intelligence initialized')

Fraud Detection Integration


Integrating fraud detection with carrier intelligence provides comprehensive security for phone-based transactions.


Risk Assessment Framework


Velocity Analysis

Velocity analysis measures the frequency of actions from a specific phone number.

  • Why it matters: A high volume of requests (e.g., 50 SMS requests in 5 minutes) usually indicates a bot attempt to bypass authentication or a distributed denial-of-service (DDoS) attack on your SMS gateway.
  • Key threshold: Alerts should trigger when requests exceed 10 per minute or 100 per hour.

Pattern Recognition

This involves analyzing number sequences and behavioral metadata.

  • Why it matters: Fraudsters often purchase blocks of sequential numbers. If your system detects a sudden influx of users with numbers ending in 0001, 0002, 0003, it’s a strong indicator of a coordinated fake account registration campaign.
  • Strategy: Compare the "new user" numbers against known fraud number ranges typically associated with virtual providers.

Blacklist Management

Maintaining a dynamic list of compromised or malicious numbers.

  • Why it matters: Once a number is identified in a fraud scheme, it should be blocked globally within your system.
  • Process: Real-time integration with databases like Spamhaus or internal historical logs ensures that known bad actors are blocked before they even reach your processing logic.

Implementation Example


// Fraud detection integration with carrier intelligence
class FraudDetectionService {
  private riskThresholds = {
    velocity: 10, // requests per minute
    pattern: 0.8, // pattern similarity score
    blacklist: true, // any blacklist match
    roaming: 0.7 // roaming risk score
  }

  async assessFraudRisk(phoneNumber: string, context: TransactionContext): Promise<FraudRiskAssessment> {
    const [carrierInfo, velocityData, patternAnalysis] = await Promise.all([
      this.getCarrierIntelligence(phoneNumber),
      this.analyzeVelocity(phoneNumber, context),
      this.analyzePattern(phoneNumber)
    ])

    const riskFactors = []
    let riskScore = 0

    // Velocity check
    if (velocityData.requestsPerMinute > this.riskThresholds.velocity) {
      riskFactors.push('High velocity detected')
      riskScore += 30
    }

    // Pattern analysis
    if (patternAnalysis.similarityScore > this.riskThresholds.pattern) {
      riskFactors.push('Suspicious pattern detected')
      riskScore += 25
    }

    // Blacklist check
    if (await this.isBlacklisted(phoneNumber)) {
      riskFactors.push('Number is blacklisted')
      riskScore += 50
    }

    // Roaming analysis
    if (carrierInfo.isRoaming && carrierInfo.roamingRiskScore > this.riskThresholds.roaming) {
      riskFactors.push('High-risk roaming detected')
      riskScore += 20
    }

    return {
      phoneNumber,
      riskScore: Math.min(riskScore, 100),
      riskLevel: this.calculateRiskLevel(riskScore),
      riskFactors,
      recommendation: this.generateRecommendation(riskScore, riskFactors),
      requiresReview: riskScore > 70,
      blocked: riskScore > 90
    }
  }
}

Performance Optimization Strategies


Optimizing carrier intelligence systems requires careful attention to caching, rate limiting, and resource management to ensure that security checks don't become a bottleneck for legitimate users.


Caching Strategies


Multi-Level Caching

  • L1 (Redis): Short-term in-memory cache (1-5 mins) to handle immediate retries or "burst" logins from the same device.
  • L2 (Database): Long-term cache (1-24 hours) for number characteristics that rarely change frequently, like carrier name or technology support.
  • L3 (CDN): Regional caching to reduce latency for global applications.

Cache Invalidation

  • Time-based: Automatic TTLs (Time To Live) ensure data doesn't become stale.
  • Event-driven: Trigger a refresh if a user reports a change in their service or if a porting event is detected.

Rate Limiting


Effective rate limiting protects both your budget and your service availability.

  • Provider Limits: External HLR providers often charge per request and have strict caps. Implement internal queues to ensure you never exceed these limits.
  • User-Based Limits: Limit how many times a single IP or User ID can trigger a carrier lookup to prevent reconnaissance by bad actors.

Implementation

class RateLimiter {
  private limits = new Map<string, { count: number; resetTime: number }>()

  async checkLimit(provider: string, limit: number): Promise<boolean> {
    const now = Date.now()
    const current = this.limits.get(provider) || { count: 0, resetTime: now + 60000 }

    if (now > current.resetTime) {
      current.count = 0
      current.resetTime = now + 60000
    }

    if (current.count >= limit) {
      return false
    }

    current.count++
    this.limits.set(provider, current)
    return true
  }
}

Implementation Best Practices


Architecture Design


Microservices Approach

Decouple carrier intelligence from your core billing or auth logic. This allows you to scale the HLR lookup service independently when traffic spikes and ensures that a failure in the lookup provider doesn't take down your entire application.


Error Handling

  • Graceful degradation: If the HLR provider is down, fallback to basic regex validation and flag the number for later verification rather than blocking the user.
  • Exponential backoff: Use retries that increase in delay to avoid overwhelming third-party APIs during recovery.

Security Considerations


Data Protection

Mobile network data is sensitive. Ensure that IMSI and location data are encrypted. Never store this information in plain text or in logs that are accessible to the entire development team.


API Security

Secure your intelligence endpoints with OAuth2 and implement IP whitelisting for your server-to-server communications.


Monitoring and Alerting


Don't wait for users to complain about slow logins. Monitor:

  • P99 Latency: If lookups take >3 seconds, it will negatively impact sign-up conversion rates.
  • Cache Hit Ratio: If your hit ratio drops below 70%, your caching strategy may be too aggressive or your TTLs too short.

Conclusion


Carrier intelligence systems provide essential capabilities for modern applications requiring phone number validation and fraud detection. Success depends on:


  • Robust architecture with proper error handling and caching to maintain high availability.
  • Comprehensive fraud detection with multiple risk assessment layers that go beyond simple blacklists.
  • Performance optimization through intelligent caching and rate limiting to ensure a seamless user experience.
  • Security-first approach with strict data protection and access controls to comply with global privacy standards.

Implement carrier intelligence with our professional solutions, designed for enterprise-scale performance and reliability.

Tags:carrier-intelligencemobile-networkshlr-lookupsnetwork-data