When automating email delivery through Google's SMTP servers (smtp.gmail.com on port 587 or 465), many developers encounter the frustrating 454 error:
SMTP Error: 454 4.7.0 Too many login attempts, please try again later
Google imposes strict rate limits on SMTP authentication attempts to prevent abuse. The thresholds aren't publicly documented, but empirical testing shows:
- Approximately 100-150 emails per hour triggers the block
- Simultaneous connections from the same IP accelerate rate limiting
- New accounts have tighter restrictions than established ones
Here's a Python example that demonstrates proper SMTP connection handling with rate limiting:
import smtplib
import time
from email.mime.text import MIMEText
MAX_EMAILS_PER_HOUR = 100
DELAY_BETWEEN_EMAILS = 36 # seconds (100 emails/hour = ~36s between sends)
def send_email(subject, body, to_email):
msg = MIMEText(body)
msg['Subject'] = subject
msg['From'] = 'your@gmail.com'
msg['To'] = to_email
try:
with smtplib.SMTP('smtp.gmail.com', 587) as server:
server.starttls()
server.login('your@gmail.com', 'your_app_password')
server.send_message(msg)
time.sleep(DELAY_BETWEEN_EMAILS)
except smtplib.SMTPException as e:
print(f"SMTP error occurred: {str(e)}")
if '454 4.7.0' in str(e):
print("Rate limited - waiting 1 hour")
time.sleep(3600)
For serious email delivery needs, consider these approaches:
# Using connection pooling (Python example)
class SMTPConnectionPool:
def __init__(self, max_connections=3):
self.pool = []
self.max_connections = max_connections
def get_connection(self):
if not self.pool or len(self.pool) < self.max_connections:
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login('your@gmail.com', 'your_app_password')
self.pool.append(server)
return server
return random.choice(self.pool)
- Use Google's Gmail API instead of SMTP (higher quota)
- Implement exponential backoff when errors occur
- Distribute sending across multiple Google accounts
- Consider professional SMTP services (SendGrid, Mailgun, etc.)
Always use App Passwords instead of your main Google account password when scripting SMTP access. Enable 2-Step Verification first, then generate app-specific passwords under your Google Account security settings.
When working with Google's SMTP servers (smtp.gmail.com on port 587 or 465), developers often encounter the frustrating 454 error. This occurs when your application makes too many authentication attempts in a short period, triggering Google's security mechanisms to prevent potential brute-force attacks.
Google imposes these restrictions to:
- Prevent account compromise through brute-force attacks
- Limit spam sent through compromised accounts
- Maintain server stability under heavy loads
Here are several approaches to handle this in your code:
1. Implement Exponential Backoff
Add delay between attempts that grows exponentially:
import time
import smtplib
def send_email_with_backoff():
max_retries = 5
base_delay = 5 # seconds
for attempt in range(max_retries):
try:
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login('your_email@gmail.com', 'your_password')
# ... send email ...
break
except smtplib.SMTPException as e:
if '454' in str(e):
wait_time = base_delay * (2 ** attempt)
print(f"Attempt {attempt + 1}: Waiting {wait_time} seconds")
time.sleep(wait_time)
else:
raise
2. Use OAuth 2.0 Instead of Password
Google recommends using OAuth for higher limits:
from google.oauth2 import service_account
import google.auth.transport.requests
from email.message import EmailMessage
import smtplib
credentials = service_account.Credentials.from_service_account_file(
'service-account.json',
scopes=['https://www.googleapis.com/auth/gmail.send']
)
message = EmailMessage()
message['From'] = 'service-account@your-domain.iam.gserviceaccount.com'
message['To'] = 'recipient@example.com'
message['Subject'] = 'Test email'
message.set_content('This is a test email')
with smtplib.SMTP('smtp.gmail.com', 587) as smtp:
smtp.starttls()
smtp.login(credentials.signer_email, credentials.token)
smtp.send_message(message)
3. Queue System with Rate Limiting
For bulk sending, implement a queue with controlled throughput:
from queue import Queue
import threading
import time
email_queue = Queue()
MAX_EMAILS_PER_HOUR = 100 # Stay under Google's limits
def email_worker():
while True:
email_data = email_queue.get()
try:
send_email(email_data)
finally:
time.sleep(3600 / MAX_EMAILS_PER_HOUR) # Spread emails evenly
email_queue.task_done()
def send_email(data):
# Your email sending implementation
pass
# Start worker threads
for i in range(3): # 3 concurrent senders
threading.Thread(target=email_worker, daemon=True).start()
# Add emails to queue
for email in email_list:
email_queue.put(email)
email_queue.join()
For mission-critical applications:
- Use multiple SMTP accounts with round-robin distribution
- Consider commercial email services like SendGrid or Mailgun
- Implement proper error logging and alerting for rate limits
Set up monitoring for 454 errors:
import logging
from prometheus_client import Counter
smtp_454_errors = Counter('smtp_454_errors', 'Count of SMTP 454 errors')
try:
# SMTP operation
except smtplib.SMTPException as e:
if '454' in str(e):
smtp_454_errors.inc()
logging.warning('SMTP rate limit hit: %s', e)