Come Inviare e-mail tramite API in Python (Tutorial Rapido)
Salta il boilerplate SMTP. Questa guida ti mostra come utilizzare API di posta elettronica unificata Unipile per inviare email in Python - con esempi di copia-incolla per Gmail, Outlook e IMAP utilizzando richieste biblioteca.
import richieste, os
CHIAVE_API = os.environ['UNIPILE_API_KEY']
DSN = os.environ['UNIPILE_DSN']
ID_ACCOUNT = os.environ['UNIPILE_ACCOUNT_ID']
response = requests.posta(
f'{DSN}/api/v1/email',
intestazioni={'X-API-KEY': CHIAVE_API},
dati={
'ID account': ID_ACCOUNT,
'a': '[{"display_name":"Alice","identifier":"alice@acme.com"}]',
'soggetto': 'Ciao da Python',
'corpo': 'Inviato tramite Unipile!
'
}
)
print(risposta.json())Esempio Python di 5 righe
Se già lo sai cos'è un'API per l'invio di e-mail e voglio solo il codice Python dell'API per le email che funzioni davvero, eccolo qui. Il tutorial completo segue qui sotto.
pip install requests python-dotenvUNIPILE_DSN, CHIAVE_API_UNIPILE, e UNIPILE_ACCOUNT_ID al tuo .env file./api/v1/emailaccount_id, a, soggetto, e corpo. Fatto.import richieste, os
from dotenv import carica_dotenv
carica_dotenv()
CHIAVE_API = os.environ['UNIPILE_API_KEY']
DSN = os.environ['UNIPILE_DSN']
ID_ACCOUNT = os.environ['UNIPILE_ACCOUNT_ID']
resp = richieste.posta(
f'{DSN}/api/v1/email',
intestazioni={'X-API-KEY': CHIAVE_API},
dati={
'ID account': ID_ACCOUNT,
'a': '[{"display_name":"Alice","identifier":"alice@acme.com"}]',
'soggetto': 'Ciao da Python',
'corpo': 'Inviato tramite Unipile!
'
}
)
print(resp.json()) # {'tracking_id': 'msg_...'}Prerequisiti e configurazione
Prima di poter utilizzare il flusso di lavoro Python dell'API email in produzione, sono necessarie quattro cose: Python 3.9+, il richieste libreria, una chiave API con DSN e un account email collegato.
| tipi unione e funzionalità della libreria standard da 3.9+. Python 3.11 LTS è raccomandato per la produzione. Verifica la tua versione con python --versione.api4.unipile.com:13444Entrambi sono richiesti in ogni header di richiesta.python -m venv .venv && source .venv/bin/activate. Non installare mai pacchetti nel Python di sistema, questo è particolarmente importante per la gestione delle credenziali.pip install requests python-dotenv
# Opzionale: supporto asincrono
pip install aiohttp httpx
# Opzionale: logica di retry
pip install tenacitypipenv install requests python-dotenv tenacitypoetry aggiungi richieste python-dotenv tenacity# Credenziali Unipile - non inviare mai questo file
UNIPILE_DSN=https://api4.unipile.com:13444
CHIAVE_API_UNIPILE=il_tuo_token_di_accesso_qui
# L'ID dell'account del servizio email collegato
UNIPILE_ACCOUNT_ID=acc_xxxxxxxxxxxxxxxxAggiungi .env al tuo .gitignore. Caricare con python-dotenv via load_dotenv() in cima al tuo script. In produzione, preferisci le variabili d'ambiente effettive iniettate dalla tua piattaforma di distribuzione (Heroku, Railway, Docker Compose).
Collegamento del tuo primo account email
Prima di poter inviare, è necessario collegare un account email a Unipile. Questo è un passaggio unico per account. Vedi l'intero Guida all'integrazione tramite API unificata per email per maggiori informazioni sui flussi multi-account.
Unipile utilizza una procedura guidata di autenticazione ospitata: il tuo script Python genera un link di autenticazione, l'utente fa clic su di esso e completa OAuth nel browser, quindi Unipile chiama il tuo webhook con il nuovo account_id. Nessuna credenziale SMTP è memorizzata nel tuo codice per Gmail o Outlook.
import richieste, os
from dotenv import carica_dotenv
carica_dotenv()
CHIAVE_API = os.environ['UNIPILE_API_KEY']
DSN = os.environ['UNIPILE_DSN']
# Passaggio 1: crea un link di autenticazione ospitato per Gmail OAuth
resp = richieste.posta(
f'{DSN}/api/v1/hosted/accounts/link',
intestazioni={'X-API-KEY': CHIAVE_API},
dati={
'tipo': 'GOOGLE',
'nome': 'Alice Gmail',
'url di successo': 'https://tuapp.com/oauth/success',
'url_fallimento': 'https://yourapp.com/oauth/failure'
}
)
# Passaggio 2: invia questo URL al tuo utente
auth_url = resp.json()['URL']
print(Indirizza l'utente a: {auth_url}')
# Passaggio 3: Unipile invia POST di {account_id} al tuo webhook dopo OAuth
# Vedi /gmail-api-send-email-a-comprehensive-guide-for-developers/ per i dettagli su Gmailimport richieste, os
from dotenv import carica_dotenv
carica_dotenv()
# Outlook OAuth - copre Outlook personale + Microsoft 365
# Vedi /microsoft-graph-api-email-integration-guide/
resp = richieste.posta(
f'{os.environ["UNIPILE_DSN"]}/api/v1/hosted/accounts/link',
intestazioni={'X-API-KEY': os.environ['UNIPILE_API_KEY']},
dati={
'tipo': 'MICROSOFT',
'nome': 'Bob Outlook',
'url di successo': 'https://tuapp.com/oauth/success',
'url_fallimento': 'https://yourapp.com/oauth/failure'
}
)
print(resp.json()['URL'])import richieste, os, json
# IMAP: passa le credenziali SMTP/IMAP direttamente (nessun redirect OAuth necessario)
# Vedi /la-guida-per-sviluppatori-sulla-soluzione-API-IMAP/ per tutti i dettagli IMAP
resp = richieste.posta(
f'{os.environ["UNIPILE_DSN"]}/api/v1/accounti',
intestazioni={'X-API-KEY': os.environ['UNIPILE_API_KEY']},
json={
'fornitore': 'IMAP',
'nome utente': 'alice@company.com',
'password': 'app_password_qui',
'imap_host': 'imap.company.com',
'smtp_host': 'smtp.company.com'
}
)
account_id = resp.json()['ID account']
print(Conto collegato: {account_id}')Invio della tua prima email da Python
Il punto di invio accetta multipart/form-data. Usa dati= (non json=in requests.post(). Il a, cc, e DCC i campi sono stringhe codificate in JSON all'interno dei dati del modulo.
import richieste, os, json
richieste.posta(
f'{os.environ["UNIPILE_DSN"]}/api/v1/email',
intestazioni={'X-API-KEY': os.environ['UNIPILE_API_KEY']},
dati={
'ID account': os.environ['UNIPILE_ACCOUNT_ID'],
'a'json.scarichi([{'nome_visualizzato': 'Alice', 'identificatore': 'alice@acme.com'}]),
'soggetto': 'Aggiornamento rapido',
'corpo': 'Ciao Alice, volevo solo salutarti.'
}
)corpo il campo accetta sia testo normale che HTML. Usa tag per la formattazione HTML.import richieste, os, json
response = requests.posta(
f'{os.environ["UNIPILE_DSN"]}/api/v1/email',
intestazioni={'X-API-KEY': os.environ['UNIPILE_API_KEY']},
dati={
'ID account': os.environ['UNIPILE_ACCOUNT_ID'],
'a'json.scarichi([{'identificatore': 'alice@acme.com'}]),
'cc'json.scarichi([{'identificatore': 'manager@acme.com'}]),
'copia carbone nascosta'json.scarichi([{'identificatore': 'crm@yourapp.com'}]),
'soggetto': 'La tua fattura è pronta',
'corpo': 'Fattura #1042
Si prega di trovare la Vostra fattura in allegato.
'
}
)
# 202 Accettato = in coda per la consegna
print(stato_richiesta.codice, risposta.json())import richieste, os, json
def invia_emailall_email: strada, oggetto: strada, corpo: strada) -> dizionario:
"Invia email tramite il wrapper Python dell'API email di Unipile."
response = requests.posta(
f'{os.environ["UNIPILE_DSN"]}/api/v1/email',
intestazioni={'X-API-KEY': os.environ['UNIPILE_API_KEY']},
dati={
'ID account': os.environ['UNIPILE_ACCOUNT_ID'],
'a'json.scarichi([{'identificatore': email_destinatario}]),
'soggetto'soggetto,
'corpo'corpo,
},
tempo_scaduto=30
)
risposta.raise_for_status() # solleva HTTPError su 4xx/5xx
return risposta.json() # {'tracking_id': 'msg_...'}tempo_scaduto=30 per evitare di rimanere bloccati per sempre su problemi di rete. Usa raise_for_status() diffondere gli errori HTTP come eccezioni Python.Ottieni la tua chiave API, collega un account Gmail o Outlook in pochi minuti ed esegui gli esempi Python di questa guida su caselle di posta reali.
Invio di allegati in Python
Gli allegati vengono inviati come parte dei dati del modulo multipart utilizzando Python's file= parametro. Apri il file in modalità binaria ('rb') - byte, non stringhe.
import richieste, os, json
# Single file attachment
con aperto('fattura.pdf', 'rb') come f:
resp = richieste.posta(
f'{os.environ["UNIPILE_DSN"]}/api/v1/email',
intestazioni={'X-API-KEY': os.environ['UNIPILE_API_KEY']},
dati={
'ID account': os.environ['UNIPILE_ACCOUNT_ID'],
'a'json.scarichi([{'identificatore': 'client@example.com'}]),
'soggetto': 'Fattura allegata',
'corpo': 'Si prega di vedere la fattura allegata.
'
},
file={'allegati': ('fattura.pdf', f, 'application/pdf')}
)
# Allegati multipli: passa un elenco di tuple
# file=[('allegati', ('d.pdf', f1, 'application/pdf')),
# ('allegati', ('b.png', f2, 'image/png'))]apri('file.pdf', 'rb'), non 'erre'. Passare un oggetto file di testo a file= solleva un Errore di tipo. Questo è un intoppo comune specifico di Python quando si migra da smtplib.file=: ogni tupla è ('allegati', (nomefile, fileobj, tipo_contenuto)). Requests gestisce automaticamente il limite multipart.BytesIO oggetto direttamente from io import BytesIO; buf = BytesIO(pdf_bytes) allora ('report.pdf', buf, 'application/pdf').Risposte, Thread e Monitoraggio
Per invio email per conto di un utente, threading e tracciamento della consegna basato su webhook, ecco i pattern Python di cui hai bisogno.
01Thread con in_reply_to
Per rispondere all'interno di un thread esistente, passa il in_risposta_a campo con il tracking_id dell'email a cui vuoi rispondere. Unipile gestisce Riferimenti e In-Reply-To intestazioni automaticamente.
richieste.posta(
f'{DSN}/api/v1/email',
intestazioni={'X-API-KEY': CHIAVE_API},
dati={
'ID account': ID_ACCOUNT,
'a': json.scarichi([{'identificatore': 'alice@acme.com'}]),
'soggetto': 'Re: La tua domanda',
'corpo': 'In seguito al tuo messaggio.
',
'in_reply_to': 'msg_id_tracciamento_originale'
}
)02Webhook in Python (esempio con Flask)
Registra un URL webhook nella tua dashboard Unipile per ricevere eventi di consegna (inviato, rimbalzato, aperto). Ecco un ricevitore Flask minimale:
from fiaschetta import Flask, richiesta, jsonify
import registrazione
app = Fiasco(__nome__)
registrazione.basicConfig(livello=logging.INFORMAZIONI)
@app.itinerario('/webhook/email', methods=['POST'])
def webhook_email():
evento = richiesta.ottieni_json()
tipo_evento = evento.ottenere('tipo')
tracking_id = evento.ottenere('id_tracciamento')
registrazione.informazioni(f'Email evento: {event_type} per {tracking_id}')
return jsonify(ok=Vero), 20003Chiavi di idempotenza
Per evitare invii duplicati in caso di ripetizione della rete, passare un ID univoco Chiave di Idempotenza header. Se la stessa chiave viene inviata due volte, Unipile restituisce la risposta originale senza inviare una seconda email.
import uuid, richieste, os, json
chiave = stradauuid.uuid4()) # genera una volta, memorizza nel DB
richieste.posta(
f'{os.environ["UNIPILE_DSN"]}/api/v1/email',
intestazioni={
'X-API-KEY'os.environ['UNIPILE_API_KEY'],
'Chiave di Idempotenza'chiave
},
dati={'ID account': os.environ['UNIPILE_ACCOUNT_ID'],
'a'json.scarichi([{'identificatore': 'alice@acme.com'}]),
'soggetto': 'Benvenuto!', 'corpo': 'Ciao!'}
)Gestione degli Errori e Riprova
Il codice di produzione Python per l'API delle email richiede una gestione adeguata delle eccezioni, logging strutturato e tentativi automatici con backoff esponenziale utilizzando la libreria `tenacity`. tenacia biblioteca.
| Codice HTTP | Significato | Azione |
|---|---|---|
| 202 | Accettato - in coda per la consegna | Memorizza tracking_id |
| 400 | Richiesta non valida (campi non validi) | Correggi il payload, non riprovare |
| 401 | Chiave API non valida | Controlla UNIPILE_API_KEY |
| 403 | Account non autorizzato | Ricollega account |
| 404 | ID account non trovato | Controlla UNIPILE_ACCOUNT_ID |
| 429 | Limite di frequenza raggiunto | Riprova con backoff (vedi codice) |
| 500 | Errore del server | Riprova dopo 5 secondi |
import richieste, os, json, logging
from tenacia import (
riprova, ferma_dopo_tentativo,
wait_exponential, retry_if_exception_type
)
registrazione.basicConfig(livello=logging.INFORMAZIONI)
logger = logging.getLogger(__nome__)
classe ErroreLimiteRate(Eccezione):
passare
@riprova(
fermati=interrompi_dopo_tentativo(4),
attendere=attendi_esponenziale(moltiplicatore=1, min=2, max=30),
riprova=riprova_se_tipo_eccezione(ErroreLimiteRate)
)
def inviare_con_ritentativoA: strada, oggetto: strada, corpo: strada) -> dizionario:
resp = richieste.posta(
f'{os.environ["UNIPILE_DSN"]}/api/v1/email',
intestazioni={'X-API-KEY': os.environ['UNIPILE_API_KEY']},
dati={
'ID account': os.environ['UNIPILE_ACCOUNT_ID'],
'a'json.scarichi([{'identificatore'},
'soggetto'soggetto, 'corpo'corpo
},
tempo_scaduto=30
)
se resp.status_code == 429:
registratore.avvertimento('Rate limitato, ripiegando...')
salire ErroreLimiteRate()
resp.raise_for_status()
return resp.json()Migliori pratiche di sicurezza in Python
Per una guida completa su come proteggere la tua integrazione API di posta elettronica, consulta guida alla sicurezza delle API via email. Ecco gli elementi essenziali specifici di Python.
os.environ o python-dotenv. Mai mettere CHIAVE_API_UNIPILE come literal di stringa nel tuo codice sorgente. Se accidentalmente inserito su Git, ruota immediatamente la chiave dalla tua dashboard.venv o conda. Ciò impedisce attacchi di confusione delle dipendenze e rende il tuo requirements.txt Verificabile. Fissa le versioni in produzione.CHIAVE_API_UNIPILE valido.tracking_id per ogni email inviata per abilitare audit di consegna. Utilizza standard di Python registrazione modulo - mai stampa() in produzione. Invia i log a un SIEM per casi d'uso con obblighi di conformità rigorosi.Trappole comuni specifiche di Python
Questi sono gli errori più comuni che gli sviluppatori Python commettono quando integrano l'API email. Se invece utilizzi Node.js, consulta la nostra Tutorial sull'API JavaScript per l'invio di email.
json= invece di dati=multipart/form-data, non JSON. Usa sempre requests.post(..., dati={...}). Utilizzando json={...} restituirà un errore 400. Il a, cc, e DCC i campi sono stringhe JSON all'interno dei dati del modulo - usa json.dumps() per codificare l'array del destinatario.apri('file.pdf', 'rb') - modalità binaria. Modalità testo ('erre') solleva un Errore di tipo quando passato a file= parametro. Per contenuti in memoria, usa io.BytesIO.richieste la libreria è sincrona. Chiamarla all'interno di un asincrono def funzioni bloccano il ciclo di eventi. Usa httpx.AsyncClient o aiohttp.ClientSession per contesti Python asincroni (FastAPI, viste Django asincrone, script asyncio).requests.post() aspetta per sempre. Una connessione bloccata bloccherà il tuo thread (o worker Celery) indefinitamente. Passa sempre tempo_scaduto=30 (timeout di connessione, timeout di lettura in secondi).from datetime import datetime, timezone; datetime.now(timezone.utc). Le date e ore naive causano errori silenziosi di ore in determinati deployment su più regioni.concurrent.futures.ThreadPoolExecutoro scaricare su una coda Celery.Domande frequenti
Domande comuni sull'utilizzo dell'API delle email in Python con l'API unificata delle email di Unipile.
Utilizzare l'API unificata per email di Unipile invece di smtplib o di una connessione SMTP diretta. Installare richieste, ottieni la tua chiave API e il DSN dalla dashboard di Unipile, collega un account Gmail o Outlook tramite OAuth, quindi POST a /api/v1/email con il tuo account_id, a, soggetto, e corpo. Nessun server SMTP, nessuna porta 587, nessuna configurazione TLS necessaria nel tuo codice Python.
Django: utilizzare l'API in una vista o in un comando di gestione. Per Django asincrono (3.1+), utilizzare httpx.AsyncClient nelle viste asincrone.
Fiaschetta chiama l'API in un gestore di route lato server. Non chiamarla mai da un template Jinja o da JavaScript lato client. Usa Flask-Celery per scaricare invii ad alto volume su worker in background.
FastAPI usare httpx.AsyncClient dentro asincrono def endpoint. Il sincrono richieste la libreria blocca il ciclo di eventi asincrono - usa sempre un client HTTP asincrono in FastAPI.
smtplib si connette direttamente a un server SMTP dal tuo processo Python. Gestisci le credenziali SMTP, la configurazione TLS e le stranezze specifiche del provider (password per app Gmail, autenticazione moderna di Outlook). È anche solo sincrono.
L'API email di Unipile è un'astrazione cloud: collega account tramite OAuth (nessuna credenziale SMTP nel tuo codice per Gmail/Outlook), ottieni un'unica API HTTP coerente per tutti i provider, e Unipile gestisce il trasporto, il refresh dei token e i tentativi. Il compromesso è che l'invio passa attraverso l'infrastruttura di Unipile anziché una connessione SMTP diretta.
Sì, ma hai bisogno di un client HTTP asincrono - lo standard richieste la libreria è sincrona e bloccherà il tuo loop di eventi. Usa httpx (alternativa asincrona consigliata e sostituibile al volo) o aiohttp.
import httpx, os, json
asincrono def invia_email_asyncA: strada, oggetto: strada, corpo: strada):
async with httpx.AsyncClient() come cliente:
risp = await cliente.posta(
f'{os.environ["UNIPILE_DSN"]}/api/v1/email',
intestazioni={'X-API-KEY': os.environ['UNIPILE_API_KEY']},
dati={'ID account': os.environ['UNIPILE_ACCOUNT_ID'],
'a'json.scarichi([{'identificatore'},
'soggetto'soggetto, 'corpo'corpo
)
resp.raise_for_status()
return resp.json()Utilizza una coda di attività Celery con un broker Redis o RabbitMQ. Ogni email diventa un'attività: Celery gestisce automaticamente la concorrenza e i tentativi. Limita la concorrenza per worker per evitare limiti di frequenza (tipicamente 5-10 invii concorrenti per account collegato). Per invii di marketing ad altissimo volume (milioni al giorno), combina Unipile per invii transazionali basati su OAuth con un ESP dedicato per le campagne di massa.
Per casi d'uso più leggeri, concurrent.futures.ThreadPoolExecutor(max_workers=5) con il richieste la libreria è un approccio più semplice che evita l'overhead di Celery.
Sì. Crea un task Celery che chiama requests.post() all'endpoint Unipile. I worker Celery sono processi Python sincroni standard, quindi richieste funziona perfettamente. Usa la funzionalità integrata di Celery autoretry_for=(requests.exceptions.HTTPError,) con max_tentativi=3 e ritardo_predefinito_tentativo=5 per il nuovo tentativo automatico in caso di errori transitori. Combina con Chiave di Idempotenza intestazioni per evitare invii duplicati al riavvio dei worker.
Avete ancora domande? Il nostro team è qui per aiutarvi.