Skip to content

SendGrid Bounces & Blocks: Handle Undeliverable Emails & Protect Reputation

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about SendGrid Bounces & Blocks: Handle Undeliverable Emails & Protect Reputation. We cover key concepts, practical examples, and best practices to help you master this topic.

Bounces and blocks are undeliverable emails that damage sender reputation. Understanding their types, handling them automatically, and cleaning your list protects deliverability.

What You'll Learn

The difference between hard bounces, soft bounces, and blocks, how to Process bounce Webhooks, implement automatic bounce handling, clean email lists, and monitor bounce rates.

Why It Matters

Bounce rates above 5% trigger ISP blocks. DodaTech maintains under 1% by automatically removing bounced addresses and monitoring bounce webhooks in real-time.

Real-World Use

An email to alice@example.com bounces (address doesn't exist). SendGrid sends a bounce event Webhook. The app removes the address from the active list and logs it for admin review.

flowchart LR
    A["Send Email\nto bob@old.com"] --> B["ISP Rejects\nAddress Invalid"]
    B --> C["SendGrid\nBounce Webhook"]
    C --> D["App Processes\nEvent"]
    D --> E["Remove from\nActive List"]
    D --> F["Log Bounce\n+ Timestamp"]
    E --> G["Future Sends\nSkip Address"]
    style B fill:#fecaca,stroke:#dc2626
    style C fill:#dbeafe,stroke:#2563eb
    style E fill:#bbf7d0,stroke:#16a34a

Understanding Bounce Types

# Hard Bounce: Permanent failure (address doesn't exist)
# Soft Bounce: Temporary failure (mailbox full, server busy)
# Block: ISP rejected (spam, blacklist, rate limit)

# SendGrid categorizes in webhook events:
# {
#   "event": "bounce",
#   "email": "alice@example.com",
#   "bounce_type": "permanent",  # or "transient"
#   "reason": "550 5.1.1 The email account that you tried to reach does not exist"
# }

def classify_bounce(event):
    bounce_type = event.get("bounce_type")
    reason = event.get("reason", "")

    if bounce_type == "permanent":
        print(f"Hard bounce: {event['email']} — remove from list")
        return "remove"
    elif bounce_type == "transient":
        print(f"Soft bounce: {event['email']} — retry later")
        return "retry"
    elif bounce_type == "block":
        print(f"Block: {event['email']} — investigate reason")
        return "investigate"

Processing Bounce Webhooks

from flask import Flask, request, jsonify

app = Flask(__name__)
bounced_emails = set()

@app.route("/sendgrid/bounces", methods=["POST"])
def handle_bounces():
    events = request.get_json()

    for event in events:
        if event["event"] in ("bounce", "block", "dropped"):
            email = event["email"]
            event_type = event["event"]
            reason = event.get("reason", "No reason")

            print(f"[{event_type}] {email}: {reason[:100]}")

            if event_type == "bounce" and event.get("bounce_type") == "permanent":
                bounced_emails.add(email)
                remove_from_active_list(email)
                log_bounce(email, reason)

    return jsonify({"status": "ok"}), 200

def remove_from_active_list(email):
    print(f"Removed {email} from active send list")

def log_bounce(email, reason):
    print(f"Logged bounce: {email}{reason[:50]}...")

Managing Bounces via API

def get_bounce_list():
    response = sg.client.suppression.bounces.get()
    bounces = response.to_dict()
    print(f"Total bounces: {len(bounces)}")
    for b in bounces[:5]:
        print(f"  {b['email']}{b.get('reason', 'N/A')[:60]}")
    return bounces

def delete_bounce(email):
    response = sg.client.suppression.bounces._(email).delete()
    print(f"Removed {email} from bounces: {response.status_code}")

def clear_all_bounces():
    response = sg.client.suppression.bounces.delete()
    print(f"Cleared all bounces: {response.status_code}")

# Get current bounces
get_bounce_list()
# Expected output:
# Total bounces: 15
#   alice@old.com — 550 5.1.1 The email account that you tried to reach does not exist
#   bob@invalid.com — 550 5.1.1 User unknown

Monitoring Bounce Rate

from datetime import datetime, timedelta

def check_bounce_rate(days=7):
    now = datetime.now()
    start = now - timedelta(days=days)

    params = {
        "start_date": start.strftime("%Y-%m-%d"),
        "end_date": now.strftime("%Y-%m-%d"),
        "aggregated_by": "day"
    }

    response = sg.client.stats.get(query_params=params)
    stats = response.to_dict()

    total_sent = 0
    total_bounces = 0

    for day in stats:
        metrics = day.get("stats", [{}])[0].get("metrics", {})
        total_sent += metrics.get("requests", 0)
        total_bounces += metrics.get("bounces", 0)

    bounce_rate = (total_bounces / max(total_sent, 1)) * 100

    print(f"Bounce rate (last {days} days): {bounce_rate:.2f}%")
    print(f"  Sent: {total_sent}")
    print(f"  Bounces: {total_bounces}")

    if bounce_rate > 5:
        print("CRITICAL: Bounce rate exceeds 5% — clean your list immediately!")
    elif bounce_rate > 3:
        print("WARNING: Bounce rate above 3% — review list hygiene")
    else:
        print("OK: Bounce rate is healthy")

check_bounce_rate()
# Expected output: Bounce rate (last 7 days): 0.8%
#                  OK: Bounce rate is healthy

Common Mistakes

1. Not Handling Bounce Webhooks

Ignoring bounce webhooks means you keep sending to invalid addresses, increasing your bounce rate and damaging reputation.

2. Confusing Soft and Hard Bounces

Soft bounces (mailbox full) should be retried. Hard bounces (address invalid) should be removed. Treating both the same wastes resources.

3. Re-sending to Bounced Addresses

Once an address hard bounces, sending again hurts your reputation. Remove it permanently unless the user re-verifies the address.

A sudden bounce rate spike indicates a problem (blacklisting, wrong list segment, ISP issue). Monitor daily bounce rates for anomalies.

5. Cleaning Bounce List Too Aggressively

SendGrid automatically suppresses bounced addresses. Deleting the bounce suppression allows sending to them again. Only clear if the address has been fixed.

Practice Questions

  1. What is the difference between hard bounce and soft bounce?
  2. How do you prevent sending to bounced addresses?
  3. What is a healthy bounce rate?
  4. How do you handle a block event?

Answers:

  1. Hard bounce: permanent failure (address doesn't exist). Soft bounce: temporary issue (mailbox full). Remove hard bounces, retry soft bounces.
  2. Process bounce webhooks and add addresses to a suppression list. Before sending, check the list. SendGrid also auto-suppresses bounces.
  3. Under 3% is good. Under 1% is excellent. Above 5% triggers ISP blocks and damages reputation.
  4. Investigate the block reason (spam content, blacklisted IP, rate limit). Check SendGrid's block list, review email content, and contact the receiving ISP if persistent.

Challenge: Build a bounce management system: process bounce webhooks to auto-remove hard bounces, retry soft bounces (up to 3 times), log all bounces with timestamps, monitor bounce rate daily, and alert if rate exceeds 3%.

FAQ

How long does SendGrid suppress bounces?

SendGrid suppresses hard bounces for 72 hours initially, then extends indefinitely. The suppression is per account, not per IP.

Can I send to a bounced address later?

Only if the address was fixed (e.g., the user provided a new email). Remove the bounce suppression via API before sending.

What is the difference between bounces and blocks?

Bounces are rejected by the receiving mail server (address issues). Blocks are rejected by ISPs (reputation or content issues). Both damage reputation.

How do I reduce bounce rate?

Use email validation on signup, remove inactive users, process bounces automatically, use double opt-in, and clean your list quarterly.

What happens if I exceed the bounce rate limit?

SendGrid may pause your account or throttle sending. Maintain bounce rate under 3% to avoid restrictions.

Mini Project

Build a bounce management and monitoring system: process bounce webhooks (classify hard/soft/block), auto-remove hard bounces from active lists, retry soft bounces with exponential backoff, generate weekly bounce rate reports, and set up alerts for bounce spikes.

What's Next

Event Webhook — receive real-time email delivery events via webhooks.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro