Aller au contenu principal

SDK Python

Intégrez facilement ZimSend dans vos applications Python avec notre wrapper API.

Installation

Installez la bibliothèque requests :

pip install requests

Classe Wrapper Complète

Créez un fichier zimsend.py :

import requests
from typing import List, Dict, Optional, Any

class ZimSend:
def __init__(self, api_key: str, client_id: str, device_id: Optional[str] = None):
self.api_key = api_key
self.client_id = client_id
self.device_id = device_id
self.base_url = 'https://api.zimsend.com/v1'

def _get_headers(self, include_device: bool = True) -> Dict[str, str]:
"""Génère les headers d'authentification"""
headers = {
'Content-Type': 'application/json',
'X-API-Key': self.api_key,
'X-Client-ID': self.client_id
}

if include_device and self.device_id:
headers['X-Device-ID'] = self.device_id

return headers

def _handle_error(self, response: requests.Response) -> Dict[str, Any]:
"""Gestion des erreurs API"""
try:
return response.json()
except:
return {
'success': False,
'error': {
'code': 'UNKNOWN_ERROR',
'message': response.text or 'Une erreur est survenue'
}
}

def send_sms(self, to: str, message: str, **options) -> Dict[str, Any]:
"""
Envoie un SMS simple

Args:
to: Numéro du destinataire (format international)
message: Contenu du SMS
**options: Options supplémentaires (priority, webhook_url, metadata, scheduled_at)

Returns:
Réponse de l'API
"""
url = f'{self.base_url}/sms/send'
data = {
'to': to,
'message': message,
**options
}

try:
response = requests.post(url, json=data, headers=self._get_headers())
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
if hasattr(e, 'response') and e.response is not None:
return self._handle_error(e.response)
return {
'success': False,
'error': {
'code': 'NETWORK_ERROR',
'message': str(e)
}
}

def send_bulk_sms(self, recipients: List[str], message: str, **options) -> Dict[str, Any]:
"""
Envoie des SMS en masse

Args:
recipients: Liste de numéros de destinataires
message: Contenu du SMS
**options: Options supplémentaires (priority, webhook_url, metadata)

Returns:
Réponse de l'API
"""
url = f'{self.base_url}/sms/send-bulk'
data = {
'recipients': recipients,
'message': message,
**options
}

try:
response = requests.post(url, json=data, headers=self._get_headers())
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
if hasattr(e, 'response') and e.response is not None:
return self._handle_error(e.response)
return {
'success': False,
'error': {
'code': 'NETWORK_ERROR',
'message': str(e)
}
}

def send_bulk_personalized_sms(self, messages: List[Dict[str, Any]]) -> Dict[str, Any]:
"""
Envoie des SMS personnalisés en masse

Args:
messages: Liste de messages avec 'to' et 'message'

Returns:
Réponse de l'API
"""
url = f'{self.base_url}/sms/send-bulk-personalized'
data = {'messages': messages}

try:
response = requests.post(url, json=data, headers=self._get_headers())
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
if hasattr(e, 'response') and e.response is not None:
return self._handle_error(e.response)
return {
'success': False,
'error': {
'code': 'NETWORK_ERROR',
'message': str(e)
}
}

def send_otp(self, to: str, **options) -> Dict[str, Any]:
"""
Génère et envoie un code OTP

Args:
to: Numéro du destinataire
**options: Options (length, expiry, template, metadata)

Returns:
Réponse de l'API avec otp_id
"""
url = f'{self.base_url}/sms/send-otp'
data = {
'to': to,
'length': options.get('length', 6),
'expiry': options.get('expiry', 300),
'template': options.get('template'),
'metadata': options.get('metadata')
}

try:
response = requests.post(url, json=data, headers=self._get_headers())
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
if hasattr(e, 'response') and e.response is not None:
return self._handle_error(e.response)
return {
'success': False,
'error': {
'code': 'NETWORK_ERROR',
'message': str(e)
}
}

def verify_otp(self, otp_id: str, code: str) -> Dict[str, Any]:
"""
Vérifie un code OTP

Args:
otp_id: ID de l'OTP
code: Code saisi par l'utilisateur

Returns:
Réponse de l'API avec valid: True/False
"""
url = f'{self.base_url}/sms/verify-otp'
data = {
'otp_id': otp_id,
'code': code
}

try:
response = requests.post(url, json=data, headers=self._get_headers(False))
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
if hasattr(e, 'response') and e.response is not None:
return self._handle_error(e.response)
return {
'success': False,
'error': {
'code': 'NETWORK_ERROR',
'message': str(e)
}
}

def get_status(self, message_id: str) -> Dict[str, Any]:
"""
Obtient le statut d'un SMS

Args:
message_id: ID du message

Returns:
Réponse de l'API avec le statut
"""
url = f'{self.base_url}/sms/status/{message_id}'

try:
response = requests.get(url, headers=self._get_headers(False))
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
if hasattr(e, 'response') and e.response is not None:
return self._handle_error(e.response)
return {
'success': False,
'error': {
'code': 'NETWORK_ERROR',
'message': str(e)
}
}

def get_history(self, **filters) -> Dict[str, Any]:
"""
Récupère l'historique des SMS

Args:
**filters: Filtres (page, limit, status, to, from_date, to_date, etc.)

Returns:
Réponse de l'API avec la liste des SMS
"""
url = f'{self.base_url}/sms/history'

try:
response = requests.get(url, params=filters, headers=self._get_headers(False))
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
if hasattr(e, 'response') and e.response is not None:
return self._handle_error(e.response)
return {
'success': False,
'error': {
'code': 'NETWORK_ERROR',
'message': str(e)
}
}

def cancel_message(self, message_id: str) -> Dict[str, Any]:
"""
Annule un SMS programmé

Args:
message_id: ID du message à annuler

Returns:
Réponse de l'API
"""
url = f'{self.base_url}/sms/{message_id}'

try:
response = requests.delete(url, headers=self._get_headers(False))
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
if hasattr(e, 'response') and e.response is not None:
return self._handle_error(e.response)
return {
'success': False,
'error': {
'code': 'NETWORK_ERROR',
'message': str(e)
}
}

def get_stats(self, from_date: str, to_date: str, device_id: Optional[str] = None) -> Dict[str, Any]:
"""
Obtient les statistiques d'envoi

Args:
from_date: Date de début (ISO 8601)
to_date: Date de fin (ISO 8601)
device_id: ID du device (optionnel)

Returns:
Réponse de l'API avec les statistiques
"""
url = f'{self.base_url}/sms/stats'
params = {
'from_date': from_date,
'to_date': to_date
}

if device_id:
params['device_id'] = device_id

try:
response = requests.get(url, params=params, headers=self._get_headers(False))
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
if hasattr(e, 'response') and e.response is not None:
return self._handle_error(e.response)
return {
'success': False,
'error': {
'code': 'NETWORK_ERROR',
'message': str(e)
}
}

Utilisation

Configuration

import os
from zimsend import ZimSend

# Initialisation avec vos credentials
zimsend = ZimSend(
api_key=os.getenv('ZIMSEND_API_KEY'),
client_id=os.getenv('ZIMSEND_CLIENT_ID'),
device_id=os.getenv('ZIMSEND_DEVICE_ID')
)

Envoyer un SMS simple

def send_simple_sms():
result = zimsend.send_sms(
to='+33612345678',
message='Bonjour ! Ceci est un message de test.'
)

if result['success']:
print(f"SMS envoyé: {result['message_id']}")
print(f"Statut: {result['status']}")
else:
print(f"Erreur: {result['error']['message']}")

send_simple_sms()

Envoyer avec options avancées

def send_advanced_sms():
result = zimsend.send_sms(
to='+33612345678',
message='Votre code de vérification : 123456',
priority=1,
webhook_url='https://monapp.com/webhooks/zimsend',
metadata={
'user_id': 'user_123',
'type': 'verification'
}
)

print(f"Résultat: {result}")

send_advanced_sms()

Envoyer en masse

def send_bulk_sms():
recipients = [
'+33612345678',
'+33698765432',
'+33687654321'
]

result = zimsend.send_bulk_sms(
recipients=recipients,
message='Offre spéciale : -20% sur tout le site ce week-end !'
)

if result['success']:
print(f"Total envoyé: {result['total_recipients']}")
for msg in result['messages']:
print(f" - {msg['to']}: {msg['status']}")
else:
print(f"Erreur: {result['error']['message']}")

send_bulk_sms()

Système OTP

def authenticate_with_otp():
phone_number = '+33612345678'

# Étape 1: Envoyer un code OTP
otp_result = zimsend.send_otp(
to=phone_number,
length=6,
expiry=300, # 5 minutes
template='Votre code MonApp : {{code}}. Valide 5 minutes.'
)

if not otp_result['success']:
print(f"Erreur envoi OTP: {otp_result['error']['message']}")
return

otp_id = otp_result['otp_id']
print(f"OTP envoyé. Expire à: {otp_result['expires_at']}")

# Étape 2: L'utilisateur saisit le code
user_code = input('Entrez le code reçu: ')

# Étape 3: Vérifier le code
verify_result = zimsend.verify_otp(otp_id, user_code)

if verify_result.get('valid'):
print('✓ Code correct ! Utilisateur authentifié.')
return True
else:
print(f'✗ Code incorrect: {verify_result.get("message")}')
print(f'Tentatives restantes: {verify_result.get("attempts_remaining")}')
return False

authenticate_with_otp()

Suivre les SMS

def track_sms():
# Obtenir le statut d'un SMS
status = zimsend.get_status('msg_1234567890abcdef')

if status['success']:
print(f"Statut: {status['status']}")
print(f"Destinataire: {status['to']}")
if status.get('delivered_at'):
print(f"Délivré à: {status['delivered_at']}")
else:
print(f"Erreur: {status['error']['message']}")

# Obtenir l'historique
history = zimsend.get_history(
status='delivered',
limit=50,
page=1
)

if history['success']:
print(f"\nSMS délivrés: {len(history['data'])}")
print(f"Total: {history['pagination']['total_items']}")

for sms in history['data']:
print(f" - {sms['to']}: {sms['message'][:50]}...")
else:
print(f"Erreur: {history['error']['message']}")

# Obtenir les statistiques
stats = zimsend.get_stats(
from_date='2025-01-01T00:00:00Z',
to_date='2025-01-31T23:59:59Z'
)

if stats['success']:
print(f"\nStatistiques du mois:")
print(f"Total envoyés: {stats['stats']['total_sent']}")
print(f"Délivrés: {stats['stats']['delivered']}")
print(f"Échoués: {stats['stats']['failed']}")
print(f"Taux de délivrance: {stats['stats']['delivery_rate']}%")
else:
print(f"Erreur: {stats['error']['message']}")

track_sms()

Intégration Flask

Exemple d'API REST avec Flask :

from flask import Flask, request, jsonify, session
from zimsend import ZimSend
import os

app = Flask(__name__)
app.secret_key = 'votre_secret_key'

zimsend = ZimSend(
api_key=os.getenv('ZIMSEND_API_KEY'),
client_id=os.getenv('ZIMSEND_CLIENT_ID'),
device_id=os.getenv('ZIMSEND_DEVICE_ID')
)

@app.route('/api/sms/send', methods=['POST'])
def send_sms():
"""Endpoint pour envoyer un SMS"""
data = request.json
to = data.get('to')
message = data.get('message')

if not to or not message:
return jsonify({'error': 'Paramètres manquants'}), 400

result = zimsend.send_sms(to, message)
return jsonify(result)

@app.route('/api/otp/send', methods=['POST'])
def send_otp():
"""Endpoint pour envoyer un OTP"""
data = request.json
phone_number = data.get('phoneNumber')

if not phone_number:
return jsonify({'error': 'Numéro de téléphone manquant'}), 400

result = zimsend.send_otp(
to=phone_number,
template='Votre code de vérification : {{code}}'
)

if result['success']:
# Stocker l'otp_id en session
session['otp_id'] = result['otp_id']

return jsonify({
'success': True,
'expires_at': result['expires_at']
})
else:
return jsonify(result), 400

@app.route('/api/otp/verify', methods=['POST'])
def verify_otp():
"""Endpoint pour vérifier un OTP"""
data = request.json
code = data.get('code')
otp_id = session.get('otp_id')

if not otp_id:
return jsonify({'error': 'Aucun OTP en attente'}), 400

if not code:
return jsonify({'error': 'Code manquant'}), 400

result = zimsend.verify_otp(otp_id, code)

if result.get('valid'):
# Authentifier l'utilisateur
session['authenticated'] = True
session.pop('otp_id', None)

return jsonify({
'success': True,
'message': 'Code vérifié'
})
else:
return jsonify({
'success': False,
'message': result.get('message'),
'attempts_remaining': result.get('attempts_remaining')
}), 400

@app.route('/api/sms/history', methods=['GET'])
def get_history():
"""Endpoint pour obtenir l'historique"""
filters = {
'page': request.args.get('page', 1, type=int),
'limit': request.args.get('limit', 50, type=int),
'status': request.args.get('status')
}

# Supprimer les paramètres None
filters = {k: v for k, v in filters.items() if v is not None}

result = zimsend.get_history(**filters)
return jsonify(result)

@app.route('/api/sms/status/<message_id>', methods=['GET'])
def get_status(message_id):
"""Endpoint pour obtenir le statut d'un SMS"""
result = zimsend.get_status(message_id)
return jsonify(result)

if __name__ == '__main__':
app.run(debug=True)

Intégration Django

Exemple avec Django :

# views.py
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
import json
from zimsend import ZimSend
import os

zimsend = ZimSend(
api_key=os.getenv('ZIMSEND_API_KEY'),
client_id=os.getenv('ZIMSEND_CLIENT_ID'),
device_id=os.getenv('ZIMSEND_DEVICE_ID')
)

@csrf_exempt
@require_http_methods(["POST"])
def send_sms(request):
"""Vue pour envoyer un SMS"""
try:
data = json.loads(request.body)
to = data.get('to')
message = data.get('message')

if not to or not message:
return JsonResponse({'error': 'Paramètres manquants'}, status=400)

result = zimsend.send_sms(to, message)
return JsonResponse(result)
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)

@csrf_exempt
@require_http_methods(["POST"])
def send_otp(request):
"""Vue pour envoyer un OTP"""
try:
data = json.loads(request.body)
phone_number = data.get('phoneNumber')

if not phone_number:
return JsonResponse({'error': 'Numéro manquant'}, status=400)

result = zimsend.send_otp(
to=phone_number,
template='Votre code : {{code}}'
)

if result['success']:
# Stocker l'otp_id en session
request.session['otp_id'] = result['otp_id']

return JsonResponse({
'success': True,
'expires_at': result['expires_at']
})
else:
return JsonResponse(result, status=400)
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)

@csrf_exempt
@require_http_methods(["POST"])
def verify_otp(request):
"""Vue pour vérifier un OTP"""
try:
data = json.loads(request.body)
code = data.get('code')
otp_id = request.session.get('otp_id')

if not otp_id:
return JsonResponse({'error': 'Aucun OTP en attente'}, status=400)

result = zimsend.verify_otp(otp_id, code)

if result.get('valid'):
request.session['authenticated'] = True
del request.session['otp_id']

return JsonResponse({
'success': True,
'message': 'Code vérifié'
})
else:
return JsonResponse({
'success': False,
'message': result.get('message'),
'attempts_remaining': result.get('attempts_remaining')
}, status=400)
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)

@require_http_methods(["GET"])
def get_history(request):
"""Vue pour obtenir l'historique"""
filters = {
'page': request.GET.get('page', 1),
'limit': request.GET.get('limit', 50),
'status': request.GET.get('status')
}

# Supprimer les paramètres None
filters = {k: v for k, v in filters.items() if v is not None}

result = zimsend.get_history(**filters)
return JsonResponse(result)

Prochaines étapes