Skip to main content
A bounce occurs when an email cannot be delivered. Ark classifies bounces into three types and handles each differently.

Bounce Types

TypeTriggerArk BehaviorSuppression
Hard bounceSMTP 5xx errorImmediate suppressionAutomatic
Soft bounceSMTP 4xx errorRetry up to 18 times (~48h)After all retries fail
NDR bounceServer accepts then sends Non-Delivery ReportWebhook notificationManual — handle via API

Hard Bounces

A hard bounce is a permanent delivery failure. The address is invalid and will never be deliverable. Common causes:
  • Invalid address (user unknown, no such user)
  • Invalid domain (no MX records)
  • Account disabled or deleted
  • Policy rejection (content filtering)
Common SMTP codes:
CodeMeaning
550Mailbox unavailable or not found
551User not local, forwarding-only
552Message rejected by policy
553Invalid mailbox name
554General permanent failure

Ark’s Handling

Immediate suppression on first hard bounce. Unlike some providers that wait for multiple failures, Ark suppresses on the first hard bounce. This protects your sender reputation.
When a hard bounce occurs:
  1. Message status → HardFail
  2. Recipient immediately added to suppression list (reason: hard fail)
  3. Future emails to this address are held (status Held)
  4. MessageDeliveryFailed webhook fires

Soft Bounces

A soft bounce is a temporary failure that may succeed on retry. Common causes: mailbox full, server unavailable, rate limiting, greylisting, connection timeout. Common SMTP codes:
CodeMeaning
421Server temporarily unavailable
450Mailbox busy
451Temporary processing error
452Mailbox full

Ark’s Handling

Ark retries with exponential backoff using the formula (1.3^attempts) x 5 minutes:
AttemptDelayCumulative
15 min5 min
38.5 min20 min
514 min45 min
832 min1.5 hours
1069 min3.5 hours
122.4 hours7 hours
155.6 hours18 hours
1814 hours~48 hours
If all 18 retries fail, the message converts to HardFail and the recipient is suppressed with reason too many soft fails.

NDR Bounces

Sometimes a server accepts a message (250 OK) but later determines it can’t deliver. It sends a Non-Delivery Report (NDR) back to the sender. When Ark receives an NDR:
  1. Original message status → Bounced
  2. MessageBounced webhook fires
NDR bounces do NOT auto-suppress. You must handle the MessageBounced webhook and suppress manually via the API if desired. This differs from HardFail, which triggers automatic suppression.

Suppression List

The suppression list prevents sending to addresses that have bounced. This protects your sender reputation.
EventSuppression ReasonTiming
Hard bounce (5xx)hard failImmediate
Soft bounce exhaustedtoo many soft failsAfter ~48 hours
NDR bounceNone (manual)Your decision
Suppressions expire after 30 days by default. If a retried message succeeds, the address is automatically unsuppressed.

Managing Suppressions

from ark import Ark

client = Ark()

# Check if suppressed
try:
    suppression = client.suppressions.retrieve("user@example.com")
    print(f"Suppressed: {suppression.data.reason}")
except ark.NotFoundError:
    print("Not suppressed")

# Remove from suppression
client.suppressions.delete("user@example.com")
Only remove hard-bounced addresses from suppression if you’ve verified they are now valid. Sending to invalid addresses damages your reputation.

Webhook Handlers

@app.post("/webhooks/ark")
async def handle_webhook(request: Request):
    payload = await request.json()
    status = payload.get("status")

    if status == "HardFail":
        # Automatically suppressed by Ark
        await mark_email_invalid(payload["message"]["to"], "hard_bounce")

    elif status == "SoftFail":
        # Ark will retry — log for diagnostics
        print(f"Soft bounce: {payload.get('classification')}")

    # NDR bounce — check for original_message key
    if "original_message" in payload:
        recipient = payload["original_message"]["to"]
        await mark_email_invalid(recipient, "bounced")
        # Optionally suppress via API
        client.suppressions.create(email=recipient)

    return {"status": "ok"}

Best Practices

Use double opt-in to verify addresses before adding them to your list. Catches typos and fake addresses before they bounce.
Remove subscribers who haven’t engaged in 6+ months. Stale addresses are more likely to bounce.
Subscribe to MessageDeliveryFailed and MessageBounced webhooks to mark addresses as invalid in your user database.
Keep below 2%. Higher rates indicate list quality issues and will harm deliverability.
Start with small volumes to engaged recipients. Sending large volumes immediately increases bounce risk.

Troubleshooting

High Hard Bounce Rate

  1. Check list source: Purchased or scraped lists have high invalid rates
  2. Look for typos: Common mistakes like gmial.com or yaho.com
  3. Verify at signup: Use double opt-in for new subscribers
  4. Clean old lists: Remove addresses that haven’t engaged in 6+ months

Soft Bounces Converting to Hard Fails

  1. Check send volume: You may be hitting rate limits
  2. Review message size: Large attachments cause rejections
  3. Spread sends over time: Avoid bursts to single domains