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.
⚠️Paid API & Mandatory Onboarding
Unlike standard M-Pesa APIs (such as STK Push or C2B), the IMSI CheckATI / SIM Swap APIs are paid services on the Safaricom Developer Portal (Daraja).
Requirements before integration:
- You must request access and complete commercial onboarding with Safaricom to enable the IMSI product family on your App credentials.
- In production, calls to V1, V2, and V3 endpoints consume API credits or billed usage per query according to your Safaricom tariff plan.
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_hereMPESA_CONSUMER_SECRET=your_consumer_secret_here
# M-Pesa Environment: "sandbox" or "production"MPESA_ENVIRONMENT=sandbox
# Optional: Default target subscriber for testingTEST_CUSTOMER_NUMBER=0722000000Overview & 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 standard254722...format. - Smart Fraud Detection: Exposes
response.is_recently_swappedon 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) andAsyncMpesaClient.imsi(Asynchronous).
Service Methods & API Endpoints
| Method | Endpoint Path | Description |
|---|---|---|
query_v1(customer_number) | /imsi/v1/checkATI | Queries basic SIM swap status and last swap timestamp. |
query_v2(customer_number) | /imsi/v2/checkATI | Retrieves registration date details and raw check status. |
query_v3(customer_number) | /imsi/v3/checkATI | Queries hashed IMSI data alongside recent swap indicators. |
Synchronous Usage Example
MpesaClient (Synchronous IMSI Check)
import osfrom dotenv import load_dotenvfrom 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 Checkv1_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 Detailsv2_res = client.imsi.query_v2(customer_number=customer)print(f"V2 Reg Date: {v2_res.msisdnRegistrationDate}")
# 3. Query V3 - Hashed IMSI & Swap Statusv3_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 osimport asynciofrom dotenv import load_dotenvfrom 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
| Parameter | Type | Description |
|---|---|---|
customer_numberrequired str | String | Target subscriber phone number (e.g. '0722000000', '254722000000', or '+254722000000'). Automatically normalized. |
Response Models Breakdown
IMSIV1Response
| Parameter | Type | Description |
|---|---|---|
requestRefIDrequired str | String | Unique query reference ID generated by Safaricom. |
responseCoderequired str | String | API return status code ('200' for success). |
responseDescrequired str | String | Status message description (e.g. 'Success'). |
lastSwapDaterequired str | String | Timestamp of last swap ('DD-MM-YYYY HH:MM'). Default baseline is '01-01-1900 00:00'. |
msisdnRegistrationDate str | String | Timestamp when subscriber MSISDN was registered with Safaricom. |
imsirequired str | String | Hashed or encrypted IMSI identifier for the subscriber line. |
customer_numberrequired str | String | Target subscriber phone number (e.g. '0722000000', '254722000000', or '+254722000000'). Automatically normalized. |
is_recently_swapped bool (property) | Boolean | Evaluates to True if lastSwapDate is strictly greater than '01-01-1900 00:00'. |
IMSIV2Response
| Parameter | Type | Description |
|---|---|---|
requestRefIDrequired str | String | Unique query reference ID generated by Safaricom. |
responseCoderequired str | String | API return status code. |
msisdnRegistrationDate str | String | Timestamp when subscriber MSISDN was registered with Safaricom. |
customer_numberrequired str | String | Target subscriber phone number (e.g. '0722000000', '254722000000', or '+254722000000'). Automatically normalized. |
IMSIV3Response
| Parameter | Type | Description |
|---|---|---|
requestRefIDrequired str | String | Unique query reference ID generated by Safaricom. |
imsirequired str | String | Hashed or encrypted IMSI identifier for the subscriber line. |
responseCoderequired str | String | API return status code ('200' for success). |
responseDescrequired str | String | Status message description (e.g. 'Success'). |
customer_numberrequired str | String | Target subscriber phone number (e.g. '0722000000', '254722000000', or '+254722000000'). Automatically normalized. |
💡Endpoints Summary
- Sandbox Base:
https://sandbox.safaricom.co.ke/imsi/ - Production Base:
https://api.safaricom.co.ke/imsi/ - Paths:
/v1/checkATI,/v2/checkATI,/v3/checkATI
Related Documentation
- Auth & Token Management - Managing credentials and OAuth tokens
- Getting Credentials - How to set up Daraja portal apps
- Production Checklist - Going live on Safaricom M-Pesa APIs