ProCat Solutions
One message a second: running an SMS gateway on our own GSM modems
Modem pools, AT commands, SIM management, delivery reports and queues. When sending SMS from your own GSM modems pays off, and when an aggregator is better.
A single GSM modem can send roughly one SMS per second. Almost every design decision in a home-grown SMS setup follows from that number. After our VoIP post this summer, several people asked whether we run SMS on our own hardware too. The answer: it depends. Over the past months we built two systems in which text messaging was the backbone of the service, and both raised the same question: an aggregator API, or our own GSM modems.

The oldest channel
SMS is the oldest digital messaging channel that everyone still uses. It needs no app, no data connection and no smartphone. A one-time code, an appointment reminder or an alert gets through even if the recipient is using a ten-year-old handset.
In practice, four arguments favour GSM modems:
- The message comes from a local number. Messages sent through an aggregator often appear from a short code or an alphanumeric sender. Some recipients treat that as spam, and some networks filter it.
- Two-way communication. A real SIM card can be replied to, and the reply arrives where the question came from. That matters in customer support.
- Predictable cost. Up to a certain volume, a business subscription with an unlimited or large SMS allowance is cheaper than per-message pricing.
- Independence. The system does not hinge on the availability of an external API.
The drawbacks are real too. We return to them at the end.
Modem pools and AT commands
At one message per second, any serious volume needs several modems. We use USB modems or industrial devices with multiple SIM slots, under Linux, over serial ports.
We talk to the modems in AT commands, a decades-old text protocol: AT+CMGS sends a message, AT+CMGL lists received ones, AT+CSQ returns signal strength. Simple on paper. In practice:
- every manufacturer reads the standard slightly differently, and the error messages are often meaningless,
- accented characters and multipart messages need PDU mode (binary encoding) to work reliably; text mode is good enough only for a demo,
- a serial port can be used by one process at a time, so access to each modem has to be serialised,
- modems freeze now and then, and only a USB-level reset or a power cycle brings them back.
A separate service therefore runs the pool. One worker per modem owns the serial port, takes jobs from a shared Redis queue and writes the result back. When a modem drops out, capacity shrinks and the service keeps running.

SIM cards are a job in themselves
On the first system we underestimated how much work the SIM cards are. Someone has to track which SIM sits in which modem, which subscription belongs to it, how many messages went out on it in a given period, and when its credit or allowance runs out. Operators may suspend SIMs with unusually heavy traffic, so we spread the load evenly across the pool and enforce a daily limit per SIM.
Delivery reports are one of the big advantages of SIM-based sending. The network confirms that the message reached the handset, not merely that the centre accepted it. That requires asking for the report when sending, then matching the incoming report to the original message by its network message reference. References can repeat per modem, so matching also has to be narrowed by modem and time window.
Queues and retries
Sending SMS is asynchronous. The application does not wait for the message to go out: it queues it and checks the status later. When designing the queue we account for:
- priority: a one-time login code cannot wait behind a marketing campaign,
- retries with exponential backoff, but only on modem errors; an invalid number will not improve on the tenth attempt,
- idempotency: the same message to the same number never goes out twice within a time window,
- outgoing messages and incoming replies land in a shared, tenant-filtered message log.
That last point opens a wider subject. SMS is most useful when it does not stand alone. In the systems we build, calls, SMS and web notifications share a common event model. If a call goes unanswered, an SMS goes out. If someone replies to the SMS, the reply lands on the customer’s timeline. We increasingly treat this pattern as a baseline requirement.
The case for aggregators
In most cases, an aggregator is the better choice. Your own modem pool pays off when a local number, two-way messaging or independence is a concrete business requirement, and someone is around to operate the hardware. For international sending, high volumes (tens of thousands a day), or simply when nobody has time to shepherd modems, the aggregator API wins: one HTTP call, a delivery webhook, an invoice at the end of the month.
Either way, we abstract the sending layer. The application sends messages through an internal interface. Whether a modem, an aggregator or both sit behind it (modems for domestic traffic and an aggregator abroad, say) is a matter of configuration. A modem failure or a change of provider then leaves the business logic untouched.