Ce tutoriel envoie un SMS vers un numéro tunisien depuis un script Python, avec la librairie requests et l’API REST Sendimo. Il fonctionne aussi dans une application Django, Flask ou FastAPI : il suffit de placer l’appel dans votre vue ou votre tâche de fond.
Ce qu’il vous faut
- Python 3.9 ou plus récent ;
- la librairie
requests:pip install requests; - une clé API Sendimo. Commencez par une clé de test
sk_test_: les réponses sont réalistes, mais rien n’est envoyé ni facturé.
Gardez la clé hors du code, dans une variable d’environnement :
export SENDIMO_API_KEY="sk_test_..."
1. Envoyer le SMS
Un envoi, c’est une requête POST /v1/messages avec le numéro au format international, le nom d’expéditeur et le texte.
import os
import requests
response = requests.post(
"https://api.sendimo.tn/v1/messages",
headers={
"Authorization": f"Bearer {os.environ['SENDIMO_API_KEY']}",
"Idempotency-Key": "order-4821-otp",
},
json={
"to": "+21620123456",
"from": "SENDIMO",
"text": "Votre code est 482913. Il expire dans 5 minutes.",
"reference": "order-4821",
},
timeout=10,
)
response.raise_for_status() # 4xx/5xx -> exception
message = response.json()
print(message["id"], message["status"]) # msg_01HF8Z3K9QX2 queued
L’API répond 202 Accepted : le message est accepté et mis en file d’envoi.
{
"id": "msg_01HF8Z3K9QX2",
"status": "queued",
"to": "+21620123456",
"from": "SENDIMO",
"encoding": "GSM-7",
"segments": 1,
"network": "ooredoo",
"cost": { "amount": "0.140", "currency": "TND" },
"reference": "order-4821"
}
Trois champs méritent votre attention :
to: toujours au format E.164, avec+216;from: un nom d’expéditeur approuvé sur votre compte ;Idempotency-Key: si votre script réessaie après une coupure réseau, la même clé garantit que le SMS ne part qu’une fois.
2. Tester sans rien envoyer
Avec une clé sk_test_, deux numéros simulent les deux issues possibles :
+21600000001finit toujours endelivered;+21600000002finit toujours enfailed.
Vous pouvez ainsi tester tout votre parcours, y compris les erreurs, avant de passer à une clé sk_live_.
3. Gérer les erreurs
Toutes les erreurs ont le même format, avec un code stable :
try:
response.raise_for_status()
except requests.HTTPError:
error = response.json()["error"]
if error["code"] == "insufficient_balance":
print("Solde insuffisant : rechargez votre compte.")
elif error["code"] == "invalid_phone_number":
print("Numéro invalide :", error["message"])
else:
print(error["code"], error["request_id"])
Les codes les plus fréquents :
| HTTP | Code | Cause |
|---|---|---|
| 400 | invalid_phone_number |
to n’est pas un numéro valide |
| 401 | unauthorized |
Clé API invalide, absente ou révoquée |
| 402 | insufficient_balance |
Solde insuffisant pour l’envoi |
| 403 | sender_not_approved |
from n’est pas un nom d’expéditeur approuvé |
| 429 | rate_limited |
Trop de requêtes ; attendez la durée de Retry-After |
En cas d’erreur 500 ou 503, réessayez avec la même Idempotency-Key.
4. Suivre la livraison
Le statut passe de queued à sent, puis à delivered ou failed. Deux façons de le suivre :
- interroger
GET /v1/messages/{id}; - mieux : recevoir l’événement
message.deliveredoumessage.failedsur votre webhook, sans interroger l’API.
Combien ça coûte ?
Ce SMS fait 48 caractères en GSM-7 : un segment, facturé au tarif de votre offre, de 0,100 DT à 0,150 DT. Le détail est dans notre article sur le prix d’un SMS en Tunisie.
Pour aller plus loin
Abonnez-vous pour recevoir chaque jour nos analyses et les actualités de l’entreprise.