Self-hosting an SMS gateway feels like the obvious engineering move. Jasmin is free, written in Python, speaks SMPP and HTTP, and routes messages through RabbitMQ and Redis. Kannel has been around for decades. Why pay a per-segment markup to an API vendor for something you can run in a container?

The answer depends on what you expect the gateway to do. An open source gateway is a message router. It isn't a carrier connection, a 10DLC registration or a compliance program. Most of the cost of US business texting sits in those three things. This post covers what each piece costs you if you run it yourself, and where a managed API like Ready earns its rate. It also covers where it doesn't.

What an open source gateway actually gives you

The common options fall into three groups:

  • Jasmin. An SMPP client and server with an HTTP API, message routing and filtering, and delivery receipt (DLR) handling. It's built on Python, RabbitMQ and Redis. It's a good fit when you want to sit between your app and one or more SMPP upstreams.
  • Kannel. An older C gateway that speaks SMPP and HTTP. It's stable and widely deployed, and it has a steep configuration curve.
  • playSMS and phone-based gateways. playSMS is a PHP web front end that usually sits on top of something like Kannel. Android "SMS gateway" apps send through a SIM card in a phone.

None of these can deliver a text to a US handset on their own. Jasmin and Kannel need an upstream SMPP bind to an aggregator or carrier-connected provider. Someone still has to sell you that route, and that route still has to be registered.

The SIM-card shortcut

Phone-based gateways deserve a separate warning. Sending application traffic through a consumer SIM is application-to-person (A2P) traffic on a person-to-person (P2P) line. Carriers filter it, and they suspend lines that send it at volume. It's fine for a hobby project texting your own phone. It isn't a production path for a business. Our A2P vs P2P breakdown explains which rules apply to which traffic.

Carrier connectivity: the part you still buy

With Jasmin you get to choose your upstream. That's the real advantage of self-hosting. You can hold binds to several SMPP providers, write routing rules, and fail over between them yourself. If multi-provider routing is a hard requirement, a gateway gives you that control. Ready doesn't fail over between providers. If you need that, a single managed API won't give it to you.

The cost is everything that comes with each bind:

  • Negotiating and maintaining an SMPP account per upstream, including bind credentials, IP allowlists and window sizes.
  • Monitoring bind health and reconnecting when a bind drops at 3 AM.
  • Normalizing each provider's DLR status codes, because they don't agree.
  • Respecting carrier throughput. Jasmin can push far more messages per second than a carrier will accept. A fresh standard 10DLC campaign is allowed roughly 1 to 4 segments per second. Higher use-case tiers get tens, roughly 10 to 75. T-Mobile also caps daily volume by brand trust score, from about 2,000 a day at the lowest score. The gateway's speed doesn't change any of these limits. Your throttling logic has to match them, or messages get rejected.

10DLC registration doesn't go away

US carriers filter unregistered A2P traffic on local numbers. A self-hosted gateway changes nothing about that. Every brand and campaign still has to be registered through your upstream provider before your SMPP traffic is treated as legitimate.

For a single company sending its own texts, that's one brand and one campaign. For a platform that texts on behalf of its customers, it's one brand per customer, plus campaigns, plus number assignment. You'd be building that registration workflow on top of whatever your upstream exposes.

This is where a managed API saves the most engineering time. On Ready, each customer gets a sub-account with its own numbers and its own 10DLC registration, and registration status is one call (GET /v1/registration). Registration costs $25/mo and $2 per number. There's no setup fee, and the first month ($25) is charged at submit. Most approvals take 2 to 7 days. The 10DLC registration API post walks through how platforms register customers programmatically.

The compliance stack you'd be writing yourself

A gateway delivers whatever you hand it. Every one of these features becomes your code:

RequirementSelf-hosted gatewayReady
STOP / UNSUBSCRIBE handlingParse inbound MO messages, maintain a suppression list, check it on every sendAutomatic. The opt-out applies across campaigns, and opted-out numbers are refused before send and not charged
Quiet hoursMap area codes to time zones and build a hold queueCampaigns, drip campaigns and automations outside permitted hours wait up to 24 hours for the window. An API text is held only if the window opens within 6 hours, otherwise refused with 409 QUIET_HOURS_BLOCK. A held text is charged only when it goes out
DNC / TCPA litigator screeningLicense lists, build a lookup, keep the lists freshOptional scrub at $0.003 per segment (off by default), or a standalone scrub at $0.005 per contact
Landline validationBuy a lookup service, cache resultsOptional at $0.003 per segment (off by default)
Consent attestationDesign your own audit logRecords who sent, to how many and when, for each bulk send and each API send that sets consent_attested
Duplicate-send protectionBuild idempotency into your producer and queueIdempotency-Key header

Two points to keep straight.

First, none of this makes anyone lawsuit-proof. TCPA exposure runs $500 to $1,500 per text. Compliance remains the sender's responsibility, and the tooling only reduces risk. Ready's attestation is a per-send record, not a per-contact consent record. Your app still has to store when and where each person opted in.

Second, duplicate sends are a classic self-hosted bug. A RabbitMQ redelivery or a client retry sends the same text twice, and you pay for it twice. See how to stop duplicate texts for the patterns.

Encoding and segment math: same either way

Billing is per segment whichever route you take. A GSM-7 message fits 160 characters in one segment, or 153 per part once it splits. One emoji switches the message to Unicode, which allows 70 characters, or 67 per part. With a self-hosted gateway, you handle the UDH concatenation headers and the encoding detection yourself. Curly quotes pasted from a CMS will silently turn a 1-segment text into a 3-segment one. Ready swaps curly quotes, long dashes and ellipses for plain characters before sending, so those never change your count. Emoji still count as Unicode.

The money: a worked example with stated assumptions

The real cost comparison looks like this. Assumptions: 100,000 segments a month, one registered brand and campaign, and an upstream route priced near Twilio's published ~$0.0083 per segment. Carrier fees pass through on both paths, so they're left out of the comparison.

Self-hosted Jasmin:

  • Messaging: 100,000 × $0.0083 = $830
  • Servers for Jasmin, RabbitMQ, Redis and monitoring: assume $100
  • Maintenance, including bind monitoring, upgrades, compliance code and on-call: assume 10 hours/month at a $100/hour loaded engineering cost = $1,000
  • Monthly total: about $1,890, plus the initial build of the compliance and registration layer

Ready Standard:

  • Messaging: 100,000 × $0.0075 = $750, plus a $0.0045 carrier fee per segment
  • This excludes the compliance surcharge. Turning on scrubbing would add 100,000 × $0.003 = $300.

On per-segment price alone, the self-hosted route wins clearly, and so does a raw CPaaS. We won't pretend otherwise. The comparison flips only when your real maintenance hours run higher than the assumption. That's common in the first year, and common for platforms managing dozens of customer registrations. It also flips when the compliance layer you'd build is worth more than the per-segment difference. Swap in your own engineering rate and hours. If the math says self-host, self-host.

What the managed path looks like in code

Here's the entire send path on Ready:

curl -X POST https://api.tryready.com/v1/messages \
  -H "X-API-Key: $READY_API_KEY" \
  -H "Idempotency-Key: 6f1c2a9e-3b4d-4e8a-9c1f-2d7b5e0a8c34" \
  -H "Content-Type: application/json" \
  -d '{"to": "+12125550199", "body": "Your order #1042 has shipped.", "consent_attested": true}'

For lists, POST /v1/messages/bulk takes up to 100 recipients per call. Docs are at docs.tryready.com/api/messages and the quickstart.

Which one fits

Self-host Jasmin or Kannel if:

  • You need multi-provider SMPP routing with your own failover rules.
  • You already have SMPP contracts and engineers who know them.
  • Volume is high enough that the per-segment gap pays for a dedicated owner.
  • You're sending outside the US, where the registration picture is different.

Use a managed API if:

  • You're a platform texting for your customers and need per-customer sub-accounts and 10DLC registration.
  • You'd rather not write STOP handling, quiet hours and scrubbing yourself.
  • Nobody on the team wants to own an SMPP bind.

If you're comparing managed options, our developer SMS API comparison puts the main vendors side by side.

The practical takeaway

An open source gateway replaces the routing layer. You still have to buy carrier routes, register your brands and campaigns, and build the compliance code. Price all three honestly, including your engineering hours, before deciding.

If you want to see the managed side for yourself, sign up, add a card, and send your first 20 one-to-one texts free. Use them to test the API before you pay for registration, because the free texts end once a registration is paid for.