McTaba Labs logo
By Bonaventure Ogeto|

How USSD Works: Sessions, Menus, and Timeouts for Developers

USSD (Unstructured Supplementary Service Data) is a GSM protocol that lets a phone exchange text with a server in real time over the signaling channel, not over the internet. The phone dials a short code (like *384#), the network routes it to your application, and your server responds with text menus. The session is synchronous and temporary, lasting about 30 seconds per interaction, with no persistent connection between requests.

USSD vs SMS: the fundamental difference

SMS stores a message and delivers it later. USSD opens a real-time session. Think of SMS as email and USSD as a phone call where you talk in text.

Key differences that matter for development:

  • Sessions: USSD has a session with state. SMS is stateless. Each SMS is independent.
  • Real-time: USSD requires the user to interact immediately. SMS can sit in an inbox for hours.
  • Cost to user: USSD sessions are typically free for the end user (the service provider pays). SMS costs the sender.
  • No storage: USSD messages are not saved on the phone. Once the session ends, the conversation disappears. SMS messages persist.
  • Channel: USSD uses the signaling channel (the same one used for call setup), not the data channel. This is why it works without mobile data or WiFi.

This real-time, session-based nature is what makes USSD perfect for interactive applications like mobile banking. But it also means tight constraints on response time and content length.

The session lifecycle

A USSD session begins when a user dials a code and ends when either party terminates it. Here is the full lifecycle:

  1. User dials the code: the user enters something like *384# and presses the call button. The phone sends this to the mobile network.
  2. Network routes to gateway: the network looks up which application server handles that code and forwards the request to the USSD gateway (e.g., Africa's Talking, Africastalking).
  3. Gateway calls your server: the gateway sends an HTTP POST to your callback URL with the session ID, phone number, and any user input.
  4. Your server responds with text: you return either a CON (continue) response to show a menu and wait for input, or an END response to display a final message and close the session.
  5. User sees the menu: the text appears on their phone screen. On feature phones, it looks like a basic text dialog. On smartphones, it appears as a USSD overlay.
  6. User responds: they type a number or text and press Send. The gateway appends their input to the previous selections (separated by *) and calls your server again.
  7. Repeat steps 3 to 6 until your server sends an END response or the session times out.

Each round trip (user sees menu, types response, server processes) takes a few seconds. The total session can include multiple rounds of interaction, but each round has a timeout.

How the text field accumulates

The text field in each request from the gateway contains the entire history of user input for the session, with each selection separated by an asterisk (*).

Example of a three-level navigation:

// First request (user just dialed the code)
text: ""

// User selected option 1
text: "1"

// User selected option 2 in the sub-menu
text: "1*2"

// User typed "John" in a text input
text: "1*2*John"

// User typed their phone number
text: "1*2*John*0712345678"

Your server uses this accumulated text to determine which menu to show. Split by * and check the length to know which level the user is at:

const levels = text === '' ? [] : text.split('*');

if (levels.length === 0) {
  // Show main menu
} else if (levels.length === 1) {
  // Show sub-menu based on levels[0]
} else if (levels.length === 2) {
  // Process based on levels[0] and levels[1]
}

This is simpler than it first appears. The gateway manages the session state for you. Your server is effectively stateless. It receives the full input history on every request and returns the appropriate menu. No session storage needed on your end for basic flows.

For complex flows where you need to store intermediate data (like partial form submissions), use the sessionId as a key in a temporary store (Redis works well for this).

Timeouts, character limits, and constraints

USSD has hard technical limits that shape how you design your service:

Session timeout: a USSD session times out after approximately 30 seconds of user inactivity. If the user does not respond to a menu within 30 seconds, the session drops. Different networks may have slightly different timeout values, but 30 seconds is the common baseline.

Server response time: your server must respond to the gateway within a few seconds (typically 10 to 15 seconds). If your server takes too long to generate a response, the gateway times out and the session dies. Avoid database queries or external API calls that could be slow.

Character limits: a USSD screen can display about 160 characters. Some phones support more, some less. To be safe, keep each response under 160 characters. Longer text gets truncated, not paginated. The user simply does not see the rest.

Menu depth: there is no hard limit on how many levels deep a USSD menu can go, but each level costs the user time. Three levels deep is the practical maximum before users start giving up. A three-level flow (main menu, category, action) takes about 15 to 20 seconds, leaving little margin before timeout.

Input format: users can only type numbers and text. No rich formatting, no images, no links (though you can display a URL as text). Design your menus with numbered options: 1. Check balance\n2. Send money\n3. Help

These constraints force clarity. Every word on a USSD screen must earn its place.

Design patterns for USSD menus

Good USSD menu design respects the medium's constraints. Here are patterns that work well:

Flat menus over deep trees: each navigation level takes 5 to 10 seconds. Three levels is fine. Five levels means the session times out before the user finishes. If your menu tree is getting deep, flatten it by offering more options at each level.

Confirm before action: before processing a payment or submitting data, show a confirmation screen with the details and ask the user to press 1 to confirm. Users make mistakes, especially on small phone keypads.

// Confirmation pattern
if (levels.length === 3) {
  const course = getCourse(levels[1]);
  const phone = levels[2];
  response = 'CON Confirm enrollment:\n';
  response += `Course: ${course.name}\n`;
  response += `Phone: ${phone}\n`;
  response += '1. Confirm\n';
  response += '2. Cancel';
} else if (levels.length === 4) {
  if (levels[3] === '1') {
    // Process enrollment
    response = 'END You are enrolled. Check SMS.';
  } else {
    response = 'END Cancelled.';
  }
}

Error recovery: when a user enters an invalid option, do not end the session. Show the menu again with a brief error message:

if (!validOptions.includes(levels[0])) {
  response = 'CON Invalid option. Try again:\n';
  response += '1. Check courses\n';
  response += '2. Talk to a mentor';
}

Keep text terse: "Check balance" not "Click here to check your account balance." Every character counts when you have 160 to work with.

Send details via SMS: if you need to give the user more information than USSD can display (a receipt, a long confirmation, a link), end the USSD session and follow up with an SMS containing the details.

Frequently Asked Questions

Can USSD work without mobile data or WiFi?
Yes. USSD uses the GSM signaling channel, which is separate from the data channel. As long as the phone has network signal (the kind needed to make a voice call), USSD works. This is the main reason it is still critical infrastructure in Africa.
Why do USSD sessions feel slow compared to apps?
Each interaction requires a round trip through the mobile network to the gateway to your server and back. That is three to five network hops. Combined with the overhead of the GSM signaling protocol, each round trip takes 2 to 5 seconds even when your server responds instantly. There is no way to reduce this latency at the application level.
Can a user resume a USSD session if they accidentally cancel?
No. Once a USSD session is terminated (by timeout, user cancellation, or the END response), it is gone. The user must dial the code again and start over. This is why keeping flows short and avoiding unnecessary levels is so important. If you need to preserve progress, save it server-side using the phone number as a key and resume when they dial again.

Ready to build real-world apps?

Join the McTaba Labs full-stack marathon. Ship 8 production apps with M-Pesa, USSD, and WhatsApp integrations, and get career support until placement.

See Programs

Also available: M-Pesa Integration course