Skip to main content

USSD API

Build interactive mobile applications using USSD (Unstructured Supplementary Service Data) to reach users without internet connectivity. Perfect for financial services, surveys, mobile services, and menu-driven applications across Africa.

📱 Overview​

USSD enables real-time, session-based interactions with mobile users through simple menu interfaces. No internet or app installation required - works on any phone.

Key Features​

  • 🔄 Session Management - Maintain state across multi-step interactions
  • 📊 Menu Builder - Create dynamic, multilevel menus
  • 🌍 Pan-African Coverage - Available in 50+ African countries
  • ⚡ Real-time Response - Instant user interactions
  • 🔐 Secure Sessions - Encrypted session data
  • 📈 Analytics - Track usage, completion rates, and user flows

Use Cases​

  • Mobile Banking - Balance checks, transfers, bill payments
  • Surveys & Polls - Quick data collection and feedback
  • Service Menus - Customer support, account management
  • Agent Applications - Field agent data collection
  • Voting Systems - Secure voting and polling
  • Ticketing - Event tickets, transport bookings

🚀 Quick Start​

Send a USSD Menu​

POST https://api.afriroute.ai/api/v1/ussd/send
{
"phone": "+251911234567",
"shortcode": "*123*456#",
"message": "Welcome to AfriRoute!\n1. Check Balance\n2. Send Money\n3. Buy Airtime\n4. Help",
"session_id": "unique-session-id-12345"
}

Response​

{
"status": "success",
"message_id": "msg_7K8L9M0N1P2Q",
"session_id": "unique-session-id-12345",
"phone": "+251911234567",
"cost": 0.005,
"timestamp": "2024-03-15T10:30:00Z"
}

📡 API Endpoints​

1. Send USSD Menu​

Send a USSD menu to a user and initiate a session.

POST /v1/ussd/send

Request Body​

ParameterTypeRequiredDescription
phonestringYesRecipient phone number in E.164 format
shortcodestringYesUSSD shortcode (e.g., *123# or *123*456#)
messagestringYesMenu text to display (max 182 characters)
session_idstringYesUnique session identifier
dataobjectNoCustom session data to store
timeoutintegerNoSession timeout in seconds (default: 90)

Example Request​

const response = await fetch('https://api.afriroute.ai/api/v1/ussd/send', {
method: 'POST',
headers: {
'Authorization': 'Bearer $AFRIROUTE_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
phone: '+251911234567',
shortcode: '*123#',
message: 'Welcome to MyBank\n1. Balance\n2. Transfer\n3. Loans\n0. Exit',
session_id: crypto.randomUUID(),
data: {
user_id: 'user_12345',
language: 'en'
}
})
});
import requests

response = requests.post(
'https://api.afriroute.ai/api/v1/ussd/send',
headers={
'Authorization': 'Bearer $AFRIROUTE_API_KEY',
'Content-Type': 'application/json'
},
json={
'phone': '+251911234567',
'shortcode': '*123#',
'message': 'Welcome to MyBank\n1. Balance\n2. Transfer\n3. Loans\n0. Exit',
'session_id': 'unique-session-123',
'data': {
'user_id': 'user_12345',
'language': 'en'
}
}
)

2. Handle User Response​

Process user input and continue the USSD session.

POST /v1/ussd/continue

Request Body​

ParameterTypeRequiredDescription
session_idstringYesSession identifier from previous step
inputstringYesUser's input/selection
messagestringYesNext menu or response to show
end_sessionbooleanNoWhether to end the session (default: false)

Example: Multi-Step USSD Flow​

// Step 1: Initial Menu
const step1 = await sendUSSD({
phone: '+251911234567',
shortcode: '*123#',
message: 'Select Service:\n1. Balance\n2. Transfer\n3. Airtime',
session_id: 'session_abc123'
});

// Step 2: User selects "2" (Transfer)
const step2 = await continueUSSD({
session_id: 'session_abc123',
input: '2',
message: 'Enter recipient phone:'
});

// Step 3: User enters phone number
const step3 = await continueUSSD({
session_id: 'session_abc123',
input: '+251922334455',
message: 'Enter amount:'
});

// Step 4: User enters amount
const step4 = await continueUSSD({
session_id: 'session_abc123',
input: '100',
message: 'Confirm transfer of 100 ETB to +251922334455?\n1. Yes\n2. No'
});

// Step 5: User confirms
const step5 = await continueUSSD({
session_id: 'session_abc123',
input: '1',
message: 'Transfer successful! Your new balance is 450 ETB.',
end_session: true
});

3. Get Session Data​

Retrieve stored session data.

GET /v1/ussd/sessions/:session_id

Response​

{
"session_id": "session_abc123",
"phone": "+251911234567",
"shortcode": "*123#",
"status": "active",
"created_at": "2024-03-15T10:30:00Z",
"last_activity": "2024-03-15T10:32:15Z",
"expires_at": "2024-03-15T10:33:30Z",
"data": {
"user_id": "user_12345",
"current_step": "confirm_transfer",
"transfer_amount": 100,
"recipient": "+251922334455"
},
"history": [
{"step": 1, "input": null, "output": "Select Service..."},
{"step": 2, "input": "2", "output": "Enter recipient phone:"},
{"step": 3, "input": "+251922334455", "output": "Enter amount:"},
{"step": 4, "input": "100", "output": "Confirm transfer..."}
]
}

4. End Session​

Manually end an active USSD session.

POST /v1/ussd/sessions/:session_id/end

🔄 Session Management​

Session Lifecycle​

  1. Initiated - User dials shortcode, session created
  2. Active - User interacting with menus
  3. Expired - No activity for timeout period
  4. Completed - User exits or flow finishes

Session Storage​

Store custom data throughout the session:

// Store data during session
await continueUSSD({
session_id: 'session_123',
input: '2',
message: 'Enter PIN:',
data: {
selected_service: 'transfer',
step: 2,
metadata: {
start_time: Date.now()
}
}
});

// Retrieve later
const session = await getSession('session_123');
console.log(session.data.selected_service); // 'transfer'

Timeout Handling​

// Set custom timeout (default: 90 seconds)
await sendUSSD({
phone: '+251911234567',
shortcode: '*123#',
message: 'Welcome! This session expires in 2 minutes.',
session_id: 'session_123',
timeout: 120 // 2 minutes
});

// Handle timeout webhook
app.post('/ussd/timeout', (req, res) => {
const { session_id, data } = req.body;

// Clean up or save partial data
console.log(`Session ${session_id} timed out`);

res.json({ status: 'acknowledged' });
});

📋 Menu Design Best Practices​

1. Keep It Simple​

// ✅ Good - Clear and concise
const menu = `Welcome to MyService
1. Check Balance
2. Buy Airtime
3. Help
0. Exit`;

// ❌ Bad - Too much text
const badMenu = `Welcome to MyService. We provide various services including...`;

2. Use Consistent Navigation​

// Always use consistent patterns
const menuPattern = {
home: '0', // Return to main menu
back: '00', // Go back one step
exit: '000' // Exit session
};

3. Provide Clear Instructions​

const menu = `Enter phone number:
(e.g., 0911234567)

00. Back 0. Home`;

4. Handle Invalid Input​

function handleInput(input, session) {
if (!isValidSelection(input)) {
return {
message: 'Invalid selection. Please try again.\n1. Balance\n2. Transfer\n0. Exit',
data: session.data // Preserve session state
};
}

// Process valid input
return processSelection(input, session);
}

🌍 Country Coverage​

USSD is available in these countries:

CountryShortcode FormatOperators
🇪🇹 Ethiopia*123#, *123*4#Ethio Telecom
🇰🇪 Kenya*123#Safaricom, Airtel
🇹🇿 Tanzania*150*00#Vodacom, Tigo, Airtel
🇺🇬 Uganda*165#MTN, Airtel
🇳🇬 Nigeria*123#MTN, Glo, Airtel, 9mobile
🇬🇭 Ghana*170#MTN, Vodafone, AirtelTigo
🇿🇦 South Africa*120#All major operators

View full country list →


🔔 Webhooks​

Configure webhooks to receive real-time notifications:

User Response Webhook​

POST https://your-server.com/ussd/response
{
"event": "ussd.response",
"session_id": "session_123",
"phone": "+251911234567",
"input": "2",
"timestamp": "2024-03-15T10:32:15Z",
"data": {
"current_step": 2
}
}

Session Ended Webhook​

{
"event": "ussd.session_ended",
"session_id": "session_123",
"phone": "+251911234567",
"reason": "completed",
"duration": 45,
"total_steps": 5,
"timestamp": "2024-03-15T10:33:00Z"
}

💡 Advanced Examples​

Banking Application​

class USSDBank {
async handleMenu(sessionId, input) {
const session = await getSession(sessionId);

switch(session.data.step) {
case 'main':
return this.mainMenu();

case 'balance':
return this.checkBalance(session.data.accountId);

case 'transfer_phone':
return this.collectRecipient(input);

case 'transfer_amount':
return this.collectAmount(input);

case 'transfer_confirm':
return this.confirmTransfer(input, session);

default:
return this.mainMenu();
}
}

mainMenu() {
return {
message: 'MyBank USSD\n1. Balance\n2. Transfer\n3. Mini Statement\n4. Airtime\n0. Exit',
data: { step: 'main' }
};
}

async checkBalance(accountId) {
const balance = await getAccountBalance(accountId);
return {
message: `Your balance is: ${balance} ETB\n\n00. Main Menu\n0. Exit`,
end_session: true
};
}
}

Survey Application​

class USSDSurvey {
async handleSurvey(sessionId, input) {
const session = await getSession(sessionId);
const questions = this.getQuestions();
const currentQ = session.data.currentQuestion || 0;

// Save answer
if (input && currentQ > 0) {
await this.saveAnswer(sessionId, currentQ - 1, input);
}

// Check if survey complete
if (currentQ >= questions.length) {
return {
message: 'Thank you for completing our survey!',
end_session: true
};
}

// Show next question
const question = questions[currentQ];
return {
message: `Question ${currentQ + 1}/${questions.length}\n${question.text}\n${question.options}`,
data: {
currentQuestion: currentQ + 1
}
};
}

getQuestions() {
return [
{
text: 'How satisfied are you with our service?',
options: '1. Very Satisfied\n2. Satisfied\n3. Neutral\n4. Dissatisfied\n5. Very Dissatisfied'
},
{
text: 'Would you recommend us?',
options: '1. Yes\n2. No'
}
];
}
}

🔒 Security​

PIN Verification​

async function verifyPIN(sessionId, pin) {
const session = await getSession(sessionId);
const userId = session.data.userId;

const isValid = await validateUserPIN(userId, pin);

if (!isValid) {
session.data.pinAttempts = (session.data.pinAttempts || 0) + 1;

if (session.data.pinAttempts >= 3) {
return {
message: 'Too many failed attempts. Account locked.',
end_session: true
};
}

return {
message: `Incorrect PIN. ${3 - session.data.pinAttempts} attempts remaining.\nEnter PIN:`,
data: session.data
};
}

return { verified: true };
}

Encryption​

All session data is encrypted at rest and in transit:

// Data is automatically encrypted
await continueUSSD({
session_id: 'session_123',
message: 'Enter PIN:',
data: {
accountNumber: '1234567890', // Encrypted automatically
sensitiveData: 'secret'
}
});

📊 Analytics​

Track USSD performance and user behavior:

GET /v1/ussd/analytics

Metrics Available​

  • Session Count - Total sessions initiated
  • Completion Rate - % of sessions completed
  • Average Duration - Mean session length
  • Drop-off Points - Where users exit
  • Popular Flows - Most used menu paths
  • Error Rate - Failed interactions

Example Dashboard Query​

const analytics = await fetch('https://api.afriroute.ai/api/v1/ussd/analytics', {
headers: { 'Authorization': 'Bearer $AFRIROUTE_API_KEY' },
params: {
start_date: '2024-03-01',
end_date: '2024-03-31',
shortcode: '*123#',
metrics: ['sessions', 'completion_rate', 'avg_duration']
}
});

// Response
{
"period": "2024-03-01 to 2024-03-31",
"shortcode": "*123#",
"sessions": 15420,
"completion_rate": 78.5,
"avg_duration": 42.3,
"popular_paths": [
{ "path": "main → balance", "count": 5240 },
{ "path": "main → transfer", "count": 3890 }
]
}

💰 Pricing​

TierPrice per SessionIncluded SessionsOverages
Starter$0.0085,000$0.010
Growth$0.00650,000$0.008
Business$0.004250,000$0.006
EnterpriseCustomUnlimitedN/A

Volume Discounts: Save up to 50% on high-volume plans.

View detailed pricing →


🛠️ SDKs​

# Node.js
npm install @afriroute/ussd

# Python
pip install afriroute-ussd

# PHP
composer require afriroute/ussd-sdk

# Java
maven: com.afriroute:ussd-sdk


❓ FAQ​

What's the maximum message length?

USSD messages can be up to 182 characters. For longer content, break it into multiple screens.

How long do sessions last?

Default session timeout is 90 seconds, configurable up to 180 seconds.

Can I use USSD for payments?

Yes! USSD is commonly used for mobile money transfers, bill payments, and airtime purchases.

Do I need a dedicated shortcode?

No, you can use shared shortcodes to get started. Dedicated shortcodes are available for enterprise customers.

How do I test USSD without a phone?

Use our USSD simulator in the dashboard or our testing API endpoints.


🚀 Get Started​

# Test with cURL
curl -X POST https://api.afriroute.ai/api/v1/ussd/send \
-H "Authorization: Bearer $AFRIROUTE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone": "+251911234567",
"shortcode": "*123#",
"message": "Welcome!\n1. Get Started\n2. Help",
"session_id": "test_session_123"
}'

Get API Keys → | View Code Examples → | Contact Support →


💬 Support​

Need help? We're here 24/7! 🚀