Python transactional email is easy to make work in development, but production failures usually come from operations gaps, not just code bugs. Common issues include retries that duplicate messages, missing queues that block requests, unverified sending domains, and no clear answer when someone asks, “Did the email actually leave?”
This guide is for SaaS and development teams sending password resets, OTPs, receipts, and system alerts from Python. It gives a production-safe checklist covering queueing, retries, idempotency, rate-limit handling, and debugging with logs and events.
It also covers the deliverability basics that should be in place before scaling: domain verification, SPF, DKIM, and DMARC (especially as volume grows). MailCub’s guidance notes a transactional email API limit of 15 requests per second per API key, so designing around burst limits is an important part of production safety.
MailCub Documentation is the best place to confirm your authentication and logs are working end-to-end before you scale. You can also review the Transactional Email Service page for webhook and event-tracking context, and check the pricing section on MailCub Pricing for plan planning.
Quick Answer
- Use a queue and worker (Celery, RQ, or similar) so web requests never send email directly.
- Implement idempotency so retries do not duplicate OTPs, receipts, or alerts.
- Retry only transient failures and avoid retrying permanent errors blindly.
- Design around rate limits using batching, controlled concurrency, and backoff.
- Verify your domain and publish SPF and DKIM, then add DMARC as volume grows.
- Store provider references and use logs and events to debug “not received” reports.
Why This Matters
Transactional email is directly tied to revenue and account access. If OTPs do not arrive, users drop off during sign-in. If receipts fail, refunds and chargebacks can increase because users do not have the confirmation they expect.
Production reliability is mostly operational discipline: queue and worker isolation, controlled retries, and observability through logs and events. The strongest teams can answer two questions quickly: “Was it accepted?” and “What happened next?” MailCub’s documented response codes and rate-limit behavior give you clear signals to build practical runbooks.
Python Transactional Email in Production
A production-safe Python transactional email setup should include:
- Fast API path: the request enqueues a job and returns immediately.
- Durable queue: jobs survive restarts and deploys.
- Retry policy: retries only when it should, with backoff.
- Idempotency: a job can run twice without sending twice.
- Debug trail: message ID or request ID tied to user context and event timeline.
If your provider supports webhook events, wire them into your app so bounces and dropped messages update your database automatically. The Transactional Email Service page mentions webhook support and event tracking as part of the feature set.
Step-by-Step Production Checklist
1) Put sending behind a queue
Do not send emails directly inside a web request. Use a queue-and-worker pattern (for example, Celery or RQ): the web app creates the job, and the worker sends the email. This prevents request timeouts and gives you controlled retries.
Store at least:
- job_id
- user_id
- message_type (otp, receipt, reset, alert)
- generated idempotency_key
2) Standardize message types and templates
Create a stable message catalog such as:
- OTP / verification
- Password reset
- Invoice / receipt
- System alerts
Each message type should have:
- Plain-text and HTML versions (good deliverability practice)
- A clear subject line
- Minimal links, especially for OTP emails
Keeping message types standardized makes debugging easier and helps your team apply different retry and priority policies where needed.
3) Use idempotency keys to stop duplicates
Duplicate emails usually happen when a worker restarts mid-send, a timeout triggers a retry, or the same queue job is delivered twice. Idempotency is what protects you from these cases.
A common pattern is:
- Compute an idempotency key (for example: otp:{user_id}:{purpose}:{time_bucket})
- Check a table or cache before sending to see if that key already sent
- If yes, return success without sending again
This is especially important for OTP and receipt flows where duplicates confuse users and support teams.
4) Design retries around provider response codes
Treat failure types differently:
- Retry: temporary network errors and rate limits
- Do not retry automatically: invalid auth, domain configuration issues, account paused or suspended states
MailCub Documentation documents the transactional email API endpoint, response behavior, and common status codes. It also documents the API authentication header and endpoint details used for sending:
- API endpoint:
https://api.mail.mailcub.com/api/send_email - Auth header:
x-sh-key: YOUR_API_KEY
Production Action Checklist
| Status / signal | What it usually means | What to do in production |
|---|---|---|
| 200 | Accepted and queued for delivery | Store provider/message reference; watch logs/events |
| 401 | API key missing/invalid | Rotate/fix secrets; block deploy if missing |
| 403 | From-domain not verified | Verify domain + DNS; stop sending from that domain |
| 404 | Domain not in your account | Fix from-address mapping; confirm tenant/domain config |
| 422 | Account paused | Halt jobs; contact support; investigate policy issues |
| 429 | Rate limit/quota exceeded | Backoff + batch; check plan quotas; reduce burst |
| 102 | Account suspended | Stop sends; investigate abuse/bounces; contact support |
5) Respect per-second rate limits
MailCub’s documented transactional email API rate guidance is 15 requests per second per API key. Even if your plan supports enough monthly volume, short traffic bursts can still hit per-second limits.
Practical ways to stay safe:
- Queue sends and process with controlled concurrency
- Batch non-urgent emails
- Use exponential backoff for 429 responses
- Add jitter so workers do not retry at the same second
6) Do deliverability basics before scaling
Start with domain and authentication hygiene:
- Verify your sending domain
- Publish the required DNS records (including SPF and DKIM)
- Add DMARC as volume and complexity grow
MailCub guidance references domain verification and DNS setup. The same MailCub Documentation flow is where your team should validate sending configuration before scaling traffic.
The provided source content also notes Google guidance: all senders should use SPF or DKIM, and bulk senders should use SPF + DKIM + DMARC. For higher-volume Gmail sending, these checks become even more important.
7) Add logs and events for fast support debugging
At minimum, store the following for every send attempt:
- user_id
- message_type
- to address
- from_domain
- timestamp
- provider response status
- provider reference or request ID
- bounce, complaint, or drop events (if supported)
MailCub’s materials emphasize logs, analytics, and monitoring usage, and the Transactional Email Service page also highlights webhook and event-tracking support. Together, these make “email not received” tickets much easier to diagnose.
MailCub Documentation is a practical next step if you want to add delivery logs and a basic retry and backoff policy quickly.
Common Mistakes
- Sending directly inside HTTP requests, which causes timeouts and duplicate retries.
- Retrying every failure, including permanent errors.
- Skipping idempotency for OTP and receipts, which leads to duplicate messages.
- Missing domain verification and DNS authentication (SPF, DKIM, DMARC).
- Ignoring per-second rate limits until launch traffic starts.
- Not storing log correlation data, so support cannot trace a single message.
Troubleshooting Playbook
If emails are accepted but users still do not see them
- Check provider logs and events for bounce, drop, or spam placement signals
- Verify domain authentication alignment (SPF, DKIM, DMARC)
- Ask users to check Promotions and Spam folders, especially for template-heavy emails
This is where a clean log and event trail becomes essential. Start with provider status and then move to template and domain checks.
If you see 401, 403, or 404 errors
- 401: API secrets or environment variables are missing or incorrect
- 403 / 404: the From-domain is not verified or not mapped to your account
Stop the queue for the affected message type until the configuration is fixed. MailCub Documentation should be the first reference for verifying domain and API setup.
If you hit 429 spikes
- Reduce worker concurrency
- Add or increase backoff and jitter
- Batch non-urgent sends
- Review monthly quota and burst behavior, since both can trigger 429 responses
FAQ
What is the safest way to send transactional email in Python?
Use a queue and worker, add idempotency keys, retry only transient failures, and store provider references and logs so your team can debug outcomes quickly.
Do I need Celery to send email reliably?
Not strictly. But you do need a queue-and-worker mechanism such as Celery, RQ, or a managed queue so web requests stay fast and retries stay controlled.
How do I avoid duplicate OTP emails?
Use an idempotency key per user, purpose, and time window, and record the send state so the worker can safely ignore duplicate processing.
What authentication do I need for Gmail deliverability?
The source content notes Google guidance that all senders should use SPF or DKIM, and bulk senders should use SPF + DKIM + DMARC.
What should I do when I hit rate limits?
Use backoff with jitter, reduce concurrency, batch non-urgent sends, and design your queue around provider limits. MailCub’s documented guidance includes a 15 req/s per API key limit for transactional sends.
How do I debug “email not received”?
Start with provider status, logs, and event outcomes (delivered, bounced, dropped), then verify authentication alignment and review templates and link patterns.
Conclusion
A reliable Python transactional email system is mostly about repeatability: queue every send, retry safely, and keep a clear trail from user action to provider response to final outcome. Once that foundation is stable, you can scale traffic and templates without guessing why emails disappear.
Use MailCub Documentation to set up the baseline send flow and logs, and pair it with the Transactional Email Service for event tracking and webhook support. You can also review MailCub Pricing while planning production capacity and rollout.