textbelt-utils
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.
Features
- 🚀 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
Installation
pip install textbelt-utils
Quick Start
from textbelt_utils import TextbeltClient, SMSRequest
client = TextbeltClient(api_key="your_api_key")
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}")
Features
Send SMS
from textbelt_utils import TextbeltClient, SMSRequest
client = TextbeltClient(api_key="your_api_key")
request = SMSRequest(
phone="+1234567890",
message="Hello!",
key="your_api_key"
)
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"
)
request_with_sender = SMSRequest(
phone="+1234567890",
message="Message from your company!",
key="your_api_key",
sender="MyCompany"
)
response = client.send_sms(request)
Bulk SMS
Send multiple SMS messages efficiently with rate limiting and batching:
from textbelt_utils import TextbeltClient, BulkSMSRequest
client = TextbeltClient(api_key="your_api_key")
request = BulkSMSRequest(
phones=["+1234567890", "+1987654321"],
message="Broadcast message to all recipients!",
batch_size=100,
delay_between_messages=0.1
)
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}")
for phone, result in response.results.items():
if result.text_id:
status = client.check_status(result.text_id)
print(f"{phone}: {status.status}")
Async Bulk SMS
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,
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())
Sender Name
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 your SMSRequest
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).
request = SMSRequest(
phone="+1234567890",
message="Important update!",
key="your_api_key",
sender="MyCompany"
)
Note: The sender name is used strictly for compliance purposes and does not override the "From" number for the SMS sender.
Check Message Status
status = client.check_status("text_id")
print(f"Message status: {status.status}")
Check Quota
quota = client.check_quota()
print(f"Remaining messages: {quota.quota_remaining}")
Test Mode
response = client.send_test(request)
Webhook Verification
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"
)
One-Time Password (OTP)
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_request = OTPGenerateRequest(
phone="+1234567890",
userid="user@example.com",
key="your_api_key",
message="Your verification code is $OTP",
lifetime=180,
length=6
)
response = await client.generate_otp(generate_request)
print(f"OTP sent! Message ID: {response.text_id}")
verify_request = OTPVerifyRequest(
otp="123456",
userid="user@example.com",
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")
OTP Features
- 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
Error Handling
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}")
Asynchronous Usage
from textbelt_utils import AsyncTextbeltClient, SMSRequest
import asyncio
async def main():
async with AsyncTextbeltClient(api_key="your_api_key") as client:
request = SMSRequest(
phone="+1234567890",
message="Async hello!",
key="your_api_key"
)
response = await client.send_sms(request)
status = await client.check_status(response.text_id)
quota = await client.check_quota()
if __name__ == "__main__":
asyncio.run(main())
Mixed Sync/Async Usage
from textbelt_utils import TextbeltClient, AsyncTextbeltClient, SMSRequest
sync_client = TextbeltClient(api_key="your_api_key")
sync_response = sync_client.send_sms(request)
async def send_async():
async with AsyncTextbeltClient(api_key="your_api_key") as client:
async_response = await client.send_sms(request)
Development
Environment Setup
- Copy the environment template:
cp .env.template .env
- Edit
.env
with your configuration:
TEXTBELT_API_KEY=your_api_key_here
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_config()
load_config(".env.test")
api_key = get_env_var('TEXTBELT_API_KEY')
test_phone = get_env_var('TEXTBELT_TEST_PHONE')
debug_mode = get_env_var('DEBUG', 'false')
For testing, you can use .env.test
which contains safe test values:
cp .env.test .env
load_config(".env.test")
Running Tests
poetry run python -m unittest discover tests
Testing Your Integration
Testing SMS
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
- Run the test scripts:
poetry run python scripts/test_send.py
poetry run python scripts/test_send_async.py
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
Testing OTP
The package also includes an OTP test script:
poetry run python scripts/test_otp.py
poetry run python scripts/test_otp.py --phone +1234567890 --key your_api_key
Security Note
- 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
Contributing
- 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
TODO
High Priority
Medium Priority
Low Priority