Skip to main content

SIM Swap & IMSI Detection

Detect recent SIM swap events and verify subscriber IMSI status across Safaricom V1, V2, and V3 endpoints to prevent account takeover and transaction fraud.

Environment Variables Setup

Configure your consumer key, secret, and optional test phone numbers in your .env file:

.env
# Safaricom Daraja Credentials (Must have IMSI/Swap product enabled)
MPESA_CONSUMER_KEY=your_consumer_key_here
MPESA_CONSUMER_SECRET=your_consumer_secret_here
# M-Pesa Environment: "sandbox" or "production"
MPESA_ENVIRONMENT=sandbox
# Optional: Default target subscriber for testing
TEST_CUSTOMER_NUMBER=0722000000

Overview & Architecture

The IMSIService and AsyncIMSIService classes manage queries against Safaricom's IMSI endpoints (V1, V2, and V3) via client.imsi.

  • Automatic MSISDN Normalization: Automatically normalizes local formats (e.g., 0722... or +254722...) into standard 254722... format.
  • Smart Fraud Detection: Exposes response.is_recently_swapped on V1 and V3 responses to instantly identify swapped SIMs compared to baseline timestamps (01-01-1900 00:00).
  • Unified Client Support: Seamlessly exposed via MpesaClient.imsi (Synchronous) and AsyncMpesaClient.imsi (Asynchronous).

Service Methods & API Endpoints

MethodEndpoint PathDescription
query_v1(customer_number)/imsi/v1/checkATIQueries basic SIM swap status and last swap timestamp.
query_v2(customer_number)/imsi/v2/checkATIRetrieves registration date details and raw check status.
query_v3(customer_number)/imsi/v3/checkATIQueries hashed IMSI data alongside recent swap indicators.

Synchronous Usage Example

MpesaClient (Synchronous IMSI Check)
import os
from dotenv import load_dotenv
from mpesakit import MpesaClient
load_dotenv()
client = MpesaClient(
consumer_key=os.getenv("MPESA_CONSUMER_KEY"),
consumer_secret=os.getenv("MPESA_CONSUMER_SECRET"),
environment=os.getenv("MPESA_ENVIRONMENT", "sandbox"),
)
customer = os.getenv("TEST_CUSTOMER_NUMBER", "0722000000")
# 1. Query V1 - Basic SIM Swap Check
v1_res = client.imsi.query_v1(customer_number=customer)
if v1_res.is_recently_swapped:
print(f"FRAUD WARNING (V1): SIM swapped on {v1_res.lastSwapDate}")
else:
print("SAFE (V1): No recent SIM swap detected")
# 2. Query V2 - Registration Details
v2_res = client.imsi.query_v2(customer_number=customer)
print(f"V2 Reg Date: {v2_res.msisdnRegistrationDate}")
# 3. Query V3 - Hashed IMSI & Swap Status
v3_res = client.imsi.query_v3(customer_number=customer)
print(f"V3 Hashed IMSI: {v3_res.imsi}")
print(f"V3 Recently Swapped: {v3_res.is_recently_swapped}")

Asynchronous Usage Example

AsyncMpesaClient (Asynchronous IMSI Check)
import os
import asyncio
from dotenv import load_dotenv
from mpesakit import AsyncMpesaClient
load_dotenv()
async def check_imsi_services():
async with AsyncMpesaClient(
consumer_key=os.getenv("MPESA_CONSUMER_KEY"),
consumer_secret=os.getenv("MPESA_CONSUMER_SECRET"),
environment=os.getenv("MPESA_ENVIRONMENT", "sandbox"),
) as client:
customer = os.getenv("TEST_CUSTOMER_NUMBER", "0722000000")
# Execute requests concurrently
v1_res, v2_res, v3_res = await asyncio.gather(
client.imsi.query_v1(customer_number=customer),
client.imsi.query_v2(customer_number=customer),
client.imsi.query_v3(customer_number=customer),
)
print(f"V1 Swap Date: {v1_res.lastSwapDate}")
print(f"V2 Reg Date: {v2_res.msisdnRegistrationDate}")
print(f"V3 Hashed IMSI: {v3_res.imsi}")
asyncio.run(check_imsi_services())

Request Parameters Definition

ParameterTypeDescription
customer_numberrequired
str
StringTarget subscriber phone number (e.g. '0722000000', '254722000000', or '+254722000000'). Automatically normalized.

Response Models Breakdown

IMSIV1Response​

ParameterTypeDescription
requestRefIDrequired
str
StringUnique query reference ID generated by Safaricom.
responseCoderequired
str
StringAPI return status code ('200' for success).
responseDescrequired
str
StringStatus message description (e.g. 'Success').
lastSwapDaterequired
str
StringTimestamp of last swap ('DD-MM-YYYY HH:MM'). Default baseline is '01-01-1900 00:00'.
msisdnRegistrationDate
str
StringTimestamp when subscriber MSISDN was registered with Safaricom.
imsirequired
str
StringHashed or encrypted IMSI identifier for the subscriber line.
customer_numberrequired
str
StringTarget subscriber phone number (e.g. '0722000000', '254722000000', or '+254722000000'). Automatically normalized.
is_recently_swapped
bool (property)
BooleanEvaluates to True if lastSwapDate is strictly greater than '01-01-1900 00:00'.

IMSIV2Response​

ParameterTypeDescription
requestRefIDrequired
str
StringUnique query reference ID generated by Safaricom.
responseCoderequired
str
StringAPI return status code.
msisdnRegistrationDate
str
StringTimestamp when subscriber MSISDN was registered with Safaricom.
customer_numberrequired
str
StringTarget subscriber phone number (e.g. '0722000000', '254722000000', or '+254722000000'). Automatically normalized.

IMSIV3Response​

ParameterTypeDescription
requestRefIDrequired
str
StringUnique query reference ID generated by Safaricom.
imsirequired
str
StringHashed or encrypted IMSI identifier for the subscriber line.
responseCoderequired
str
StringAPI return status code ('200' for success).
responseDescrequired
str
StringStatus message description (e.g. 'Success').
customer_numberrequired
str
StringTarget subscriber phone number (e.g. '0722000000', '254722000000', or '+254722000000'). Automatically normalized.