Infobip
Send text messages to one or many recipients through the Infobip SMS API from an outgoing REST connection.
This page will show you how to send text messages through Infobip from Python:
- What to collect from the Infobip portal
- Which security definition and outgoing REST connection to create
- How to send a message from a service and read Infobip's response
- How to send the same message to many recipients in one request
Collecting Infobip config
You'll need three details from the Infobip portal:
- The API base URL, which is specific to each account and is shown on the portal's home page, e.g.
xy1z2w.api.infobip.com - An API key, created on the API keys page of the portal
- The sender, which is a number or an alphanumeric sender ID registered with the account
Creating Zato connections
First, create an API key definition under Security → API keys. Infobip expects the key in the Authorization header, after the word App and a space:
| Field | Value | Notes |
|---|---|---|
| Name | Infobip SMS | --- |
| Header | Authorization | --- |
| API key | App <API key> | E.g. App 4a3f6e1c..., with the space |

Next, create an outgoing REST connection under Connections → Outgoing → REST:
| Field | Value | Notes |
|---|---|---|
| Name | Infobip SMS | --- |
| Host | https://<API base URL> | E.g. https://xy1z2w.api.infobip.com |
| URL path | /sms/3/messages | --- |
| Data format | JSON | --- |
| Security | API key/Infobip SMS | The definition just created |

Sending a message
The service below will:
- Build the message from the input it received
- Send it to Infobip
- Return the ID Infobip gave the message and its status
# -*- coding: utf-8 -*-
# Zato
from zato.server.service import Service
class SendInfobipSMS(Service):
name = 'sms.infobip.send'
input = 'to', 'text'
def handle(self):
input = self.request.input
# The message in Infobip's format ..
message = {
'sender': 'Riverside',
'destinations': [{'to': input.to}],
'content': {'text': input.text},
}
# .. one request can carry many messages ..
request = {'messages': [message]}
# .. get the connection to Infobip ..
conn = self.rest['Infobip SMS']
# .. send the message ..
response = conn.post(request)
# .. make sure Infobip accepted it ..
if not response.ok:
request_error = response.data['requestError']
error = request_error['serviceException']
reason = error['text']
raise Exception(f'Infobip error: {reason}')
# .. there is one result for each message ..
messages = response.data['messages']
result = messages[0]
status = result['status']
# .. and tell our caller what Infobip said.
self.response.payload = {
'message_id': result['messageId'],
'status': status['groupName'],
}
Numbers go to Infobip in E.164 form without the leading +, so the service is invoked with {"to": "12025550123", "text": "Your appointment is tomorrow at 9:00"}.
After invoking the service you'll see:
The PENDING status group means Infobip has accepted the message and is delivering it.
Sending to many recipients
Say the clinic closes early one afternoon and every patient booked for that afternoon is to be told. A message can list any number of destinations, so one request sends the same text to all of them.
The service below will:
- Build one destination for each number in its input
- Send the message to all of them in one request
- Return the ID and the status of the message to each number
# -*- coding: utf-8 -*-
# Zato
from zato.server.service import Service
class SendInfobipSMSToMany(Service):
name = 'sms.infobip.send-to-many'
input = 'phones', 'text'
def handle(self):
input = self.request.input
# One destination for each number ..
destinations = []
for phone in input.phones:
destinations.append({'to': phone})
# .. the message with all of them ..
message = {
'sender': 'Riverside',
'destinations': destinations,
'content': {'text': input.text},
}
# .. send it in one request ..
request = {'messages': [message]}
conn = self.rest['Infobip SMS']
response = conn.post(request)
if not response.ok:
request_error = response.data['requestError']
error = request_error['serviceException']
reason = error['text']
raise Exception(f'Infobip error: {reason}')
# .. Infobip returns one result for each destination ..
results = []
for item in response.data['messages']:
status = item['status']
result = {
'phone': item['destination'],
'message_id': item['messageId'],
'status': status['groupName'],
}
results.append(result)
# .. and our caller receives all of them.
self.response.payload = results
After invoking the service you'll see:
[
{
"phone": "12025550123",
"message_id": "4f7a2c9e1b3d5f6a8c0e",
"status": "PENDING"
},
{
"phone": "12025550124",
"message_id": "8b1d3f5a7c9e2b4d6f8a",
"status": "PENDING"
}
]
Errors
When Infobip does not accept a request, it answers with a 4xx status and a JSON body whose requestError.serviceException.text says why, e.g.:
{
"requestError": {
"serviceException": {
"messageId": "UNAUTHORIZED",
"text": "Invalid login details"
}
}
}
The services above turn such a response into an exception with the text.
See also
| Page | What it covers |
|---|---|
| Your first SMS | One service that every other service sends text messages through |
| API keys | Every field of an API key definition, including the header it is sent in |
| Calling external REST APIs | Parameters, headers and the response object of a REST call |