Most SMS APIs give you programmable messaging.
You control the message.
They control the route.
That sounds convenient, until delivery starts changing between countries, costs shift without context, and performance becomes something you can feel but not inspect.
You send an SMS request.
It either lands or it doesn’t.
The provider handles the rest somewhere behind the curtain.
That model is common.
It is also a black box.
BridgeXAPI was built around a different idea:
routing should not be hidden from developers.
Instead of treating transport as an implementation detail, BridgeXAPI exposes it directly.
You choose the route.
You estimate the cost before sending.
You inspect route availability.
You track delivery with BridgeX message identifiers.
You compare what actually lands on a real handset.
That is the difference between programmable messaging and programmable routing.
Programmable messaging vs programmable routing
The traditional SMS API model looks like this:
- write a message
- submit a request
- hope delivery behavior stays stable
- accept whatever route selection happened in the background
The BridgeXAPI model looks like this:
- choose a
route_id - see pricing per destination
- send through a route intentionally
- track the message using
bx_message_id - compare behavior across routes
That changes the role of SMS inside a backend.
It stops being:
“send text”
and becomes:
“control transport”
That matters if you care about:
- OTP flows
- delivery reliability
- route testing
- cost optimization
- high-volume messaging behavior
- repeatable delivery analysis
The Python SDK v2
We just released BridgeXAPI Python SDK v2.
Install:
pip install bridgexapi
GitHub:
https://github.com/bridgexapi-dev/bridgexapi-python-sdk
This SDK is not just a thin wrapper around one send endpoint.
It exposes multiple parts of the system:
Sending
send_one(...)send_sms(...)
Pricing
estimate(...)get_route_pricing(route_id)
Routes
list_routes()get_route(route_id)
Delivery
list_dlr(...)get_dlr(bx_message_id)get_order_dlr(order_id)
Account / observability
get_balance()get_activity_summary()get_activity_logs(...)get_activity_usage(...)
Those methods are directly reflected in the included example scripts. :contentReference[oaicite:10]{index=10} :contentReference[oaicite:11]{index=11} :contentReference[oaicite:12]{index=12} :contentReference[oaicite:13]{index=13} :contentReference[oaicite:14]{index=14} :contentReference[oaicite:15]{index=15} :contentReference[oaicite:16]{index=16}
Quick start
A basic single-message send looks like this:
from bridgexapi import BridgeXAPI, Route
client = BridgeXAPI(api_key="YOUR_API_KEY")
response = client.send_one(
route_id=Route.ROUTE_2,
caller_id="BRIDGEXAPI",
number="31651860670",
message="Your verification code is 927144",
)
print(response.to_dict())
There is also a batch example:
from bridgexapi import BridgeXAPI, Route
client = BridgeXAPI(api_key="YOUR_API_KEY")
response = client.send_sms(
route_id=Route.ROUTE_2,
caller_id="BRIDGEXAPI",
numbers=[
"34699108839",
"34676784008",
"34683466361",
],
message="Your verification code is 483921",
)
print(response.to_dict())
These two are included as runnable example files in the repo. :contentReference[oaicite:18]{index=18} :contentReference[oaicite:19]{index=19}
Included example scripts
The SDK repo ships with real examples, not placeholders.
Sending
-
examples/send_one.py— send a single SMS :contentReference[oaicite:20]{index=20} -
examples/send_batch.py— send a batch of SMS messages :contentReference[oaicite:21]{index=21}
Balance / pricing / routes
-
examples/balance.py— fetch current balance :contentReference[oaicite:22]{index=22} -
examples/estimate.py— preview cost before sending :contentReference[oaicite:23]{index=23} -
examples/routes.py— inspect route catalog and pricing :contentReference[oaicite:24]{index=24}
Delivery tracking
-
examples/dlr_list.py— list recent delivery reports :contentReference[oaicite:25]{index=25} -
examples/get_dlr.py— fetch a single delivery report viabx_message_id:contentReference[oaicite:26]{index=26} -
examples/order_dlr.py— fetch delivery reports for a whole order :contentReference[oaicite:27]{index=27}
Observability
-
examples/activity_summary.py— usage summary :contentReference[oaicite:28]{index=28} -
examples/activity_logs.py— recent API activity logs :contentReference[oaicite:29]{index=29} -
examples/activity_usage.py— usage over time :contentReference[oaicite:30]{index=30}
Route comparison
-
examples/test_all_routes.py— compare route behavior on your own number :contentReference[oaicite:31]{index=31}
That last one is the most important.
The killer example: test routes on your own number
Most SMS APIs do not let you compare delivery behavior across routes in a direct, developer-friendly way.
This SDK includes exactly that.
Run:
python examples/test_all_routes.py
The script:
- reads your API key from
BRIDGEXAPI_API_KEY - reads your target number from
BRIDGEXAPI_TEST_NUMBER - sends the same message across routes 1, 2, 3 and 4
- prints:
- route status
- order ID
- BridgeX message ID
- handset reference
- per-route cost
The script uses a clean message format:
BridgeXAPI verification notice. Ref: R1.
BridgeXAPI verification notice. Ref: R2.
BridgeXAPI verification notice. Ref: R3.
So the developer can literally look at their own phone and compare what lands per route. That behavior is implemented directly in examples/test_all_routes.py. :contentReference[oaicite:32]{index=32}
Example output looks like this:
BridgeXAPI programmable routing test
Recipient: 316278727227
[Route 1] ACCEPTED
order_id : 22327
bx_message_id : BX-22327-8d77942ea22dda34
cost : 0.088 EUR
handset_ref : R1
[Route 2] ACCEPTED
order_id : 22329
bx_message_id : BX-22329-903feeb249128f95
cost : 0.088 EUR
handset_ref : R2
[Route 3] ACCEPTED
order_id : 22331
bx_message_id : BX-22331-a8b7044d9842d5ab
cost : 0.091 EUR
handset_ref : R3
[Route 4] REJECTED
reason : No pricing available for destination '316278727227' on route 4.
That is not “marketing”.
That is transport behavior, exposed to the developer.
Why this matters
If you only have programmable messaging, then routing stays hidden.
You can optimize content.
You can’t optimize transport.
With programmable routing, you can start asking useful engineering questions:
- which route is valid for this destination?
- what is the price difference?
- what actually lands on the handset?
- which route should I use for OTP?
- which route is appropriate for a given geography?
- how should my backend choose delivery paths?
This is where BridgeXAPI stops looking like a normal gateway wrapper.
It becomes infrastructure.
Pricing before sending
A major part of working with messaging infrastructure is cost visibility.
This SDK includes examples/estimate.py, which lets you preview the cost of a message batch before you send anything. :contentReference[oaicite:33]{index=33}
Example:
from bridgexapi import BridgeXAPI, Route
client = BridgeXAPI(api_key="YOUR_API_KEY")
response = client.estimate(
route_id=Route.ROUTE_2,
caller_id="BRIDGEXAPI",
numbers=[
"34681326502",
"34676784008",
],
message="BridgeXAPI estimate test",
)
print(response.to_dict())
That means the backend can decide:
- whether a route is affordable
- whether balance is sufficient
- whether a destination should use a different path
Again, this is not hidden.
It is part of your application logic.
Route visibility
Most providers give you a send endpoint and not much else.
BridgeXAPI exposes route information directly.
examples/routes.py shows:
- route catalog
- route detail
- route pricing
It calls:
list_routes()get_route(1)get_route_pricing(1)
and prints the response. :contentReference[oaicite:34]{index=34}
That means a developer can inspect:
- which routes exist
- which ones are available
- how they are priced
- which destinations are covered
That is a much stronger model than just “submit and hope”.
Delivery tracking with BridgeX identifiers
Every accepted message gets a bx_message_id.
That matters because it gives the developer a stable infrastructure-level identifier they can use for tracing delivery.
You can see that in the send responses, and you can use it in examples/get_dlr.py, which fetches a single delivery report using the returned BridgeX ID.
There is also examples/order_dlr.py for order-level tracking, and examples/dlr_list.py for recent DLR inspection.
This is important because delivery is not a single state.
A message lifecycle looks more like this:
- request accepted
- message queued
- delivery observed
- final state reached
That is exactly why bx_message_id exists.
Activity and observability
The SDK is not limited to sending and delivery.
It also includes activity scripts for observability:
-
activity_summary.py:contentReference[oaicite:37]{index=37} -
activity_logs.py:contentReference[oaicite:38]{index=38} -
activity_usage.py:contentReference[oaicite:39]{index=39}
These expose:
- current activity summary
- recent request logs
- usage across time
That turns the SDK into more than a send client.
It becomes a developer interface to the broader system.
Who this is for
This is not primarily for hobby projects that send one message and forget about it.
This is for developers and teams who care about:
- OTP delivery
- backend integration
- route behavior
- delivery traceability
- pricing per destination
- infrastructure visibility
- high-volume operational messaging
That includes:
- backend engineers
- infrastructure-minded developers
- OTP / auth platforms
- trading platforms
- iGaming systems
- marketplaces
- teams running serious alerting or transactional flows
The key thing is that these people do not want mystery.
They want control.
How to run the examples
After cloning the repo:
git clone https://github.com/bridgexapi-dev/bridgexapi-python-sdk
cd bridgexapi-python-sdk
Set your API key:
export BRIDGEXAPI_API_KEY=your_api_key
On Windows:
set BRIDGEXAPI_API_KEY=your_api_key
If you want to compare routes on your own number:
export BRIDGEXAPI_TEST_NUMBER=31612345678
On Windows:
set BRIDGEXAPI_TEST_NUMBER=31612345678
Then run:
python examples/send_one.py
python examples/send_batch.py
python examples/balance.py
python examples/estimate.py
python examples/routes.py
python examples/get_dlr.py
python examples/order_dlr.py
python examples/activity_summary.py
python examples/activity_logs.py
python examples/activity_usage.py
python examples/test_all_routes.py
All of these example scripts are included in the public SDK repo and use environment variables instead of hardcoded credentials.
If you want to test this
If you want to try BridgeXAPI in a real environment:
- request free test credits
- ask for access
- message us on our socials
- or email us directly
If you are building something volume-sensitive and want to test delivery behavior across routes, that is exactly the kind of use case this SDK is meant for.
Final note
Programmable messaging says:
write the message, we handle the rest
Programmable routing says:
delivery is part of your code
That is the difference.
That is what the BridgeXAPI Python SDK v2 is built around.
GitHub: https://github.com/bridgexapi-dev/bridgexapi-python-sdk
PyPI: pip install bridgexapi
BridgeXAPI
programmable routing > programmable messaging
Top comments (0)