Python client for the CPSMS.dk SMS Gateway API
Project description
CPSMS-client
A Python client for the CPSMS.dk SMS Gateway API with both synchronous and asynchronous support.
Features
- ✅ Full API coverage (SMS, Groups, Contacts, Logs)
- ✅ Both sync and async clients
- ✅ Type hints throughout
- ✅ Dataclasses for all response objects
- ✅ Custom exceptions for error handling
- ✅ Context manager support
Installation
pip install cpsms-client
Quick Start
Synchronous Usage
from cpsms import CPSMSClient
with CPSMSClient(username="your_username", api_key="your_api_key") as client:
# Send an SMS
result = client.send_sms(
to="4512345678",
message="Hello from Python!",
from_="MyApp"
)
print(f"Sent! Cost: {result.success[0].cost} points")
# Check your credit balance
credit = client.get_credit()
print(f"Remaining credit: {credit}")
Asynchronous Usage
import asyncio
from cpsms import AsyncCPSMSClient
async def main():
async with AsyncCPSMSClient(username="your_username", api_key="your_api_key") as client:
result = await client.send_sms(
to="4512345678",
message="Hello from async Python!",
from_="MyApp"
)
print(f"Sent! Cost: {result.success[0].cost} points")
asyncio.run(main())
Getting Your API Credentials
- Create an account at cpsms.dk (you get 10 free SMS points)
- Log in to your dashboard at cpsms.dk/login
- Navigate to INDSTILLINGER → API
- Generate your API key
API Reference
Sending SMS
from cpsms import CPSMSClient, SMSFormat
from datetime import datetime, timedelta
with CPSMSClient(username="user", api_key="key") as client:
# Simple send
result = client.send_sms(
to="4512345678",
message="Hello!",
from_="MyApp"
)
# Send to multiple recipients
result = client.send_sms(
to=["4512345678", "4587654321"],
message="Bulk message!",
from_="MyApp"
)
# Schedule for later
result = client.send_sms(
to="4512345678",
message="This arrives in 1 hour",
from_="MyApp",
timestamp=datetime.now() + timedelta(hours=1),
reference="my-ref-123" # Required if you want to cancel later
)
# Send with delivery report webhook
result = client.send_sms(
to="4512345678",
message="Track me!",
from_="MyApp",
dlr_url="https://myserver.com/webhook"
)
# Send Unicode (for emojis, Chinese characters, etc.)
result = client.send_sms(
to="4512345678",
message="你好 🎉",
from_="MyApp",
format_=SMSFormat.UNICODE
)
# Send flash SMS (appears directly on screen)
result = client.send_sms(
to="4512345678",
message="URGENT!",
from_="MyApp",
flash=True
)
Managing Groups
with CPSMSClient(username="user", api_key="key") as client:
# Create a group
group = client.create_group("VIP Customers")
print(f"Created group ID: {group.group_id}")
# List all groups
groups = client.list_groups()
for g in groups:
print(f"{g.group_name} (ID: {g.group_id})")
# Update group name
client.update_group(group_id=12345, group_name="Premium Customers")
# Delete group (must be empty)
client.delete_group(group_id=12345)
# Send SMS to entire group
result = client.send_to_group(
to_group=12345,
message="Hello VIP customers!",
from_="MyStore"
)
Managing Contacts
with CPSMSClient(username="user", api_key="key") as client:
# Add contact to a group
client.create_contact(
group_id=12345,
phone_number="4512345678",
contact_name="John Doe"
)
# List contacts in a group
contacts = client.list_contacts(group_id=12345)
for contact in contacts:
print(f"{contact.contact_name}: {contact.phone_number}")
# Update contact name
client.update_contact(
group_id=12345,
phone_number="4512345678",
contact_name="John Smith"
)
# Find which groups a contact belongs to
groups = client.list_group_membership("4512345678")
print(f"Contact is in {len(groups)} groups")
# Remove contact from group
client.delete_contact(group_id=12345, phone_number="4512345678")
Viewing Logs
from datetime import datetime, timedelta
with CPSMSClient(username="user", api_key="key") as client:
# Get recent log entries (up to 3 months back)
log = client.get_log()
for entry in log:
print(f"{entry.time_sent}: To {entry.to} - Status: {entry.dlr_status_text}")
# Filter by recipient
log = client.get_log(to="4512345678")
# Filter by date range
log = client.get_log(
from_date=datetime.now() - timedelta(days=7),
to_date=datetime.now()
)
Account Management
with CPSMSClient(username="user", api_key="key") as client:
# Check credit balance
credit = client.get_credit()
print(f"Balance: {credit}")
# Cancel a scheduled SMS (must be >10 min before send time)
client.delete_sms(reference="my-ref-123")
Error Handling
from cpsms import (
CPSMSClient,
CPSMSError,
AuthenticationError,
InsufficientCreditError,
BadRequestError,
)
with CPSMSClient(username="user", api_key="key") as client:
try:
result = client.send_sms(to="4512345678", message="Hello!", from_="App")
except AuthenticationError:
print("Invalid username or API key")
except InsufficientCreditError:
print("Not enough SMS credits - please top up!")
except BadRequestError as e:
print(f"Invalid request: {e.message}")
except CPSMSError as e:
print(f"API error {e.code}: {e.message}")
Exception Types
| Exception | HTTP Code | Description |
|---|---|---|
AuthenticationError |
401 | Invalid credentials |
InsufficientCreditError |
402 | Not enough SMS credits |
ForbiddenError |
403 | IP not whitelisted |
NotFoundError |
404 | Resource not found |
ConflictError |
409 | Operation conflict |
BadRequestError |
400 | Invalid parameters |
CPSMSError |
* | Base exception for all API errors |
Async Concurrent Requests
import asyncio
from cpsms import AsyncCPSMSClient
async def send_bulk():
async with AsyncCPSMSClient(username="user", api_key="key") as client:
# Send to multiple recipients concurrently
tasks = [
client.send_sms(to=num, message="Hello!", from_="MyApp")
for num in ["4512345678", "4587654321", "4599887766"]
]
results = await asyncio.gather(*tasks, return_exceptions=True)
for result in results:
if isinstance(result, Exception):
print(f"Error: {result}")
else:
for sms in result.success:
print(f"Sent to {sms.to}")
asyncio.run(send_bulk())
Response Objects
SendResponse
result = client.send_sms(to=["4512345678", "invalid"], message="Hi", from_="App")
# Successful sends
for sms in result.success:
print(f"To: {sms.to}, Cost: {sms.cost}, Segments: {sms.sms_amount}")
# Failed sends
for error in result.errors:
print(f"To: {error.to}, Error: {error.message} (code: {error.code})")
Group
group = client.create_group("My Group")
print(f"ID: {group.group_id}, Name: {group.group_name}")
Contact
contacts = client.list_contacts(group_id=12345)
for c in contacts:
print(f"Name: {c.contact_name}")
print(f"Phone: {c.phone_number}")
print(f"Added: {c.time_added}") # datetime object
LogEntry
log = client.get_log()
for entry in log:
print(f"To: {entry.to}")
print(f"From: {entry.from_}")
print(f"Segments: {entry.sms_amount}")
print(f"Cost: {entry.point_price}")
print(f"Reference: {entry.user_reference}")
print(f"Status: {entry.dlr_status} - {entry.dlr_status_text}")
print(f"Sent: {entry.time_sent}") # datetime object
SMS Limits
| Format | Single SMS | Multipart (per segment) |
|---|---|---|
| GSM | 160 chars | 153 chars |
| Unicode | 70 chars | 67 chars |
Maximum message length: 1530 characters (10 SMS segments joined)
Development
# Clone the repository
git clone https://github.com/jetdk/cpsms-client.git
<<<<<<< HEAD
cd cpsms-client
=======
cd cpsms
>>>>>>> 8cc22b3acf8430a6d0279ed57bee173b41907922
# Install dev dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Run type checking
mypy src/cpsms
# Run linting
ruff check src/cpsms-client
License
MIT License - see LICENSE for details.
Links
- CPSMS.dk - Official website
- API Documentation - Official API docs
- PyPI Package - Python Package Index
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 cpsms_client-1.0.0.tar.gz.
File metadata
- Download URL: cpsms_client-1.0.0.tar.gz
- Upload date:
- Size: 11.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
971a0a2eb2b10d50e5230a0c125616d1f5b64d50a7998ed5568bdbdbe143719f
|
|
| MD5 |
7cfe7803515cd486051196c146538946
|
|
| BLAKE2b-256 |
9e581ef0d5279568d30515e578ace7dc71f89cffaededfa3c2f9a4deadfc8089
|
Provenance
The following attestation bundles were made for cpsms_client-1.0.0.tar.gz:
Publisher:
release.yml on jetdk/cpsms-client
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cpsms_client-1.0.0.tar.gz -
Subject digest:
971a0a2eb2b10d50e5230a0c125616d1f5b64d50a7998ed5568bdbdbe143719f - Sigstore transparency entry: 743007133
- Sigstore integration time:
-
Permalink:
jetdk/cpsms-client@46927065dd48e013fb4ccc0a9ab898972ec48d90 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/jetdk
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@46927065dd48e013fb4ccc0a9ab898972ec48d90 -
Trigger Event:
push
-
Statement type:
File details
Details for the file cpsms_client-1.0.0-py3-none-any.whl.
File metadata
- Download URL: cpsms_client-1.0.0-py3-none-any.whl
- Upload date:
- Size: 12.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bdd9df706ea6c79528a235bdee057e62b8cb7356a68f3c53cb4e1cc86e5a74c6
|
|
| MD5 |
c133524049df82fd87e29758f118f3f0
|
|
| BLAKE2b-256 |
978d750cf9aff041320848a9684cd31062722dd10fefe0dcc73dc00ef7ca8f20
|
Provenance
The following attestation bundles were made for cpsms_client-1.0.0-py3-none-any.whl:
Publisher:
release.yml on jetdk/cpsms-client
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cpsms_client-1.0.0-py3-none-any.whl -
Subject digest:
bdd9df706ea6c79528a235bdee057e62b8cb7356a68f3c53cb4e1cc86e5a74c6 - Sigstore transparency entry: 743007136
- Sigstore integration time:
-
Permalink:
jetdk/cpsms-client@46927065dd48e013fb4ccc0a9ab898972ec48d90 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/jetdk
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@46927065dd48e013fb4ccc0a9ab898972ec48d90 -
Trigger Event:
push
-
Statement type: