IP Geolocation for Telecom & MSISDN — IP-vs-IMSI Roaming-Arbitrage, A2P SMS Grey-Route, SIM-Swap Defence, EECC + CALEA Routing

Why telecom / MSISDN is its own axis: every consumer mobile session, every A2P SMS submission, every eSIM-provisioning call, and every carrier-OAuth callback is a four-jurisdiction decision in <40 ms — IP-vs-IMSI MCC/MNC residency check (subscriber-home-network vs visiting-network detection + roaming-arbitrage block), A2P messaging trust + grey-route detection (operator-bypass via SS7/SMPP + Artificially-Inflated Traffic / AIT), SIM-swap + STIR/SHAKEN attestation defence (carrier-OAuth claim of new device IP vs prior session + A/B/C attestation cross-check), and EECC Directive 2018/1972 + ePrivacy + CALEA + national lawful-intercept jurisdiction routing. IP-layer signals are upstream of the HLR/HSS roaming decision, the SMSC/MSC store-and-forward routing, the eSIM SM-DP+ provisioning call, and the post-session GDPR Art. 6 + ePrivacy + national-telecom-regulator audit. A wrong country resolve at session-init either burns roaming-margin to wholesale-arbitrage (Pareto: top-10 grey-route operators leak ~$2-3B/yr per GSMA WAS 2024) or breaches lawful-intercept obligations (DE TKG §170, FR L.34-1, UK IPA 2016, US CALEA 47 USC §1001-1010).

The country an IP resolves to, the ASN-as-mobile-carrier vs ASN-as-MVNO vs ASN-as-hosting it sits on, and whether it’s a known datacenter, VPN, residential-proxy, eSIM-aggregator, or Tor exit are inputs to four separate telecom / MSISDN control surfaces:

  1. IP-vs-IMSI MCC/MNC residency check + roaming-arbitrage detection at session-init — every MVNO, eSIM-aggregator, and carrier-aggregator faces IP-vs-IMSI cross-validation: the IMSI MCC/MNC encodes the subscriber’s home network (e.g. 310 = US, 262 = DE, 204 = NL, 234 = UK, 404/405 = IN); the IP resolves the visiting network. Hard-fail-closed thresholds: IP-country ≠ IMSI-home-country AND ASN ≠ known-roaming-partner → flag as potential SIM-cloning / IMSI-spoofing (Truphone 2023 incident: ~50K cloned IMSIs at carrier-aggregator level, $40M wholesale-rate exposure); residential-proxy ASN at session-init from an “MVNO consumer” subscriber → reject (impossible topology, subscriber’s IMSI should never egress through Bright Data / Oxylabs / Smartproxy / IPRoyal); datacenter ASN (AWS / GCP / Azure / OVH / Hetzner / DO) on a consumer-mobile MSISDN → manual review (likely M2M/IoT misclassified into consumer-tariff, ~€0.50/MB margin leak per misclassification). Roaming-arbitrage: home-routing arbitrage exploits the price-gap between EU-MTR €0.0019/min wholesale and US-Mexico $0.10/min consumer; IP-resolve at session-init lets the home-network operator detect “consumer plan B from US, visiting EU, but IP egresses NL while IMSI MCC 310” and choose home-routing vs local-breakout per GSMA BA.27 / IR.34 IPX recommendations.
  2. A2P SMS messaging trust + grey-route + AIT detection at message-submission — every A2P SMS submission to Twilio, Sinch, MessageBird/Bird, Vonage, Infobip, or directly via SMPP to an MNO is a per-country trust + grey-route + AIT decision. Grey-route = operator-bypass: aggregator injects via SS7 / SMPP into a third-country MNO that has unfiltered international peering, then onward-routes to destination — bypasses destination MNO’s A2P termination fee (~$0.005 → $0.05/SMS gap = ~$45/1K margin loss per AIB Report 2024). Hard-fail-closed: submission origin IP in hosting ASN (AWS/GCP/Azure/OVH) AND destination country has known grey-route history (BD/PK/NG/PH/ID) → 24h delayed-delivery + AIT-score eval. Artificially-Inflated Traffic: hosting-ASN sender + bulk-OTP destination + premium-rate-number prefix → reject + Telesign/Twilio fraud-feed escalation (Mobile Identity 2024: AIT losses ~$2.5B/yr industry-wide). OTP-flooding defence: same destination MSISDN receiving >5 OTPs in 60 sec from VPN/residential-proxy origin → rate-limit + carrier-routing-override (cf. Apple iCloud 2024 SMS-bombing: ~$70M opex burn before Sinch + Telesign IP-geo-feed integration). Origin-country to A2P sender-ID registration: BR ANATEL requires CNPJ-anchored sender-IDs for inbound A2P; IN TRAI DLT registration; DE BNetzA Sender-ID-Whitelist — all require origin-IP-country ≡ sender-ID-registration-country evidence in the submission envelope.
  3. SIM-swap + STIR/SHAKEN attestation + carrier-OAuth defence at authentication callback — every Number Verification API call (Twilio Verify, Sinch Verification, Vonage SilentAuth, TMo Open Gateway / GSMA Open Gateway camara.org), every STIR/SHAKEN attestation, and every carrier-OAuth (TMo Connect, AT&T Mobile Identity, Verizon Mobile-First) ingests IP-country + ASN as a SIM-swap + ATO signal. Hard-fail-closed: carrier-OAuth callback IP-country ≠ MSISDN-home-country AND time-since-last-session < 24h → SIM-swap-suspect-flag (FBI IC3 2024: $48M SIM-swap fraud losses, 1,075 complaints; T-Mobile 2023 class-action: $350M settlement for SIM-swap data exposure). STIR/SHAKEN attestation cross-check: inbound caller claims Attestation Level A (carrier vouches for caller + number) but IP-of-originating-SBC ASN is in different country than claimed-CLI country → downgrade to B or C + tag for analytics (FCC TRACED Act + RobocallRules.com 2024: ~70% of robocalls now Attestation C or “no attestation” — IP-geo provides the override signal). eSIM provisioning: SM-DP+ activation call must originate from billing-country (anti-eSIM-fraud per GSMA SGP.32 IoT Remote SIM Provisioning); hosting-ASN at eSIM-activation → reject (consumer eSIM should never provision from AWS/GCP). VoIP/CLI spoofing detection: ASN-classified-as-VoIP-provider (Bandwidth.com / Telnyx / Voxbone / Zenlayer / inteliquent) on inbound CLI ≠ MSISDN-country-prefix → tag + cross-feed to STIR/SHAKEN attestation-rating pipeline.
  4. EECC + ePrivacy + CALEA + national lawful-intercept jurisdiction routing at data-plane termination — every telecom session is subject to layered jurisdiction obligations: EU EECC Directive 2018/1972 (transposed by 2020-12-21 into national law: DE TKG, FR CPCE, NL Tw, IT CCE), ePrivacy Directive 2002/58/EC, EU Data Retention (post-Tele2/Digital Rights Ireland: case-by-case lawful retention for serious crime), DE TKG §170 + BNetzA Tk-Üv (telecommunications-surveillance ordinance), FR Code des P&CE L.34-1 + ARCEP, UK IPA 2016 + Ofcom, US CALEA 47 USC §1001-1010 + FCC E911 NextGen (sub-50-meter dispatchable location), India DoT Section 5(2) Indian Telegraph Act + CMS (Central Monitoring System), China MIIT + Cybersecurity Law Art. 37 (telecom data must reside on-shore), Brazil Marco Civil + ANATEL. IP-resolve at session-init routes the call-detail-record (CDR) + signalling-trace + content-tap to the correct national LI gateway: EU-subscriber session → EU-FRA / EU-AMS / DE-NUE LI-mediation node (TKG §170 compliant), US-subscriber → US-IAD CALEA mediation, CN-subscriber → CN-shard MIIT-compliant, IN-subscriber → IN-shard DoT-compliant. VPN/proxy/Tor → fall back to highest-protection EU-FRA shard + flag for manual LI-eval. 5G SUCI/SUPI privacy: subscriber concealed-identifier (SUCI) at attach must match home-PLMN per 3GPP TS 33.501; mismatched SUPI decryption-domain vs IP-geo → potential IMSI-catcher (Stingray) detection signal. Wrong call = €20M GDPR/€20M EECC fine + national-regulator licence-suspension (BNetzA 2024: €1.4M fine on Vodafone DE for inadequate LI; ARCEP 2023: €700K on Bouygues Telecom).

A single REST call to IP Geo API returns all four signal classes — country/region/subdivision/city/lat-lon, ASN + ASN-org-name (carrier vs MVNO vs hosting vs VoIP-provider classification), threat-flags (VPN/proxy/Tor/hosting/relay), risk score — on every plan, no add-on SKU, ≤40 ms median in EU.

What telecom / MSISDN buyers care about (in order)

  1. Session-init latency budget ≤ 40 ms. Every consumer mobile session-attach, every A2P SMS submission, every eSIM-provisioning call, and every carrier-OAuth callback must complete the IP-resolve inside the GSMA SG.27 / IR.34 IPX latency budget (≤ 50 ms aggregate per IPX-hop) and inside the STIR/SHAKEN attestation-rendering budget (≤ 100 ms before downstream SIP INVITE forwarded). IP Geo API runs on EU edges (Hetzner Frankfurt) for ≤ 30-40 ms median across DE/NL/FR/IE/ES/IT/UK, ≤ 60 ms US-EU round-trip — well under the session-attach budget and ahead of any HLR/HSS roaming-decision cold-start (200-500 ms for new-IMSI provisioning on Oracle/Mavenir/Affirmed core).
  2. EECC + ePrivacy + CALEA + national-LI audit-trail bundle. EECC Art. 41 + national transposition (DE TKG §170, FR L.34-1, UK IPA 2016 Part 4, IT CCE Art. 96 quater, NL Tw 13.1) require per-session lawful-intercept-readiness evidence; CALEA 47 CFR §1.20003 + FCC E911 NextGen require sub-50-m dispatchable-location records for IP-originating sessions; China MIIT + India DoT require on-shore CDR retention 12-24 months. IP Geo API ships a deterministic-replay log-format on every paid tier — same country_code + subdivision_iso_code + asn + is_vpn/is_proxy + risk_score at lookup-time gets persisted in the response envelope for audit-grade reconstruction by your LI compliance team + national-regulator inspections.
  3. Threat fields on every plan, not a paid add-on. Most US incumbents (MaxMind, ipinfo.io, ipstack) split datacenter/VPN/proxy classification into a paid Security Module or Privacy add-on — a problem for telecom where 100% of A2P fraud (AIT, OTP-flooding, grey-route) traces to hosting/VPN ASN egress. IP Geo API ships is_vpn, is_proxy, is_tor, is_hosting, is_relay, and a numeric risk_score on the free tier — critical for AIT defence, eSIM-provisioning origin-validation, SIM-swap-callback ASN-classification, and grey-route detection at SMPP/SS7 ingress.
  4. ASN-org-name + ASN-classification granularity for carrier vs MVNO vs hosting vs VoIP-provider routing. Country-only checks miss the carrier-vs-MVNO-vs-hosting-vs-VoIP distinction critical to telecom trust decisions. A consumer-mobile IMSI session egressing through an MVNO ASN (Lyca AS33941, Lebara AS28876, TracFone AS22394) is legitimate; the same MSISDN egressing through AWS AS16509 or DigitalOcean AS14061 is M2M misclassified or fraud. We expose asn + asn_org_name + asn_type (isp / business / hosting / mobile / cdn) on every paid tier — exactly what carrier-aggregator fraud-scoring engines (Twilio Lookup v2, Sinch Number Lookup, Telesign PhoneID) consume.
  5. EU residency + GDPR + ePrivacy + EECC posture. Subscriber IPs cannot be transferred to a US vendor without GDPR §28 DPA + SCCs + TIA (Schrems II); EECC Art. 100 requires national-regulator approval for cross-border subscriber-data transfers in some MS; ePrivacy Directive 2002/58/EC requires confidentiality of communications-metadata. Cookieless IP-resolve preferred over cookie-based tracking under ePrivacy + EU CJEU Planet49 (no consent required for IP-resolve as legitimate-interest §6(1)(f) on fraud-prevention + security + LI-compliance basis). The entire chain (IP-resolve, IMSI cross-check, A2P routing, eSIM-provisioning gate, LI-mediation route) must be EU-data-residency end-to-end for EU-subscriber sessions. IP Geo API is EU-only data-flow, signed DPA in 24h, no SCCs required.

The four telecom / MSISDN control surfaces, in code

1. IP-vs-IMSI MCC/MNC residency check + roaming-arbitrage detection (hard-fail-closed)

// /api/session/attach.js — Node 20 / session-init, ≤40 ms hard budget on IP-resolve
// IMSI MCC/MNC → home-country; IP → visiting-country; ASN → carrier/MVNO/hosting class.
const fetch = require('undici').fetch;

// MCC → ISO country code (subset; full table at ITU-T E.212 + 3GPP TS 23.122)
const MCC_TO_COUNTRY = {
  '262':'DE','310':'US','311':'US','312':'US','313':'US','316':'US','204':'NL','234':'UK','235':'UK',
  '208':'FR','214':'ES','222':'IT','238':'DK','240':'SE','244':'FI','272':'IE','219':'HR','231':'SK',
  '230':'CZ','260':'PL','268':'PT','216':'HU','260':'PL','404':'IN','405':'IN','406':'IN','410':'PK',
  '413':'LK','414':'MM','450':'KR','440':'JP','441':'JP','460':'CN','461':'CN','525':'SG','520':'TH',
  '510':'ID','515':'PH','502':'MY','334':'MX','724':'BR','730':'CL','732':'CO','722':'AR','764':'AU'
};

// GSMA-listed roaming-partner ASN allowlist (carrier-class IPX peers)
const ROAMING_PARTNER_ASN = new Set([
  '15169','3320','3215','3209','12876','2856','5089','12389','5400','6453','174','209'
]);

// Sanctioned home-PLMN: IMSI MCC of these → hard-stop on roaming-attach (per OFAC + EU CONSILIUM)
const SANCTIONED_MCC = new Set(['432','434','410','412','420','417','419','413','428','438']);

async function attachGate(req, imsi) {
  const ip = req.headers['cf-connecting-ip'] || req.ip;
  const mcc = imsi.substring(0,3);
  const homeCountry = MCC_TO_COUNTRY[mcc];

  let geo;
  try {
    geo = await (await fetch(`https://ipgeo.10b.app/v1/${ip}?fields=country_code,subdivision_iso_code,asn,asn_org_name,asn_type,is_vpn,is_proxy,is_tor,is_hosting,risk_score`, {
      headers: { 'Authorization': `Bearer ${process.env.IPGEO_KEY}` },
      signal: AbortSignal.timeout(40)
    })).json();
  } catch {
    return { gate: 'manual_review_required', reason: 'geo_timeout_no_auto_attach' };
  }

  // Sanctioned home-PLMN → hard-stop
  if (SANCTIONED_MCC.has(mcc)) return { gate: 'sanctions_hardstop', status: 451 };

  // Residential-proxy or Tor at consumer-mobile attach → impossible topology
  if (geo.is_proxy || geo.is_tor) return { gate: 'reject_impossible_origin', reason: 'consumer_mobile_via_proxy_or_tor' };

  // Hosting ASN on consumer MSISDN → manual M2M classification review
  if (geo.is_hosting && geo.asn_type === 'hosting') {
    return { gate: 'manual_review_m2m_misclass', reason: `hosting_asn_${geo.asn}_${geo.asn_org_name}` };
  }

  // IP-country ≠ home-country: legitimate roaming OR arbitrage attempt — discriminate by ASN
  if (geo.country_code !== homeCountry) {
    if (!ROAMING_PARTNER_ASN.has(geo.asn)) {
      // Visiting-country ASN not on GSMA roaming-partner list → suspect SIM-cloning / IMSI-spoofing
      return { gate: 'sim_clone_suspect', reason: `imsi_home_${homeCountry}_ip_${geo.country_code}_asn_${geo.asn}_not_roaming_partner`, status: 'flag_only' };
    }
    return { gate: 'allow_roaming', billing_mode: 'roaming_partner_rate', evidence: { home: homeCountry, visiting: geo.country_code, asn: geo.asn } };
  }

  return { gate: 'allow_home_network', billing_mode: 'home_rate', evidence: { home: homeCountry, visiting: geo.country_code, asn: geo.asn, asn_org: geo.asn_org_name } };
}

module.exports = { attachGate };

2. A2P SMS grey-route + AIT detection at message-submission

# /api/messaging/submit.py — FastAPI, ≤40 ms IP-resolve budget on SMPP/HTTP submit
# Origin IP → ASN classification + threat fields → grey-route + AIT score
import os, httpx
from fastapi import HTTPException

# Known grey-route-heavy destination countries (AIB Report 2024 + Mobile Identity 2024)
GREY_ROUTE_DESTINATIONS = {'BD','PK','NG','PH','ID','VN','EG','MA','DZ','TN','KE','TZ','UG','GH'}

# Premium-rate prefixes that trigger AIT-score boost (E.164 country + range)
PREMIUM_RATE_PREFIXES = ['+359', '+371', '+254', '+27', '+91-1800']  # selected high-AIT-fraud ranges

# Hosting ASN classification triggers stricter AIT eval
HOSTING_ASN_TYPES = {'hosting', 'business'}

async def submit_gate(origin_ip: str, destination_msisdn: str, sender_id: str):
    async with httpx.AsyncClient(timeout=0.04) as client:
        try:
            r = await client.get(
                f"https://ipgeo.10b.app/v1/{origin_ip}",
                params={'fields': 'country_code,asn,asn_org_name,asn_type,is_vpn,is_proxy,is_tor,is_hosting,risk_score'},
                headers={'Authorization': f"Bearer {os.environ['IPGEO_KEY']}"}
            )
            geo = r.json()
        except Exception:
            raise HTTPException(503, 'ip_geo_timeout_no_auto_route')

    dest_country = destination_msisdn[:3].lstrip('+')  # crude E.164 extraction — replace with libphonenumber in prod
    score = 0
    reasons = []

    # Tor / residential-proxy origin → reject outright (no legitimate A2P)
    if geo.get('is_tor') or geo.get('is_proxy'):
        raise HTTPException(403, f"a2p_reject_anon_origin_{geo.get('asn')}")

    # Hosting/VPN origin → AIT eval
    if geo.get('is_hosting') or geo.get('is_vpn'):
        score += 30
        reasons.append('hosting_or_vpn_origin')

    # Grey-route-heavy destination + hosting origin → high AIT risk
    if dest_country in GREY_ROUTE_DESTINATIONS and (geo.get('is_hosting') or geo.get('asn_type') in HOSTING_ASN_TYPES):
        score += 40
        reasons.append('hosting_origin_grey_route_destination')

    # Premium-rate destination prefix → AIT-score boost
    if any(destination_msisdn.startswith(p) for p in PREMIUM_RATE_PREFIXES):
        score += 25
        reasons.append('premium_rate_destination_prefix')

    # Sender-ID country mismatch (e.g. BR ANATEL CNPJ-anchored, IN TRAI DLT, DE BNetzA whitelist)
    sender_country = sender_id_to_country(sender_id)  # impl: lookup against your registered-sender DB
    if sender_country and sender_country != geo.get('country_code'):
        score += 15
        reasons.append(f"sender_id_country_{sender_country}_ip_country_{geo.get('country_code')}")

    if score >= 70:
        raise HTTPException(403, f"ait_reject_score_{score}_{'_'.join(reasons)}")
    if score >= 40:
        return {'route': 'delayed_24h_ait_eval', 'score': score, 'reasons': reasons}

    return {'route': 'normal_a2p_termination', 'score': score, 'reasons': reasons, 'evidence': geo}

3. SIM-swap + STIR/SHAKEN + carrier-OAuth defence

// /api/auth/carrier-oauth-callback.js — invoked on carrier-OAuth verification + STIR/SHAKEN ingest
// SIM-swap signal = IP-country flip + ASN flip + short time-since-last-session
const fetch = require('undici').fetch;
const db = require('./db');

// VoIP-provider ASN allowlist (Bandwidth, Telnyx, Voxbone, Zenlayer, inteliquent)
const VOIP_PROVIDER_ASN = new Set(['397423','55222','12779','21859','3303','46887','46562','11151']);

async function carrierOAuthCallback({ msisdn, claimed_attestation, ip, prior_session_id }) {
  let geo;
  try {
    geo = await (await fetch(`https://ipgeo.10b.app/v1/${ip}?fields=country_code,asn,asn_org_name,asn_type,is_vpn,is_proxy,is_tor,is_hosting,is_relay,risk_score`, {
      headers: { 'Authorization': `Bearer ${process.env.IPGEO_KEY}` },
      signal: AbortSignal.timeout(40)
    })).json();
  } catch {
    return { decision: 'manual_review', reason: 'geo_timeout' };
  }

  const msisdnHomeCountry = msisdn.startsWith('+1') ? 'US'
    : msisdn.startsWith('+44') ? 'UK'
    : msisdn.startsWith('+49') ? 'DE'
    : msisdn.startsWith('+33') ? 'FR'
    : msisdn.startsWith('+31') ? 'NL'
    : msisdn.startsWith('+91') ? 'IN'
    : null;

  // STIR/SHAKEN attestation downgrade: carrier claims A but originating IP is VoIP-provider in different country
  if (claimed_attestation === 'A' && VOIP_PROVIDER_ASN.has(geo.asn) && geo.country_code !== msisdnHomeCountry) {
    return { decision: 'downgrade_to_C', evidence: { claimed: 'A', actual_asn: geo.asn, asn_org: geo.asn_org_name } };
  }

  // SIM-swap suspect: prior-session IP country ≠ current callback IP country AND time-delta < 24h
  const prior = await db.priorSession(prior_session_id);
  if (prior && prior.country_code && prior.country_code !== geo.country_code) {
    const hoursSince = (Date.now() - prior.timestamp) / 3600000;
    if (hoursSince < 24) {
      return { decision: 'sim_swap_suspect', reason: `prior_${prior.country_code}_now_${geo.country_code}_delta_${hoursSince.toFixed(1)}h`, action: 'require_step_up_kba' };
    }
  }

  // eSIM provisioning from hosting ASN → reject (consumer eSIM should never originate from AWS/GCP)
  if (geo.is_hosting) {
    return { decision: 'esim_reject_hosting_origin', reason: `hosting_asn_${geo.asn}_${geo.asn_org_name}` };
  }

  return { decision: 'allow', evidence: geo };
}

module.exports = { carrierOAuthCallback };

4. EECC + CALEA + national-LI jurisdiction routing at data-plane termination

# /api/li-mediation/route.py — routes CDR + signalling-trace + content-tap to correct national LI gateway
# Per-jurisdiction shard map; VPN/proxy/Tor → highest-protection fallback
import os, httpx

LI_SHARD_MAP = {
    'DE': {'shard': 'eu-fra-li-tkg', 'regulator': 'BNetzA', 'statute': 'TKG §170'},
    'FR': {'shard': 'eu-par-li-arcep', 'regulator': 'ARCEP', 'statute': 'CPCE L.34-1'},
    'NL': {'shard': 'eu-ams-li-acm', 'regulator': 'ACM', 'statute': 'Tw 13.1'},
    'IT': {'shard': 'eu-mil-li-mise', 'regulator': 'MISE', 'statute': 'CCE Art. 96 quater'},
    'UK': {'shard': 'uk-lon-li-ipa', 'regulator': 'Ofcom + UKHO', 'statute': 'IPA 2016 Part 4'},
    'US': {'shard': 'us-iad-li-calea', 'regulator': 'FCC + DOJ', 'statute': 'CALEA 47 USC §1001-1010 + 47 CFR §1.20003'},
    'CN': {'shard': 'cn-pek-li-miit', 'regulator': 'MIIT + CAC', 'statute': 'Cybersecurity Law Art. 37 + Telecom Reg.'},
    'IN': {'shard': 'in-bom-li-dot', 'regulator': 'DoT + CMS', 'statute': 'Indian Telegraph Act §5(2)'},
    'BR': {'shard': 'br-sao-li-anatel', 'regulator': 'ANATEL', 'statute': 'Marco Civil + LGPD'},
    'AU': {'shard': 'au-syd-li-tcia', 'regulator': 'TCIA', 'statute': 'TIA Act 1979'},
}
FALLBACK_SHARD = LI_SHARD_MAP['DE']  # highest GDPR + EECC protection

async def route_li_session(ip: str, msisdn: str):
    async with httpx.AsyncClient(timeout=0.04) as client:
        try:
            r = await client.get(
                f"https://ipgeo.10b.app/v1/{ip}",
                params={'fields': 'country_code,subdivision_iso_code,is_vpn,is_proxy,is_tor,risk_score'},
                headers={'Authorization': f"Bearer {os.environ['IPGEO_KEY']}"}
            )
            geo = r.json()
        except Exception:
            return {'shard': FALLBACK_SHARD['shard'], 'reason': 'geo_timeout_highest_protection_fallback'}

    # VPN / proxy / Tor → highest-protection fallback + manual LI-eval flag
    if geo.get('is_vpn') or geo.get('is_proxy') or geo.get('is_tor'):
        return {'shard': FALLBACK_SHARD['shard'], 'reason': 'anonymised_origin_li_manual_eval', 'flag': 'manual_li_review'}

    cc = geo.get('country_code')
    target = LI_SHARD_MAP.get(cc, FALLBACK_SHARD)

    return {
        'shard': target['shard'],
        'regulator': target['regulator'],
        'statute': target['statute'],
        'evidence': {
            'ip_country': cc,
            'subdivision': geo.get('subdivision_iso_code'),
            'msisdn_country_prefix': msisdn[:3],
            'audit_log_retention_required': True,
        }
    }

Why the IP Geo API is purpose-built for telecom / MSISDN

How to migrate from your incumbent — drop-in code-level guides

Already on an incumbent? These step-by-step migration guides ship with field-by-field maps, code diffs, shadow-mode validation, and rollback notes:

Industry deep-dives

Other vertical-specific surfaces using the same IP Geo API primitives:


Get started — telecom / MSISDN procurement

Sign up at https://ipgeo.10b.app/pricing and start with a sandbox key today.


Get early access — 50% off for 12 months

First 100 signups lock in 50% off any paid plan for the first year. No credit card required — we’ll email you at launch.