Skip to main content

AI Number

AI Number Python API Client

PyPI version

Table of contents

Contacts

Contact Tags

Contact Fields

Conversations

Teams

Automation

Webhooks

Mics

Installation

> pip install sendbee-api

Initialization

from sendbee_api import SendbeeApi

api = SendbeeApi('__your_api_key_here__', '__your_secret_key_here__')

Contacts

Fetch contacts

contacts = api.contacts(
    [tags=['...', ...]], [status='subscribed|unsubscribed'], 
    [search_query='...'], [page=...], [limit=...]
)

for contact in contacts:
    contact.id
    contact.status
    contact.folder
    contact.created_at
    
    contact.name
    contact.phone
    
    for tag in contact.tags:
        tag.id
        tag.name
    
    for note in contact.notes:
        note.value
    
    for contact_field in contact.contact_fields:
        contact_field.key
        contact_field.value

Subscribe contact

contact = api.subscribe_contact(
    phone='+...',
    # this is mandatory the most important information
    # about the subscribing contact
    
    [tags=['...', ...]], 
    # tag new contact
    # if tag doesn't exist, it will be created
    
    [name='...'], 
    
    [notes=[...]], 
    # write notes about your new subscriber
    
    [contact_fields={'__field_name__': '__field_value__', ...}],
    # fill contact fields with your data (value part)
    # contact fields must be pre-created in Sendbee Dashboard
    # any non-existent field will be ignored 
    
    [block_notifications=[True|False]],
    # prevent sending browser push notification and email 
    # notification to agents, when new contact subscribes
    # (default is True) 
    
    [block_automation=[True|False]]
    # prevent sending automated template messages to newly
    # subscribed contact (if any is set in Sendbee Dashboard) 
    # (default is True) 
)

contact.id
contact.status
contact.folder
contact.created_at

contact.name
contact.phone

for tag in contact.tags:
    tag.id
    tag.name

for note in contact.notes:
    note.value

for contact_field in contact.contact_fields:
    contact_field.key
    contact_field.value

Update contact

contact = api.update_contact(
    id='...',
    # contact is identified with ID
    
    [phone='+...'],
    # this is the most important information 
    # about the subscribing contact
    
    [tags=['...', ...]], 
    # tag new contact
    # if tag doesn't exist, it will be created
    
    [name='...'],
    
    [notes=[...]], 
    # write notes about your new subscriber
    # if there are notes already saved for this contact
    # new notes will be appended
    
    [contact_fields={'__field_name__': '__field_value__', ...}],
    # fill contact fields with your data (value part)
    # contact fields must be pre-created in Sendbee Dashboard
    # any non-existent field will be ignored 
    # if there are fields already filled with data for this contact
    # it will be overwritten with new data 
)

contact.id
contact.status
contact.folder
contact.created_at

contact.name
contact.phone

for tag in contact.tags:
    tag.id
    tag.name

for note in contact.notes:
    note.value

for contact_field in contact.contact_fields:
    contact_field.key
    contact_field.value

Contact tags

Fetch contact tags

tags = api.tags([name='...'], [page=...], [limit=...])

for tag in tags:
    tag.id
    tag.name

Create contact tag

tag = api.create_tag(name='...')

tag.id
tag.name

Update contact tag

tag = api.update_tag(id='...', name='...')

tag.id
tag.name

Delete contact tag

response = api.delete_tag(id='...')

response.message

Contact fields

Fetch contact fields

contact_fields = api.contact_fields([search_query='...'], [page=...], [limit=...])

for contact_field in contact_fields:
    contact_field.name
    contact_field.type
    
    if contact_field.type == 'list':
        contact_field.options

Create contact field

If a contact field type is a list, then you need to send a list options.
List options is a list of option names: ['option1', 'option2', ...]

contact_field = api.create_contact_field(
    name='...', type='text|number|list|date|boolean',
    [options=['...'. ...]] # if contact field type is list
)

contact_field.id
contact_field.name
contact_field.type

if contact_field.type == 'list':
    contact_field.options

Update contact field

If a contact field type is a list, then you need to send a list options.
List options is a list of option names: ['option1', 'option2', ...]

contact_field = api.update_contact_field(
    id='...', [name='...'], [type='text|number|list|date|boolean'],
    [options=['...'. ...]] # if contact field type is list
)

contact_field.id
contact_field.name
contact_field.type

if contact_field.type == 'list':
    contact_field.options

Delete contact field

response = api.delete_contact_field(id='...')

response.message

Conversations

Fetch conversations

conversations = api.conversations(
    [folder='open|done|spam|notified'], [search_query='...'], 
    [page=...], [limit=...], [date_from=__timestamp__], [date_to=__timestamp__]
)

for conversation in conversations:
    conversation.id
    conversation.folder
    conversation.chatbot_active
    conversation.platform
    conversation.created_at
    
    conversation.contact.id
    conversation.contact.name
    conversation.contact.phone
    
    conversation.last_message.direction
    conversation.last_message.status
    conversation.last_message.inbound_sent_at
    conversation.last_message.outbound_sent_at

Fetch conversation messages

messages = api.messages(conversation_id='...', [page=...])

for message in messages:
    message.body
    message.media_type
    message.media_url
    message.status
    message.direction
    message.sent_at

Fetch message templates

Message templates must first be sent for approval.
Therefor every message template could have following status: pending, approved, rejected
If message template comms with rejected status, it also comes with rejected_reason.

templates = api.message_templates(
    [status="pending|approved|rejected"], [search_query='...'],  
    [page=...], [limit=...]
)

for template in templates:
    template.id
    template.text
    template.buttons # available for all Sendbee users onboarded after 11th of December 2020
    template.button_tags # available for all Sendbee users onboarded after 11th of December 2020
    template.tags
    template.keyword
    template.language
    template.status
    template.rejected_reason

Send template message

Message template can be purely textual or textual with attachment.
If it is with attachment, that means you can send image, video or document URL together with text.
When you get a list of message templates, every template in that list has attachment field with it's value.
Attachment field value defines which type of attachment can be sent with message template:

Value Description
image Message template can be sent with image URL: JPG/JPEG, PNG
video Message template can be sent with video URL: MP4, 3GPP
document Message template can be sent with document URL: PDF, DOC, DOCX, PPT, PPTX, XLS, XLSX
null Message template does not support attachment URL
response = api.send_template_message(
    phone='+...',
    
    template_keyword='...',
    # every pre-created and approved message template
    # is identified with a keyword
    
    language='...', 
    # language keyword
    # example: en (for english)
    
    tags={'__tag_key__': '__tag_value__', ...},
    # tags for template messages are parts of the message that need
    # to be filled with your custom data
    # example:
    # template message: "Welcome {{1}}! How can we help you?"
    # tags: {"1": "John"}
    # Learn more: https://developer.sendbee.io/#send-message-template
        
    button_tags={'__tag_key__': '__tag_value__', ...}
    # tags for call-to-action button with dynamic URL suffix that need
    # to be filled with your custom data
    # example:
    # template message: https://example.com/{{1}}
    # tags: {"1": "page/123"}
    
    [prevent_bot_off=True|False],
    # if set to True, will prevent turning-off chatbot for the conversation
    # default system behaviour is that chatbot is turned-off
    
    [attachment='http...']
)

response.status
response.conversation_id
response.message_id
# save this id, and when you get sent message status requests on
# your webhook, you'll get this same id to identify the conversation

Send message

You can send either text message or media message.
For media message, following formats are supported:

Category Formats
Audio AAC, M4A, AMR, MP3, OGG OPUS
Video MP4, 3GPP
Image JPG/JPEG, PNG
Document PDF, DOC, DOCX, PPT, PPTX, XLS, XLSX
response = api.send_message(
    phone='+...',
    
    [text='...'],
    # any kind of message text
    
    [media_url='...'],
    # URL to a media. 
    # you need to upload it your self and send us the URL
    
    [prevent_bot_off=True|False]
    # if set to True, will prevent turning-off chatbot for the conversation
    # default system behaviour is that chatbot is turned-off
)

response.status
response.conversation_id
response.message_id
# save this id, and when you get sent message status requests on
# your webhook, you'll get this same id to identify the conversation

Teams

Fetch teams

teams = api.teams([member_id='...'])

for team in teams:
    team.id
    team.name

    for member in team.members:
        member.id
        member.name
        member.role
        member.online
        member.available

Fetch team members

members = api.members([team_id='...'])

for member in members:
    member.id
    member.name
    member.role
    member.online
    member.available

    for team in member.teams:
        team.id
        team.name

Automation

Managing chatbot (automated responses) status settings

Every contact is linked to a conversation with an agent.
Conversation could be handled by an agent or a chatbot (automated responses).
Every time a message has been sent to a contact by an agent or using the API, the chatbot will automatically be turned off for that conversation - except when you set 'prevent_bot_off' to true via API call (see Send message).
Use the example below to change the chatbot status based on your use case.

api.chatbot_activity(conversation_id='...', active=True|False)

Get chatbot (automated responses) status

You can also check if chatbot is turned on or off for a conversation.

response = api.chatbot_activity_status(conversation_id='...')

response.conversation_id
response.chatbot_active # True/False

Webhooks

Subscribe to webhook

Register a URL of yours and subscribe it to a webhook event, so we start sending you requests when that event happens. This does the same thing as activating a webhook URL in the Sendbee Dashboard.

webhook = api.subscribe_webhook(
    url='https://example.com/sendbee/webhook',
    event='message.received'
)

webhook.id                 # UUID of the webhook endpoint
webhook.url                # the URL you registered
webhook.active             # True/False
webhook.selected_webhooks  # list of all events this URL is subscribed to
webhook.secret_key         # key used to sign requests we send to your URL
webhook.created_at

Both url and event are required. Calling it again for the same url is safe - it will not create a duplicate, it just adds the new event to that URL (and re-activates it if it was deactivated), so subscribe one event per call:

for event in ('message.received', 'message.sent'):
    api.subscribe_webhook(url='https://example.com/sendbee/webhook', event=event)

Available events:

Event Description
contact.created A new contact was created
contact.subscription_created A contact subscribed
contact.subscription_updated A contact's subscription changed
contact.unsubscribed A contact unsubscribed
message.received A message was received from a contact
message.sent A message was sent to a contact
message.sent_status Delivery status of a sent message changed
whatsapp.message.session.sent_status Delivery status of a session message changed
whatsapp.message.template.sent_status Delivery status of a template message changed
whatsapp.message.template.approval_status A message template was approved or rejected
conversation.change_folder A conversation was moved to another folder

An unknown event name returns a SendbeeRequestApiException. For the authoritative and up to date list, see the documentation.

Once subscribed, see Authenticate webhook request for how to verify the requests we send you.

Misc

Pagination

You can paginate on every endpoint/method where a list of something is fetching.
Wherever you see [page=...] it means you can paginate like page=2, page=3, etc. The best way to do that is to use .next_page() method.

There are two ways to detect that pagination ended, using PaginationException and using .has_next() method.

Paginate using .next_page() and PaginationException:

from sendbee_api import PaginationException

messages = api.messages(conversation_id='...') # first page
while True:
    try:
        messages = api.messages(
            conversation_id='...', page=messages.next_page()
        )
    except PaginationException as e:
        break

Paginate using .next_page() and .has_next() methods:

messages = api.messages(conversation_id='...') # first page
while True:
    if not messages.has_next():
        break
    messages = api.messages(
        conversation_id='...', page=messages.next_page()
    )

API Rate Limit

A rate limit is the number of API calls a business number can make within a given time period. If this limit is exceeded, API requests are throttled, and will fail with 429 error response.

No matter how many API keys you make in your Sendbee account, rate limit will always be counted within a business number.

from sendbee_api import SendbeeRequestApiException

try:
    api.rate_limit_request_test()
except SendbeeRequestApiException as ex:
    retry_after = ex.response.headers.get('Retry-After')

Raw response

If you prefer to deal with the raw server response, response string is available under raw_data

from sendbee_api import SendbeeApi

api = SendbeeApi('__your_api_key_here__', '__your_secret_key_here__')

response = api.contacts()

print(response.raw_data)

Exception handling

Every time something is not as it should be, like parameter is missing, parameter value is invalid, authentication fails, etc, API returns a http status code accordingly and an error message.
By using this client library, an error message is detected and taken, and an exception is raised, so you can handle it like this:

from sendbee_api import SendbeeRequestApiException

try:
    api.send_template_message(...)
except SendbeeRequestApiException as e:
    # handle exception

Authenticate webhook request

After activating your webhook URL in Sendbee Dashboard or with Subscribe to webhook, we will start sending requests on that URL depending on which webhook type is linked with that webhook URL.
Every request that we make will have authorization token in header, like this:

{
    ...
    'X-Authorization': '__auth_token_here__',
    ...
}

To authenticate requests that we make to your webhook URL, take this token from request header and check it using Sendbee API Client:

from sendbee_api import SendbeeApi

api = SendbeeApi('__your_api_key_here__', '__your_secret_key_here__')

token = '...'  # taken from the request header
if not api.auth.check_auth_token(token):
    # error! authentication failed!

Requests to a webhook URL that has its own secret key also carry a second header, X-Auth-Token, signed with that key instead of your account secret. You get the key back as webhook.secret_key from Subscribe to webhook. Check it the same way, using SendbeeAuth directly:

from sendbee_api import SendbeeAuth

secret_key = '...'  # webhook.secret_key, store it when you subscribe

token = '...'  # taken from the 'X-Auth-Token' request header
if not SendbeeAuth(secret_key).check_auth_token(token):
    # error! authentication failed!

Warnings

Sometimes APi returns a warning so you could be warned about something.
The waning is displayed in standard output:

Debugging

Debugging

This library has it's own internal debugging tool.
By default it is disabled, and to enable it, pass the debug parameter:

from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__', debug=True
)

Once you enabled the internal debug tool, every request to API will output various request and response data in standard output:

Debugging

Metadata

Release files for sendbee-api 1.8.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sendbee-api 1.8.1
File Size Uploaded
sendbee_api-1.8.1.tar.gz 38.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sendbee-api 1.8.1
File Interpreter ABI Platform
sendbee_api-1.8.1-py3-none-any.whl Python 3 none any Details

Total release size: 67.7 kB

Release files / sendbee_api-1.8.1.tar.gz

Download URL sendbee_api-1.8.1.tar.gz
Size 38.1 kB
Tags Source
SHA-256 checksum
How to use checksums
ff05062c672e8051b71fbc8323aedaa8a1a9a9c2f1ed9bec929a3ca749ebe10d
BLAKE2b-256 checksum
How to use checksums
96114de2d905e8cbcf7ed065cd341f9d314b366d61fad86301ed8f7fbe2b0df5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 20, 2026.

Transparency log

Release files / sendbee_api-1.8.1-py3-none-any.whl

Download URL sendbee_api-1.8.1-py3-none-any.whl
Size 29.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6c8e1024b2808a72e82a42c7397a629d8c7092f3e4fb36622e349df145dec0f8
BLAKE2b-256 checksum
How to use checksums
84ede7039df66a6386b0c0da6881d7d0736e5e3a180524b31245ba672ff2ab96
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 20, 2026.

Transparency log
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page