Official Sendly Python SDK for SMS messaging
Project description
sendly
Official Python SDK for the Sendly SMS API.
Installation
# pip
pip install sendly
# poetry
poetry add sendly
# pipenv
pipenv install sendly
Requirements
- Python 3.8+
- A Sendly API key (get one here)
Quick Start
from sendly import Sendly
# Initialize with your API key
client = Sendly('sk_live_v1_your_api_key')
# Send an SMS
message = client.messages.send(
to='+15551234567',
text='Hello from Sendly!'
)
print(f'Message sent: {message.id}')
print(f'Status: {message.status}')
Prerequisites for Live Messaging
Before sending live SMS messages, you need:
-
Business Verification - Complete verification in the Sendly dashboard
- International: Instant approval (just provide Sender ID)
- US/Canada: Requires carrier approval (3-7 business days)
-
Credits - Add credits to your account
- Test keys (
sk_test_*) work without credits (sandbox mode) - Live keys (
sk_live_*) require credits for each message
- Test keys (
-
Live API Key - Generate after verification + credits
- Dashboard → API Keys → Create Live Key
Test vs Live Keys
| Key Type | Prefix | Credits Required | Verification Required | Use Case |
|---|---|---|---|---|
| Test | sk_test_v1_* |
No | No | Development, testing |
| Live | sk_live_v1_* |
Yes | Yes | Production messaging |
Note: You can start development immediately with a test key. Messages to sandbox test numbers are free and don't require verification.
Features
- ✅ Full type hints (PEP 484)
- ✅ Sync and async clients
- ✅ Automatic retries with exponential backoff
- ✅ Rate limit handling
- ✅ Pydantic models for data validation
- ✅ Python 3.8+ support
Usage
Sending Messages
from sendly import Sendly
client = Sendly('sk_live_v1_xxx')
# Basic usage
message = client.messages.send(
to='+15551234567',
text='Your verification code is: 123456'
)
# With custom sender ID (international)
message = client.messages.send(
to='+447700900123',
text='Hello from MyApp!',
from_='MYAPP'
)
Listing Messages
# Get recent messages (default limit: 50)
result = client.messages.list()
print(f'Found {result.count} messages')
# Get last 10 messages
result = client.messages.list(limit=10)
# Iterate through messages
for msg in result.data:
print(f'{msg.to}: {msg.status}')
Getting a Message
message = client.messages.get('msg_xxx')
print(f'Status: {message.status}')
print(f'Delivered: {message.delivered_at}')
Rate Limit Information
# After any API call, check rate limit status
client.messages.send(to='+1555...', text='Hello!')
rate_limit = client.get_rate_limit_info()
if rate_limit:
print(f'{rate_limit.remaining}/{rate_limit.limit} requests remaining')
print(f'Resets in {rate_limit.reset} seconds')
Async Client
For async/await support, use AsyncSendly:
import asyncio
from sendly import AsyncSendly
async def main():
async with AsyncSendly('sk_live_v1_xxx') as client:
# Send a message
message = await client.messages.send(
to='+15551234567',
text='Hello from async!'
)
print(message.id)
# List messages
result = await client.messages.list(limit=10)
for msg in result.data:
print(f'{msg.to}: {msg.status}')
asyncio.run(main())
Configuration
from sendly import Sendly, SendlyConfig
# Using keyword arguments
client = Sendly(
api_key='sk_live_v1_xxx',
base_url='https://sendly.live/api', # Optional
timeout=60.0, # Optional: seconds (default: 30)
max_retries=5 # Optional: (default: 3)
)
# Using config object
config = SendlyConfig(
api_key='sk_live_v1_xxx',
timeout=60.0,
max_retries=5
)
client = Sendly(config=config)
Error Handling
The SDK provides typed exception classes:
from sendly import (
Sendly,
SendlyError,
AuthenticationError,
RateLimitError,
InsufficientCreditsError,
ValidationError,
NotFoundError,
)
client = Sendly('sk_live_v1_xxx')
try:
message = client.messages.send(
to='+15551234567',
text='Hello!'
)
except AuthenticationError as e:
print(f'Invalid API key: {e.message}')
except RateLimitError as e:
print(f'Rate limited. Retry after {e.retry_after} seconds')
except InsufficientCreditsError as e:
print(f'Need {e.credits_needed} credits, have {e.current_balance}')
except ValidationError as e:
print(f'Invalid request: {e.message}')
except NotFoundError as e:
print(f'Resource not found: {e.message}')
except SendlyError as e:
print(f'API error [{e.code}]: {e.message}')
Testing (Sandbox Mode)
Use a test API key (sk_test_v1_xxx) for testing:
from sendly import Sendly, SANDBOX_TEST_NUMBERS
client = Sendly('sk_test_v1_xxx')
# Check if in test mode
print(client.is_test_mode()) # True
# Use sandbox test numbers
message = client.messages.send(
to=SANDBOX_TEST_NUMBERS.SUCCESS, # +15550001234
text='Test message'
)
# Test error scenarios
message = client.messages.send(
to=SANDBOX_TEST_NUMBERS.INVALID, # +15550001001
text='This will fail'
)
Available Test Numbers
| Number | Behavior |
|---|---|
+15550001234 |
Instant success |
+15550001010 |
Success after 10s delay |
+15550001001 |
Fails: invalid_number |
+15550001002 |
Fails: carrier_rejected (2s delay) |
+15550001003 |
Fails: rate_limit_exceeded |
Pricing Tiers
from sendly import CREDITS_PER_SMS, SUPPORTED_COUNTRIES, PricingTier
# Credits per SMS by tier
print(CREDITS_PER_SMS[PricingTier.DOMESTIC]) # 1 (US/Canada)
print(CREDITS_PER_SMS[PricingTier.TIER1]) # 8 (UK, Poland, etc.)
print(CREDITS_PER_SMS[PricingTier.TIER2]) # 12 (France, Japan, etc.)
print(CREDITS_PER_SMS[PricingTier.TIER3]) # 16 (Germany, Italy, etc.)
# Supported countries by tier
print(SUPPORTED_COUNTRIES[PricingTier.DOMESTIC]) # ['US', 'CA']
print(SUPPORTED_COUNTRIES[PricingTier.TIER1]) # ['GB', 'PL', ...]
Utilities
The SDK exports validation utilities:
from sendly import (
validate_phone_number,
get_country_from_phone,
is_country_supported,
calculate_segments,
)
# Validate phone number format
validate_phone_number('+15551234567') # OK
validate_phone_number('555-1234') # Raises ValidationError
# Get country from phone number
get_country_from_phone('+447700900123') # 'GB'
get_country_from_phone('+15551234567') # 'US'
# Check if country is supported
is_country_supported('GB') # True
is_country_supported('XX') # False
# Calculate SMS segments
calculate_segments('Hello!') # 1
calculate_segments('A' * 200) # 2
Type Hints
The SDK is fully typed. Import types for your IDE:
from sendly import (
SendlyConfig,
SendMessageRequest,
Message,
MessageStatus,
ListMessagesOptions,
MessageListResponse,
RateLimitInfo,
PricingTier,
)
Context Manager
Both sync and async clients support context managers:
# Sync
with Sendly('sk_live_v1_xxx') as client:
message = client.messages.send(to='+1555...', text='Hello!')
# Async
async with AsyncSendly('sk_live_v1_xxx') as client:
message = await client.messages.send(to='+1555...', text='Hello!')
API Reference
Sendly / AsyncSendly
Constructor
Sendly(
api_key: str,
base_url: str = 'https://sendly.live/api',
timeout: float = 30.0,
max_retries: int = 3,
)
Properties
messages- Messages resourcebase_url- Configured base URL
Methods
is_test_mode()- ReturnsTrueif using a test API keyget_rate_limit_info()- Returns current rate limit infoclose()- Close the HTTP client
client.messages
send(to, text, from_=None) -> Message
Send an SMS message.
list(limit=None) -> MessageListResponse
List sent messages.
get(id) -> Message
Get a specific message by ID.
Support
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sendly-1.0.6.tar.gz.
File metadata
- Download URL: sendly-1.0.6.tar.gz
- Upload date:
- Size: 36.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2004ec6a4c056e4e1789aec62154fc07b1bb7def520b93eae13972da63a61cda
|
|
| MD5 |
9353b68e3ba2461879a9401134c7b54a
|
|
| BLAKE2b-256 |
c7684a0b56feb177fddafb00680df3ecfd45683872b0238af6d30664ebc4e6fd
|
File details
Details for the file sendly-1.0.6-py3-none-any.whl.
File metadata
- Download URL: sendly-1.0.6-py3-none-any.whl
- Upload date:
- Size: 21.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ce29216ed6357c6245de3536c91587f3bd84ef982f5e09a4d84981cbe6acee66
|
|
| MD5 |
c86f63040ec1d4aea4084aab024ad61b
|
|
| BLAKE2b-256 |
6168e1226bf04ef74037b2ca7cbabda22d62d940fe223d50a30cf8f673683165
|