Why AutoSend Doesn’t Have a Free Plan
How protecting deliverability, reliability, and focus shaped our decision to not offer the free plan.
Akash Bhadange • 11 Nov 2025 • engineering
Akash Bhadange • 12 Nov 2025 • engineering
When choosing how to send emails from your application, you'll encounter two primary approaches: SMTP (Simple Mail Transfer Protocol) and REST/HTTP APIs. This guide explains both methods, their tradeoffs, and helps you choose the right approach for your use case.
SMTP is the internet standard protocol for email transmission, established in 1982. When you send an email, whether through Gmail, Outlook, or a programmatic service, SMTP handles the actual delivery between mail servers.
How SMTP works:
Your application connects to an SMTP server (usually on port 587 for submission or 465 for SSL)
Authenticates using credentials
Issues a series of text commands (HELO, MAIL FROM, RCPT TO, DATA)
Transfers the email content
Closes the connection
An email API is a REST/HTTP interface that abstracts SMTP complexity. Instead of managing protocol-level commands, you make HTTP requests with JSON payloads to send emails.
How Email APIs work:
Your application makes an HTTP POST request
Sends email parameters as JSON (to, from, subject, body, attachments)
The API service handles SMTP communication with receiving servers
Returns an immediate response with status and metadata
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
def send_email_smtp(to_email, subject, body):
smtp_server = "smtp.autosend.com"
smtp_port = 587
username = "your_username"
password = "your_password"
msg = MIMEMultipart()
msg['From'] = username
msg['To'] = to_email
msg['Subject'] = subject
msg.attach(MIMEText(body, 'plain'))
try:
server = smtplib.SMTP(smtp_server, smtp_port)
server.starttls()
server.login(username, password)
server.send_message(msg)
server.quit()
return True
except Exception as e:
print(f"Error: {e}")
return False
import requests
def send_email_api(to_email, subject, body):
api_url = "https://api.autosend.com/v1/send"
api_key = "your_api_key"
payload = {
"to": to_email,
"subject": subject,
"text": body,
"from": "[email protected]"
}
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
try:
response = requests.post(api_url, json=payload, headers=headers)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
return None
SMTP: Requires managing persistent connections or creating new connections for each email. Connection pooling can improve performance but adds complexity.
API: Stateless HTTP requests. No connection management needed. HTTP clients handle connection pooling automatically.
SMTP: Uses username/password authentication. Some servers support OAuth2, but implementation varies.
API: Typically uses API keys or tokens in HTTP headers. More straightforward to implement and rotate credentials.
SMTP: Returns SMTP status codes (250 for success, 550 for rejection, etc.). Error messages can be cryptic and require parsing server responses.
550 5.1.1 <[email protected]>: Recipient address rejected
API: Returns structured JSON with clear error messages, error codes, and often includes request IDs for debugging.
{
"error": "invalid_recipient",
"message": "The email address is not valid",
"request_id": "req_abc123"
}
SMTP: Limited to basic email sending. Advanced features require custom headers or complex MIME construction.
API: Built-in support for:
Templating
Scheduled sending
Analytics and tracking
Attachment handling via URLs
Webhook notifications
Batch sending
SMTP: Often enforced but not transparent. You discover limits when connections are rejected or throttled.
API: Clearly documented rate limits. HTTP headers often include remaining quota (X-RateLimit-Remaining).
SMTP: Limited visibility. Must parse logs and SMTP responses. Difficult to trace individual emails.
API: Request IDs, detailed logs, dashboard analytics, and webhook events provide comprehensive visibility.
SMTP:
Connection setup: 100-500ms (TCP + TLS handshake)
Authentication: 50-200ms
Email transmission: 50-300ms
Total per email: 200-1000ms
With connection reuse, subsequent emails: 50-300ms each.
API:
HTTP request: 100-500ms (including TLS)
Total per email: 100-500ms
APIs are often faster for single emails due to optimized infrastructure.
SMTP: Can send multiple emails over a single connection (pipelining). Ideal for high-volume batch sending when implemented correctly.
API: Limited by HTTP request overhead, but easier to parallelize. Most services support batch endpoints for sending multiple emails in one request.
SMTP: Lower bandwidth for multiple emails on same connection. More CPU for managing connections.
API: Slightly higher bandwidth due to HTTP overhead. Lower CPU due to simpler implementation.
SMTP:
STARTTLS (port 587): Upgrades connection to TLS
Implicit TLS (port 465): TLS from connection start
Requires proper certificate validation
API:
Always uses HTTPS (TLS 1.2+)
Certificate validation handled by HTTP client
More foolproof for developers
SMTP: Username/password transmitted during authentication. If connection isn't encrypted, credentials can be intercepted.
API: API keys in HTTPS headers. Less vulnerable to credential stuffing attacks. Easier to implement key rotation.
SMTP: Typically all-or-nothing access to send emails.
API: Granular permissions. Different API keys for different environments or applications. Can restrict by IP, domain, or feature.
High-volume batch sending: Sending thousands of emails in short bursts where connection reuse matters
Legacy system integration: Working with older systems that only support SMTP
Self-hosted infrastructure: Running your own mail server
Protocol requirements: Specific SMTP features or server-to-server communication needed
Cost sensitivity: Some SMTP relay services are cheaper at high volumes
Modern application development: Building new applications or microservices
Need advanced features: Templates, scheduling, analytics, webhooks
Developer experience priority: Faster implementation and easier debugging
Dynamic content: Frequently changing email content, personalization
Multi-channel communication: Email is one of several notification channels
Many email services offer both SMTP and API access. Consider using both:
SMTP for high-volume transactional emails (receipts, notifications)
API for marketing campaigns, complex templates, or emails requiring tracking
Benefits:
Simpler codebase
Better error handling
Access to advanced features
Challenges:
API rate limits may differ from SMTP limits
Need to update all sending code
Testing in staging environment required
Benefits:
Greater control over email delivery
Potentially lower costs at scale
No vendor lock-in to specific API format
Challenges:
More complex error handling
Need to implement retry logic
Connection management overhead
Both SMTP and APIs ultimately deliver via SMTP to recipient servers. Deliverability depends on:
Sender reputation: Your domain and IP reputation
Content quality: Avoiding spam triggers
Recipient engagement: Open rates, spam complaints
The sending method (SMTP vs API) doesn't directly impact deliverability, but APIs often provide better tools for monitoring and improving it.
SMTP:
from email.mime.base import MIMEBase
from email import encoders
msg = MIMEMultipart()
# ... set headers ...
with open("document.pdf", "rb") as f:
part = MIMEBase("application", "pdf")
part.set_payload(f.read())
encoders.encode_base64(part)
part.add_header("Content-Disposition", "attachment; filename=document.pdf")
msg.attach(part)
API:
import base64
with open("document.pdf", "rb") as f:
encoded = base64.b64encode(f.read()).decode()
payload = {
"to": "[email protected]",
"subject": "Document attached",
"text": "Please see attached",
"attachments": [{
"filename": "document.pdf",
"content": encoded,
"type": "application/pdf"
}]
}
SMTP:
from email.mime.text import MIMEText
html = """
<html>
<body>
<h1>Welcome!</h1>
<p>Thanks for signing up.</p>
</body>
</html>
"""
msg = MIMEMultipart('alternative')
msg.attach(MIMEText("Welcome! Thanks for signing up.", 'plain'))
msg.attach(MIMEText(html, 'html'))
API:
payload = {
"to": "[email protected]",
"subject": "Welcome",
"text": "Welcome! Thanks for signing up.",
"html": "<h1>Welcome!</h1><p>Thanks for signing up.</p>"
}
Low volume (0-10K/month): Often free
Medium volume (10K-100K/month): $10-50/month
High volume (100K-1M/month): $50-200/month
Low volume (0-10K/month): $0-15/month
Medium volume (10K-100K/month): $15-80/month
High volume (100K-1M/month): $80-300/month
API services typically cost 20-50% more but include additional features that might require separate tools with SMTP-only services.
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger('smtp')
server = smtplib.SMTP(smtp_server, smtp_port)
server.set_debuglevel(1) # Enable debug output
Debug output shows protocol-level communication but requires parsing for structured logging.
response = requests.post(api_url, json=payload, headers=headers)
log_data = {
"email_id": response.json().get("id"),
"status": response.status_code,
"recipient": payload["to"],
"timestamp": datetime.now().isoformat()
}
logger.info("Email sent", extra=log_data)
Structured JSON responses integrate naturally with logging systems.
import time
def send_with_retry(to_email, subject, body, max_retries=3):
for attempt in range(max_retries):
try:
return send_email_smtp(to_email, subject, body)
except smtplib.SMTPException as e:
if attempt == max_retries - 1:
raise
wait_time = 2 ** attempt # Exponential backoff
time.sleep(wait_time)
Most HTTP libraries have built-in retry mechanisms:
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
session = requests.Session()
retry = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504]
)
adapter = HTTPAdapter(max_retries=retry)
session.mount('https://', adapter)
import smtplib
from unittest.mock import Mock, patch
def test_email_sending():
with patch('smtplib.SMTP') as mock_smtp:
mock_server = Mock()
mock_smtp.return_value = mock_server
send_email_smtp("[email protected]", "Test", "Body")
mock_server.starttls.assert_called_once()
mock_server.login.assert_called_once()
mock_server.send_message.assert_called_once()
import responses
@responses.activate
def test_email_api():
responses.add(
responses.POST,
'https://api.emailservice.com/v1/send',
json={"id": "msg_123", "status": "queued"},
status=200
)
result = send_email_api("[email protected]", "Test", "Body")
assert result["id"] == "msg_123"
API testing is generally simpler with HTTP mocking libraries.
Ask yourself these questions:
What's your email volume?
< 1,000/month: Either works, API is easier
1,000-100,000/month: Either works well
100,000/month: Consider SMTP for cost efficiency
What features do you need?
Basic sending only: SMTP sufficient
Templates, analytics, webhooks: API preferred
What's your team's expertise?
Comfortable with HTTP/REST: API
Experienced with mail servers: SMTP
What's your infrastructure?
Cloud-native/microservices: API
Traditional server setup: Either
How important is developer velocity?
Critical: API
Not a priority: Either
Neither SMTP nor APIs are universally better. The right choice depends on your specific requirements:
Choose SMTP if you need maximum control, have high volumes, or work with legacy systems
Choose API if you want faster development, better debugging, or advanced features
Use both if you have different types of emails with different requirements
Most modern applications benefit from starting with an API for ease of use, then potentially adding SMTP for high-volume use cases as they scale.
The email service provider you choose matters more than the protocol. Look for providers offering both options, strong deliverability, good documentation, and responsive support.
Related Articles
Why AutoSend Doesn’t Have a Free Plan
How protecting deliverability, reliability, and focus shaped our decision to not offer the free plan.
Akash Bhadange • 11 Nov 2025 • engineering
Avoid These 5 Mistakes While Sending Transactional Emails
Learn the common mistakes teams make when sending transactional emails and how to fix them for better deliverability, clarity, and compliance.
Akash Bhadange • 14 Oct 2025 • engineering
The Hidden Cost of Poor Email Deliverability
Poor email deliverability hurts trust, engagement, and revenue. Learn how to protect your domain and improve email deliverability with AutoSend.
Akash Bhadange • 01 Nov 2025 • email marketing
Do Transactional Emails Need an Unsubscribe Link?
Learn when transactional emails need an unsubscribe link, when they don’t, and how to stay compliant with CAN-SPAM and GDPR rules.
Akash Bhadange • 01 Nov 2025 • email marketing

Start sending better emails today!
Transactional emails, marketing campaigns, and everything in between. No clutter. No surprises. Just fast and reliable deliverability.
Solutions