Comment faire Envoyer un e-mail via l'API en Python (Tutoriel Rapide)
Évitez la mise en forme standard SMTP. Ce guide vous montre comment utiliser le API unifiée de messagerie Unipile envoyer un e-mail en Python - avec des exemples de copier-coller pour Gmail, Outlook et IMAP en utilisant le demandes bibliothèque.
import requiert, os
CLÉ_API = os.environ['UNIPILE_API_KEY']
DSN = os.environ['UNIPILE_DSN']
ID_COMPTE = os.environ['UNIPILE_ACCOUNT_ID']
response = requests.poste(
f'{DSN}/api/v1/emails',
en-têtes={'X-API-KEY': CLÉ_API},
données={
'identifiant_de_compte': ID_COMPTE,
'à': '[{"display_name":"Alice","identifier":"alice@acme.com"}]',
'sujet': 'Bonjour depuis Python',
'corps': 'Envoyé via Unipile !
'
}
)
print(réponse.json())Exemple Python en 5 lignes
Si vous savez déjà ce qu'est une API d'envoi d'e-mails et je veux juste le code Python de l'API d'e-mail qui fonctionne réellement, le voici. Le tutoriel complet suit ci-dessous.
pip install requests python-dotenvUNIPILE_DSN, CLÉ_API_UNIPILE, ou encore ID_COMPTE_UNIPILE à vous .env fichier./api/v1/emailsaccount_id, à, sujet, ou encore corps. Fait.import requiert, os
from dotenv import charger_dotenv
charger_dotenv()
CLÉ_API = os.environ['UNIPILE_API_KEY']
DSN = os.environ['UNIPILE_DSN']
ID_COMPTE = os.environ['UNIPILE_ACCOUNT_ID']
resp = requests.poste(
f'{DSN}/api/v1/emails',
en-têtes={'X-API-KEY': CLÉ_API},
données={
'identifiant_de_compte': ID_COMPTE,
'à': '[{"display_name":"Alice","identifier":"alice@acme.com"}]',
'sujet': 'Bonjour depuis Python',
'corps': 'Envoyé via Unipile !
'
}
)
print(respectivement.json()) # {'tracking_id': 'msg_...'}Prérequis et installation
Avant de pouvoir utiliser le flux de travail Python de l'API d'e-mail en production, vous avez besoin de quatre éléments : Python 3.9+, le demandes bibliothèque, une clé API avec DSN et un compte de messagerie lié.
| types d'union, et des fonctionnalités de la bibliothèque standard à partir de la version 3.9+. Python 3.11 LTS est recommandé pour la production. Vérifiez votre version avec python --version.api4.unipile.com:13444Les deux sont requis dans chaque en-tête de requête.python -m venv .venv && source .venv/bin/activate. N'installez jamais de packages dans le Python système - ceci est particulièrement important pour la gestion des identifiants.pip install requests python-dotenv
# Facultatif : prise en charge asynchrone
pip install aiohttp httpx
# Facultatif : logique de nouvelle tentative
pip installer ténacitépipenv installer requests python-dotenv tenacitypoésie ajouter requests python-dotenv persévérance# Identifiants Unipile - ne jamais valider ce fichier
UNIPILE_DSN=https://api4.unipile.com:13444
CLÉ_API_UNIPILE=ton_jeton_d'accès_ici
# L'identifiant du compte de messagerie lié
ID_COMPTE_UNIPILE=acc_xxxxxxxxxxxxxxxxAjouter .env à vous .gitignore. Charger avec python-dotenv via charger_dotenv() en haut de votre script. En production, privilégiez les variables d'environnement réelles injectées par votre plateforme de déploiement (Heroku, Railway, Docker Compose).
Connexion de votre premier compte de messagerie
Avant de pouvoir envoyer, vous devez lier un compte de messagerie à Unipile. C'est une étape unique par compte. Voir le complet guide d'intégration API d'email unifiée pour en savoir plus sur les flux multi-comptes.
Unipile utilise un assistant d'authentification hébergé - votre script Python génère un lien d'authentification, l'utilisateur clique dessus et termine l'OAuth dans le navigateur, puis Unipile appelle votre webhook avec le nouveau account_id. Aucune information d'identification SMTP n'est stockée dans votre code pour Gmail ou Outlook.
import requiert, os
from dotenv import charger_dotenv
charger_dotenv()
CLÉ_API = os.environ['UNIPILE_API_KEY']
DSN = os.environ['UNIPILE_DSN']
# Étape 1 : créer un lien d'authentification hébergé pour l'authentification OAuth Gmail
resp = requests.poste(
f'{DSN}/api/v1/hébergé/comptes/lier',
en-têtes={'X-API-KEY': CLÉ_API},
données={
'type': 'Google',
'nom': 'Alice Gmail',
'URL de succès': 'https://yourapp.com/oauth/success',
'url_échec': 'https://yourapp.com/oauth/failure'
}
)
# Étape 2 : envoyez cette URL à votre utilisateur
auth_url = resp.json()['url']
print(Rediriger l'utilisateur vers : {auth_url}')
# Étape 3 : Unipile publie {account_id} sur votre webhook après l'authentification OAuth
# Voir /gmail-api-send-email-a-comprehensive-guide-for-developers/ pour les détails sur Gmailimport requiert, os
from dotenv import charger_dotenv
charger_dotenv()
# Outlook OAuth - couvre Outlook personnel + Microsoft 365
# Voir /microsoft-graph-api-email-integration-guide/
resp = requests.poste(
f'{os.environ["UNIPILE_DSN"]}/api/v1/hosted/accounts/link',
en-têtes={'X-API-KEY': os.environ'UNIPILE_API_KEY']},
données={
'type': 'MICROSOFT',
'nom': 'Bob Outlook',
'URL de succès': 'https://yourapp.com/oauth/success',
'url_échec': 'https://yourapp.com/oauth/failure'
}
)
print(respectivement.json()['url'])import requêtes, os, json
# IMAP : transmettez les identifiants SMTP/IMAP directement (pas de redirection OAuth nécessaire)
# Voir /the-developers-guide-to-imap-api-solution/ pour les détails complets de l'API IMAP
resp = requests.poste(
f'{os.environ["UNIPILE_DSN"]}/api/v1/comptes',
en-têtes={'X-API-KEY': os.environ'UNIPILE_API_KEY']},
json={
'fournisseur': 'IMAP',
'nom d'utilisateur': 'alice@company.com',
'mot de passe': 'mot_de_passe_application_ici',
'imap_hote': 'imap.company.com',
'smtp_host': 'smtp.company.com'
}
)
compte_id = resp.json()['identifiant_de_compte']
print(Compte lié : {account_id}')Envoyer votre premier courriel depuis Python
Le point d'envoi accepte multipart/form-data. utiliser données= (pas json=) dans requêtes.post(). Le à, cc, ou encore CCI les champs sont des chaînes de caractères encodées en JSON à l'intérieur des données du formulaire.
import requêtes, os, json
requêtes.poste(
f'{os.environ["UNIPILE_DSN"]}/api/v1/emails',
en-têtes={'X-API-KEY': os.environ'UNIPILE_API_KEY']},
données={
'identifiant_de_compte': os.environ'UNIPILE_ACCOUNT_ID'],
'à': json.vidages([{'nom_affichage': 'Alice', 'identifier': 'alice@acme.com'}]),
'sujet': 'Mise à jour rapide',
'corps': 'Bonjour Alice, je voulais juste prendre des nouvelles.'
}
)corps Le champ accepte du texte brut et du HTML. Utiliser balises pour la mise en forme HTML.import requêtes, os, json
response = requests.poste(
f'{os.environ["UNIPILE_DSN"]}/api/v1/emails',
en-têtes={'X-API-KEY': os.environ'UNIPILE_API_KEY']},
données={
'identifiant_de_compte': os.environ'UNIPILE_ACCOUNT_ID'],
'à'json.vidages([{'identifier': 'alice@acme.com'}]),
'salut'json.vidages([{'identifier': 'manager@acme.com'}]),
'CCI': json.vidages([{'identifier': 'crm@yourapp.com'}]),
'sujet': 'Votre facture est prête',
'corps': 'Facture #1042
Veuillez trouver votre facture ci-jointe.
'
}
)
# 202 Accepté = mis en file d'attente pour livraison
print(response.code_statut, response.json())import requêtes, os, json
déf envoyer_emailÀ_e-mail : chaîne, sujet : chaîne, corps : chaîne) -> dictionnaire:
"Envoyer un e-mail via le wrapper Python de l'API e-mail d'Unipile."
response = requests.poste(
f'{os.environ["UNIPILE_DSN"]}/api/v1/emails',
en-têtes={'X-API-KEY': os.environ'UNIPILE_API_KEY']},
données={
'identifiant_de_compte': os.environ'UNIPILE_ACCOUNT_ID'],
'à': json.vidages([{'identifier': to_email}]),
'sujet': sujet,
'corps'corps,
},
délai d'attente=30
)
réponse.lancer_statut() # déclenche une HTTPError sur 4xx/5xx
return réponse.json() # {'tracking_id': 'msg_...'}délai d'attente=30 pour éviter de rester bloqué indéfiniment sur des problèmes réseau. Utiliser lancer_pour_statut() remonter les erreurs HTTP en exceptions Python.Obtenez votre clé API, liez un compte Gmail ou Outlook en quelques minutes, et exécutez les exemples Python de ce guide sur des boîtes aux lettres réelles.
Envoi de pièces jointes en Python
Les pièces jointes sont envoyées dans le cadre de données de formulaire multipart en utilisant Python. fichiers= paramètre. Ouvrez le fichier en mode binaire ('rb') - octets, pas chaînes de caractères.
import requêtes, os, json
# Pièce jointe unique
avec ouvrir('facture.pdf', 'rb') En tant que F
resp = requests.poste(
f'{os.environ["UNIPILE_DSN"]}/api/v1/emails',
en-têtes={'X-API-KEY': os.environ'UNIPILE_API_KEY']},
données={
'identifiant_de_compte': os.environ'UNIPILE_ACCOUNT_ID'],
'à': json.vidages([{'identifier': 'client@example.com'}]),
'sujet': 'Facture ci-jointe',
'corps': 'Veuillez consulter la facture ci-jointe.
'
},
fichiers={'pièces jointes': ('facture.pdf', f, 'application/pdf')}
)
# Pièces jointes multiples : passer une liste de tuples
# fichiers=[('pièces jointes', ('a.pdf', f1, 'application/pdf')),
# ('attachments', ('b.png', f2, 'image/png'))]open('file.pdf', 'rb'), pas 'r'. Passer un objet de fichier texte à fichiers= soulever une TypeError. C'est un piège courant spécifique à Python lors de la migration depuis smtplib.fichiers=: chaque tuple est ('pièces jointes', (nom_fichier, objet_fichier, type_contenu)). Requests gère la délimitation multipart automatiquement.BytesIO objet directement : from io import BytesIO; buf = BytesIO(pdf_bytes) puis ('rapport.pdf', buf, 'application/pdf').Réponses, Fils & Suivi
Pour envoyer un e-mail au nom d'un utilisateur, le multithreading et le suivi des livraisons basé sur les webhooks, voici les modèles Python dont vous avez besoin.
01Filetage avec in_reply_to
new_thread = False en_réponse_à champ avec le identifiant de suivi du courriel auquel vous souhaitez répondre. Unipile gère Références et En réponse à en-têtes automatiquement.
requêtes.poste(
f'{DSN}/api/v1/emails',
en-têtes={'X-API-KEY': CLÉ_API},
données={
'identifiant_de_compte': ID_COMPTE,
'à': json.vidages([{'identifier': 'alice@acme.com'}]),
'sujet': 'Re: Votre question',
'corps': 'Suite à votre message.
',
'en_réponse_à': 'msg_original_tracking_id'
}
)02Webhooks en Python (exemple Flask)
Enregistrez une URL de webhook dans votre tableau de bord Unipile pour recevoir les événements de livraison (envoyé, rejeté, ouvert). Voici un récepteur Flask minimal :
from flasque import Flask, requête, jsonify
import journalisation
appliquer Flasque(__nom__)
connexion.basicConfigniveau=logging.INFO)
@app.itinéraire('/webhook/email', méthodes=[POST])
déf email_webhook():
événement = demande.get_json()
type_événement = événement.obtenir('type')
tracking_id = événement.obtenir('identifiant_de_suivi')
connexion.informations(f'Email événement : {event_type} pour {tracking_id}')
return jsonifyD'accord=Vrai), 20003Clés d'idempotence
Pour éviter les envois en double lors de la nouvelle tentative sur le réseau, passez un identifiant unique Clé d'idempotence En-tête. Si la même clé est envoyée deux fois, Unipile renvoie la réponse originale sans envoyer de deuxième e-mail.
import uuid, requêtes, os, json
clé = chaîne(uuid.uuid4()) # générer une fois, stocker dans la base de données
requêtes.poste(
f'{os.environ["UNIPILE_DSN"]}/api/v1/emails',
en-têtes={
'X-API-KEY': os.environ['UNIPILE_API_KEY'],
'Clé d'idempotence'clé
},
données={'identifiant_de_compte': os.environ'UNIPILE_ACCOUNT_ID'],
'à': json.vidages([{'identifier': 'alice@acme.com'}]),
'sujet': 'Bienvenue !', 'corps': 'Salut !'}
)Gestion des erreurs et nouvelles tentatives
La production du code Python pour l'API d'e-mail nécessite une gestion appropriée des exceptions, une journalisation structurée et des tentatives automatiques avec une stratégie de retrait exponentiel en utilisant ténacité bibliothèque.
| Code HTTP | Sens | Action |
|---|---|---|
| 202 | Accepté - mis en attente de livraison | Stocker tracking_id |
| 400 | Mauvaise requête (champs invalides) | Corriger la charge utile, ne pas réessayer |
| 401 | Clé API invalide | Vérifier UNIPILE_API_KEY |
| 403 | Compte non autorisé | Relier le compte |
| 404 | Identifiant de compte introuvable | Vérifier UNIPILE_ACCOUNT_ID |
| 429 | Limité par le taux | Backoff + relance (voir code) |
| 500 | Erreur du serveur | Réessayer après un délai de 5 secondes |
import requests, os, json, logging
from ténacité import (
réessayer, arrêter_après_tentative,
wait_exponential, retry_if_exception_type
)
connexion.basicConfigniveau=logging.INFO)
logger = logging.getLogger(__nom__)
classe ErreurDeLimiteDeRequêtes(Exception) :
Passe
@Réessayez(
arrêter=stopper_après_tentative(4),
attendre=attendre_exponentiel(multiplicateur=1, min=2, max=30),
réessayer=réessayer_si_type_exception(ErreurDeLimiteDeRequêtes)
)
déf envoyer_avec_réessai(à : chaîne, sujet : chaîne, corps : chaîne) -> dictionnaire:
resp = requests.poste(
f'{os.environ["UNIPILE_DSN"]}/api/v1/emails',
en-têtes={'X-API-KEY': os.environ'UNIPILE_API_KEY']},
données={
'identifiant_de_compte': os.environ'UNIPILE_ACCOUNT_ID'],
'à': json.vidages([{'identifier': à}],
'sujet': sujet, 'corps': corps
},
délai d'attente=30
)
si resp.status_code == 429:
journal.avertissement('Ratelimité, en attente...')
soulever ErreurDeLimiteDeRequêtes()
resp.lancer_statut()
return resp.json()Bonnes pratiques de sécurité en Python
Pour un guide complet sur la protection de votre intégration d'API d'e-mails, consultez le Guide de sécurité des API d'e-mail. Voici les essentiels spécifiques à Python.
os.environ ou python-dotenv. Ne jamais mettre CLÉ_API_UNIPILE comme une chaîne littérale dans votre code source. S'il est accidentellement poussé vers Git, faites pivoter la clé immédiatement depuis votre tableau de bord.environnement virtuel ou conda. Cela évite les attaques par confusion de dépendances et rend votre requirements.txt auditable. Épinglez les versions en production.CLÉ_API_UNIPILE valide.identifiant de suivi pour chaque e-mail envoyé afin d'activer les audits de livraison. Utilisez la bibliothèque standard de Python journalisation module - jamais imprimer() en production. Expédier des journaux vers un SIEM pour des cas d'utilisation à forte conformité.Pièges courants spécifiques à Python
Voici les erreurs les plus courantes commises par les développeurs Python lors de l'intégration de l'API d'e-mail. Si vous êtes plutôt sur Node.js, consultez notre Tutoriel API JavaScript pour envoyer des e-mails.
json= au lieu de données=multipart/form-data, pas JSON. Utilisez toujours requests.post(..., données={...}). En utilisant json={...} renverra une erreur 400. Le à, cc, ou encore CCI les champs sont des chaînes JSON dans les données du formulaire - utiliser json.dumps() pour encoder le tableau des destinataires.open('file.pdf', 'rb') - mode binaire. Mode texte ('r') soulève une TypeError lorsque passé au fichiers= paramètre. Pour le contenu en mémoire, utilisez io.BytesIO.demandes La bibliothèque est synchrone. L'appeler à l'intérieur d'une asynchrone def La fonction bloque la boucle d'événements. Utiliser httpx.AsyncClient ou aiohttp.ClientSession pour les contextes Python asynchrones (FastAPI, vues Django asynchrones, scripts asyncio).requêtes.post() attend éternellement. Une connexion bloquée immobilisera votre thread (ou votre worker Celery) indéfiniment. Passez toujours délai d'attente=30 (délai d'attente de connexion, délai d'attente de lecture en secondes).from datetime import datetime, timezone; datetime.now(timezone.utc). Les dates et heures naïves provoquent des erreurs silencieuses d'une heure ou plus dans les déploiements multi-régions.concurrent.futures.ThreadPoolExecutor) ou déchargement vers une file d'attente Celery.Questions fréquemment posées
Questions fréquentes sur l'utilisation de l'API d'e-mail en Python avec l'API unifiée d'e-mail Unipile.
Utilisez l'API unifiée de messagerie Unipile au lieu de smtplib ou d'une connexion SMTP directe. Installer demandes, obtenez votre clé API et votre DSN depuis le tableau de bord Unipile, liez un compte Gmail ou Outlook via OAuth, puis effectuez une requête POST à /api/v1/emails avec toi account_id, à, sujet, ou encore corps. Aucun serveur SMTP, aucun port 587, aucune configuration TLS nécessaire dans votre code Python.
Django : appeler l'API dans une vue ou une commande de gestion. Pour Django asynchrone (3.1+), utilisez httpx.AsyncClient dans les vues asynchrones.
Flasque Appelez l'API dans un gestionnaire de route côté serveur. Ne l'appelez jamais depuis un modèle Jinja ou du JavaScript côté client. Utilisez Flask-Celery pour décharger les envois à haut volume vers des workers en arrière-plan.
FastAPI : Utiliser httpx.AsyncClient à l'intérieur asynchrone def points de terminaison. Le synchrone demandes La bibliothèque bloque la boucle d'événements asynchrone - utilisez toujours un client HTTP asynchrone dans FastAPI.
smtplib se connecte directement à un serveur SMTP depuis votre processus Python. Vous gérez les identifiants SMTP, la configuration TLS et les particularités par fournisseur (mots de passe d'application Gmail, authentification moderne Outlook). Il est également uniquement synchrone.
L'API d'email Unipile est une abstraction cloud : liez les comptes via OAuth (pas d'identifiants SMTP dans votre code pour Gmail/Outlook), obtenez une API HTTP unique et cohérente pour tous les fournisseurs, et Unipile gère le transport, le rafraîchissement des jetons et les nouvelles tentatives. Le compromis est que les envois passent par l'infrastructure d'Unipile plutôt que par une connexion SMTP directe.
Oui, mais vous avez besoin d'un client HTTP asynchrone – le standard demandes La bibliothèque est synchrone et bloquera votre boucle d’événements. Utilisez httpx (recommandé, alternative asynchrone simple) ou aiohttp.
import httpx, os, json
asynchrone def envoyer_email_en_asynchrone(à : chaîne, sujet : chaîne, corps : chaîne):
en asynchrone avec httpx.AsyncClient() En tant que client :
resp = await client.poste(
f'{os.environ["UNIPILE_DSN"]}/api/v1/emails',
en-têtes={'X-API-KEY': os.environ'UNIPILE_API_KEY']},
données={'identifiant_de_compte': os.environ'UNIPILE_ACCOUNT_ID'],
'à': json.vidages([{'identifier': à}],
'sujet': sujet, 'corps': corps
)
resp.lancer_statut()
return resp.json()Utilisez une file d'attente de tâches Celery avec un broker Redis ou RabbitMQ. Chaque e-mail devient une tâche - Celery gère la concurrence et les nouvelles tentatives automatiquement. Limitez la concurrence par travailleur pour éviter les limites de débit (généralement 5 à 10 envois simultanés par compte lié). Pour des envois marketing à très grand volume (millions par jour), combinez Unipile pour les envois transactionnels basés sur OAuth avec un ESP dédié pour les campagnes de masse.
Pour des cas d'utilisation plus légers, concurrent.futures.ThreadPoolExecutor(max_workers=5) avec le demandes la bibliothèque est une approche plus simple qui évite la surcharge de Celery.
Oui. Créez une tâche Celery qui appelle requêtes.post() vers le point de terminaison Unipile. Les workers Celery sont des processus Python synchrones standard, donc demandes fonctionne parfaitement. Utiliser le intégré de Celery autoretry_for=(requests.exceptions.HTTPError,) avec tentatives_max=3 et délai_de_nouvel_essai_par_défaut=5 pour la nouvelle tentative automatique en cas d'échecs temporaires. Combiner avec Clé d'idempotence En-têtes pour éviter les envois en double lors des redémarrages de workers.
Vous avez encore des questions ? Notre équipe est là pour vous aider.