DEV Community

BridgeXAPI
BridgeXAPI

Posted on

Programmable routing vs programmable messaging: a Python SMS SDK you can actually test

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
Enter fullscreen mode Exit fullscreen mode

GitHub:

https://github.com/bridgexapi-dev/bridgexapi-python-sdk
Enter fullscreen mode Exit fullscreen mode

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())
Enter fullscreen mode Exit fullscreen mode

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())
Enter fullscreen mode Exit fullscreen mode

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 via bx_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
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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())
Enter fullscreen mode Exit fullscreen mode

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:

  1. request accepted
  2. message queued
  3. delivery observed
  4. 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
Enter fullscreen mode Exit fullscreen mode

Set your API key:

export BRIDGEXAPI_API_KEY=your_api_key
Enter fullscreen mode Exit fullscreen mode

On Windows:

set BRIDGEXAPI_API_KEY=your_api_key
Enter fullscreen mode Exit fullscreen mode

If you want to compare routes on your own number:

export BRIDGEXAPI_TEST_NUMBER=31612345678
Enter fullscreen mode Exit fullscreen mode

On Windows:

set BRIDGEXAPI_TEST_NUMBER=31612345678
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)