# BulkSMSRates Python SDK

Official Python client for the [BulkSMSRates](https://bulksmsrates.com) REST API.

## Requirements

- Python 3.8+
- `requests` library

## Installation

```bash
pip install requests
# Then copy bulksmsrates.py to your project (PyPI package coming soon)
```

Or install from source:

```bash
pip install requests
# Download bulksmsrates.py and import it directly
```

## Quick Start

```python
from bulksmsrates import BulkSMSRates

client = BulkSMSRates(
    api_key="bsms_live_...",
    api_secret="sk_live_..."
)

# Send a single SMS
result = client.send_sms("+447700900123", "Hello from BulkSMSRates!")
print(f"Message ID: {result['id']}, Status: {result['status']}")

# Check balance
balance = client.get_balance()
print(f"Balance: £{balance['balance'] / 100:.2f}")
```

## Authentication

### API Key + Secret (recommended for server-to-server)

```python
client = BulkSMSRates(
    api_key="bsms_live_abc123",
    api_secret="sk_live_xyz789"
)
```

### JWT Bearer Token

```python
client = BulkSMSRates(access_token="eyJhbGci...")

# Or login with email/password to get a token:
client = BulkSMSRates.__new__(BulkSMSRates)
# (use login method after construction)
```

## Sandbox Mode

Use `sandbox=True` to test without sending real messages:

```python
client = BulkSMSRates(
    api_key="bsms_test_...",
    api_secret="sk_test_...",
    sandbox=True
)

result = client.send_sms("+447700900123", "Test message")
print(result["id"])  # Returns a fake message ID
print(result.get("sandbox"))  # True

# Reset sandbox state
client.reset_sandbox()
```

## Send SMS

```python
# Single message
result = client.send_sms(
    destination="+447700900123",
    message="Hello {{name}}, your code is {{code}}!",
    sender_id="MyApp",
    variables={"name": "Alice", "code": "1234"},
    client_ref="order-123"
)

# Scheduled send
import datetime
scheduled = (datetime.datetime.utcnow() + datetime.timedelta(hours=1)).isoformat() + "Z"
result = client.send_sms(
    "+447700900123",
    "Reminder: your appointment is in 1 hour",
    scheduled_at=scheduled
)
```

## Bulk SMS

```python
recipients = [
    "+447700900001",
    "+447700900002",
    "+447700900003",
]

result = client.send_bulk(
    recipients=recipients,
    message="Hi, your order has shipped!",
    sender_id="MyShop"
)

print(f"Batch ID: {result['batch_id']}")
print(f"Accepted: {result['accepted']}, Rejected: {result['rejected']}")
```

## Campaigns

```python
# Create a campaign
campaign = client.create_campaign(
    name="Spring Sale 2026",
    message="Hi {{name}}, get 30% off this weekend!",
    sender_id="MyShop",
    scheduled_at="2026-03-15T09:00:00Z"
)
campaign_id = campaign["id"]

# Submit for sending
client.submit_campaign(campaign_id)

# Monitor progress
status = client.get_campaign(campaign_id)
print(f"Sent: {status['sent']}/{status['total']}")

# Pause / resume
client.pause_campaign(campaign_id)
client.resume_campaign(campaign_id)
```

## Contacts

```python
# Create a contact
contact = client.create_contact(
    phone="+447700900123",
    name="Alice Smith",
    email="alice@example.com",
    custom_fields={"tier": "VIP"}
)

# List contacts
contacts = client.list_contacts(page=1, per_page=50, search="Alice")

# Create a group and add members
group = client.create_group("VIP Customers")
client.add_group_members(group["id"], [contact["id"]])

# Tags
tag = client.create_tag("Newsletter", color="#0066cc")
```

## Billing

```python
# Get balance
balance = client.get_balance()
print(f"Available: £{balance['available'] / 100:.2f} {balance['currency']}")

# Transaction history
txns = client.list_transactions(page=1, per_page=20)
for t in txns["data"]:
    print(f"{t['type']}: £{t['amount'] / 100:.2f}")
```

## Error Handling

```python
from bulksmsrates import (
    BulkSMSRates,
    BulkSMSRatesError,
    RateLimitError,
    AuthError,
    InsufficientBalanceError,
    ValidationError,
)

client = BulkSMSRates(api_key="...", api_secret="...")

try:
    result = client.send_sms("+447700900123", "Hello!")
except InsufficientBalanceError as e:
    print(f"Top up required! {e.details}")
except RateLimitError as e:
    import time
    print(f"Rate limited. Retry in {e.retry_after}s")
    time.sleep(e.retry_after)
except AuthError as e:
    print(f"Auth failed: {e}")
except ValidationError as e:
    print(f"Invalid params: {e}")
except BulkSMSRatesError as e:
    print(f"API error {e.status_code}: {e}")
```

## Rate Limit Info

```python
result = client.send_sms("+447700900123", "Hello!")

print(f"Limit: {client.rate_limit.limit}")
print(f"Remaining: {client.rate_limit.remaining}")
print(f"Reset: {client.rate_limit.reset}")
```

## Context Manager

```python
with BulkSMSRates(api_key="...", api_secret="...") as client:
    result = client.send_sms("+447700900123", "Hello!")
# Session automatically closed
```

## Auto-Retry

The SDK automatically retries on `429 Rate Limited` and `5xx` errors with exponential backoff:

```python
# Retry up to 5 times with 2s base backoff
client = BulkSMSRates(
    api_key="...",
    api_secret="...",
    max_retries=5,
    retry_backoff=2.0
)

# Disable retries
client = BulkSMSRates(api_key="...", api_secret="...", max_retries=0)
```

## API Reference

| Method | Description |
|--------|-------------|
| `send_sms(destination, message, ...)` | Send a single SMS |
| `send_bulk(recipients, message, ...)` | Send to multiple recipients |
| `get_message(message_id)` | Get message status |
| `list_messages(page, per_page, status)` | List messages |
| `get_balance()` | Get wallet balance |
| `list_transactions(page, per_page)` | Transaction history |
| `create_campaign(name, message, ...)` | Create campaign |
| `list_campaigns()` | List campaigns |
| `submit_campaign(campaign_id)` | Submit campaign for sending |
| `pause_campaign(campaign_id)` | Pause campaign |
| `resume_campaign(campaign_id)` | Resume campaign |
| `cancel_campaign(campaign_id)` | Cancel campaign |
| `create_contact(phone, ...)` | Create contact |
| `list_contacts(page, per_page, ...)` | List contacts |
| `update_contact(contact_id, ...)` | Update contact |
| `delete_contact(contact_id)` | Delete contact |
| `create_group(name)` | Create contact group |
| `list_groups()` | List groups |
| `create_tag(name, color)` | Create tag |
| `me()` | Get current user profile |
| `login(email, password)` | Login and store token |
| `reset_sandbox()` | Reset sandbox state |

## License

MIT — see LICENSE file.

## Support

- 📧 support@bulksmsrates.com
- 📖 https://bulksmsrates.com/docs
- 🐛 Report issues on GitHub
