A lightweight Python package for interacting with the Textbelt SMS API. Send SMS messages, check delivery status, and handle webhook responses with a clean, type-hinted interface.
- 🚀 Simple, intuitive API
- 📝 Type hints and dataclasses for better IDE support
- ✅ Webhook verification
- 🧪 Test mode support
- 🔐 One-Time Password (OTP) support
- 🏢 Custom sender name support
- 📨 Bulk SMS support with rate limiting
- ⚡ Async/sync clients for flexibility
- 0️⃣ Zero external dependencies beyond requests
pip install textbelt-utils
from textbelt_utils import TextbeltClient, SMSRequest
# Initialize client
client = TextbeltClient(api_key="your_api_key")
# Send an SMS
request = SMSRequest(
phone="+1234567890",
message="Hello from textbelt-utils!",
key="your_api_key"
)
response = client.send_sms(request)
print(f"Message sent! ID: {response.text_id}")
from textbelt_utils import TextbeltClient, SMSRequest
client = TextbeltClient(api_key="your_api_key")
# Basic SMS
request = SMSRequest(
phone="+1234567890",
message="Hello!",
key="your_api_key"
)
# SMS with webhook for replies
request_with_webhook = SMSRequest(
phone="+1234567890",
message="Reply to this message!",
key="your_api_key",
reply_webhook_url="https://your-site.com/webhook",
webhook_data="custom_data"
)
# SMS with custom sender name
request_with_sender = SMSRequest(
phone="+1234567890",
message="Message from your company!",
key="your_api_key",
sender="MyCompany" # Set a custom sender name for this message
)
response = client.send_sms(request)
Send multiple SMS messages efficiently with rate limiting and batching:
from textbelt_utils import TextbeltClient, BulkSMSRequest
client = TextbeltClient(api_key="your_api_key")
# Send same message to multiple recipients
request = BulkSMSRequest(
phones=["+1234567890", "+1987654321"],
message="Broadcast message to all recipients!",
batch_size=100, # Process in batches of 100
delay_between_messages=0.1 # 100ms delay between messages
)
# Or send individual messages to each recipient
request = BulkSMSRequest(
phones=["+1234567890", "+1987654321"],
individual_messages={
"+1234567890": "Custom message for recipient 1",
"+1987654321": "Different message for recipient 2"
},
batch_size=100,
delay_between_messages=0.1
)
response = client.send_bulk_sms(request)
print(f"Total messages: {response.total_messages}")
print(f"Successful: {response.successful_messages}")
print(f"Failed: {response.failed_messages}")
# Check individual results
for phone, result in response.results.items():
if result.text_id:
status = client.check_status(result.text_id)
print(f"{phone}: {status.status}")
Send messages concurrently with proper rate limiting:
from textbelt_utils import AsyncTextbeltClient, BulkSMSRequest
import asyncio
async def send_bulk():
async with AsyncTextbeltClient(api_key="your_api_key") as client:
request = BulkSMSRequest(
phones=["+1234567890", "+1987654321"],
message="Async bulk message!",
batch_size=100, # Process 100 messages concurrently
delay_between_messages=0.1
)
response = await client.send_bulk_sms(request)
print(f"Sent: {response.successful_messages}")
print(f"Failed: {response.failed_messages}")
asyncio.run(send_bulk())
You can set a sender name for your SMS messages in two ways:
- Account-wide: Set a default sender name in your Textbelt account settings at https://textbelt.com/account
- Per-message: Set the
sender
parameter in yourSMSRequest
The sender name is used for compliance purposes and helps recipients identify who sent the message. If you don't specify a sender name, Textbelt will automatically append your default sender name to the message (unless it already appears in the message content).
# Example with custom sender name
request = SMSRequest(
phone="+1234567890",
message="Important update!",
key="your_api_key",
sender="MyCompany" # This overrides your account's default sender name
)
Note: The sender name is used strictly for compliance purposes and does not override the "From" number for the SMS sender.
status = client.check_status("text_id")
print(f"Message status: {status.status}") # DELIVERED, SENT, SENDING, etc.
quota = client.check_quota()
print(f"Remaining messages: {quota.quota_remaining}")
# Send a test message (doesn't use quota)
response = client.send_test(request)
from textbelt_utils.utils import verify_webhook
is_valid = verify_webhook(
api_key="your_api_key",
timestamp="webhook_timestamp",
signature="webhook_signature",
payload="webhook_payload"
)
The package provides built-in support for generating and verifying one-time passwords:
from textbelt_utils import AsyncTextbeltClient, OTPGenerateRequest, OTPVerifyRequest
async def handle_otp():
async with AsyncTextbeltClient(api_key="your_api_key") as client:
# Generate and send OTP
generate_request = OTPGenerateRequest(
phone="+1234567890",
userid="[email protected]", # Unique identifier for your user
key="your_api_key",
message="Your verification code is $OTP", # Optional custom message
lifetime=180, # Optional validity duration in seconds (default: 180)
length=6 # Optional code length (default: 6)
)
response = await client.generate_otp(generate_request)
print(f"OTP sent! Message ID: {response.text_id}")
# Later, verify the OTP entered by the user
verify_request = OTPVerifyRequest(
otp="123456", # Code entered by user
userid="[email protected]", # Same userid used in generate
key="your_api_key"
)
verify_response = await client.verify_otp(verify_request)
if verify_response.is_valid_otp:
print("OTP verified successfully!")
else:
print("Invalid OTP")
- Custom Messages: Use the
$OTP
placeholder in your message to control where the code appears - Configurable Lifetime: Set how long the code remains valid (30-3600 seconds)
- Configurable Length: Choose the number of digits in the code (4-10 digits)
- No Extra Cost: OTP functionality is included in your regular SMS quota
- Automatic Cleanup: Invalid/expired codes are automatically cleaned up
- Input Validation: Built-in validation for phone numbers, message length, and code format
The package provides specific exceptions for different error cases:
from textbelt_utils.exceptions import (
QuotaExceededError,
InvalidRequestError,
WebhookVerificationError,
APIError
)
try:
response = client.send_sms(request)
except QuotaExceededError:
print("Out of quota!")
except InvalidRequestError as e:
print(f"Invalid request: {e}")
except WebhookVerificationError:
print("Webhook verification failed")
except APIError as e:
print(f"API error: {e}")
from textbelt_utils import AsyncTextbeltClient, SMSRequest
import asyncio
async def main():
async with AsyncTextbeltClient(api_key="your_api_key") as client:
# Send SMS
request = SMSRequest(
phone="+1234567890",
message="Async hello!",
key="your_api_key"
)
response = await client.send_sms(request)
# Check status
status = await client.check_status(response.text_id)
# Check quota
quota = await client.check_quota()
if __name__ == "__main__":
asyncio.run(main())
from textbelt_utils import TextbeltClient, AsyncTextbeltClient, SMSRequest
# Synchronous
sync_client = TextbeltClient(api_key="your_api_key")
sync_response = sync_client.send_sms(request)
# Asynchronous
async def send_async():
async with AsyncTextbeltClient(api_key="your_api_key") as client:
async_response = await client.send_sms(request)
- Copy the environment template:
cp .env.template .env
- Edit
.env
with your configuration:
# Textbelt API Configuration
TEXTBELT_API_KEY=your_api_key_here
# Test Phone Numbers (E.164 format)
TEXTBELT_TEST_PHONE=your_test_phone_here
TEXTBELT_TEST_PHONE2=your_second_test_phone_here
The package automatically loads environment variables from your .env
file on import. You can also explicitly load or reload configuration:
from textbelt_utils import load_config, get_env_var
# Load default .env file
load_config()
# Load specific env file
load_config(".env.test")
# Get environment variables with helpful error messages
api_key = get_env_var('TEXTBELT_API_KEY')
test_phone = get_env_var('TEXTBELT_TEST_PHONE')
# Get with default value
debug_mode = get_env_var('DEBUG', 'false')
For testing, you can use .env.test
which contains safe test values:
# Use test environment
cp .env.test .env
# Or specify a different env file in code
load_config(".env.test")
poetry run python -m unittest discover tests
The package includes test scripts in the scripts
directory to help you verify your Textbelt integration. To use them:
- Set up your environment variables:
export TEXTBELT_API_KEY=your_api_key_here
export TEXTBELT_TEST_PHONE=your_phone_number_here # E.164 format, e.g., +1234567890
- Run the test scripts:
# Test basic SMS
poetry run python scripts/test_send.py
# Test async SMS
poetry run python scripts/test_send_async.py
# Test bulk SMS
poetry run python scripts/test_bulk_send.py
The scripts will:
- Send test messages (using test mode, won't use your quota)
- Display message IDs and delivery status
- Show your remaining quota
The package also includes an OTP test script:
# Using environment variables
poetry run python scripts/test_otp.py
# Or provide values directly
poetry run python scripts/test_otp.py --phone +1234567890 --key your_api_key
- Never commit test scripts with actual phone numbers or API keys
- Always use environment variables for sensitive data
- Add test scripts to your
.gitignore
if you modify them with any sensitive data
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
- Add comprehensive webhook support
- Add webhook handler/router functionality
- Add webhook signature verification middleware
- Add example webhook handlers for common use cases
- Document webhook payload structure and events
- Add webhook testing utilities
- Add retry mechanism for failed API calls
- Add rate limiting configuration options
- Add logging configuration options
- Add support for scheduling messages
- Add support for message templates
- Add support for contact lists/groups
- Add message history tracking
- Add support for delivery reports
- Add support for analytics and reporting
- Add CLI tool for common operations