What's Actually Happening

SendGrid API accepts email requests but emails are not delivered. Recipients don't receive emails sent through SendGrid.

The Error You'll See

```python import sendgrid sg = sendgrid.SendGridAPIClient(api_key='SG.xxx') response = sg.send(message)

# Response 202 but no email received ```

API response:

json
{
  "message": "success"
}
// But email never arrives

Bounce notification:

bash
Email bounced: 550 5.7.1 Unauthenticated email from domain.com

No logs:

bash
# SendGrid Activity Feed shows no entries

Why This Happens

  1. 1.Sender not verified - Domain/email not authenticated
  2. 2.API key invalid - Wrong or revoked API key
  3. 3.SPF/DKIM not configured - Email authentication missing
  4. 4.Email in spam - Delivered but marked as spam
  5. 5.Rate limiting - Sending limit exceeded
  6. 6.Invalid recipient - Wrong email address

Step 1: Verify API Key

```bash # Test API key: curl -X "GET" "https://api.sendgrid.com/v3/user/account" \ -H "Authorization: Bearer YOUR_API_KEY"

# Should return account info, not 401

# Check API key permissions: # SendGrid Dashboard -> Settings -> API Keys

# Required permissions: # - Mail Send: Mail Send # - Mail Send: Template Engine (if using templates)

# Create new API key: curl -X POST https://api.sendgrid.com/v3/api_keys \ -H "Authorization: Bearer PARENT_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name":"new-key","scopes":["mail.send"]}' ```

Step 2: Check Sender Verification

```bash # Check verified senders: curl -X GET "https://api.sendgrid.com/v3/verified_senders" \ -H "Authorization: Bearer YOUR_API_KEY"

# In SendGrid Dashboard: # Settings -> Sender Authentication

# Option 1: Single Sender Verification: # Settings -> Sender Authentication -> Verify a Single Sender

# Option 2: Domain Authentication: # Settings -> Sender Authentication -> Authenticate a Domain

# Add DNS records: # - CNAME record for DKIM # - CNAME record for domain verification

# Verify DNS records: dig CNAME s1._domainkey.yourdomain.com dig CNAME s2._domainkey.yourdomain.com

# Check authentication status: curl -X GET "https://api.sendgrid.com/v3/whitelabel/domains" \ -H "Authorization: Bearer YOUR_API_KEY" ```

Step 3: Configure SPF and DKIM

```bash # SPF Record - Add to DNS TXT:

yourdomain.com. IN TXT "v=spf1 include:sendgrid.net -all"

# Check SPF: dig TXT yourdomain.com | grep spf

# DKIM - SendGrid provides CNAME records:

# Add these CNAME records: s1._domainkey.yourdomain.com -> s1.domainkey.sendgrid.net s2._domainkey.yourdomain.com -> s2.domainkey.sendgrid.net

# Check DKIM: dig CNAME s1._domainkey.yourdomain.com

# DMARC (recommended): _dmarc.yourdomain.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com"

# Verify with online tools: # https://mxtoolbox.com/SenderPolicyFramework.aspx # https://mxtoolbox.com/dkim.aspx

# In SendGrid Dashboard: # Settings -> Sender Authentication -> Domain Authentication ```

Step 4: Check Email Activity

```bash # View email activity: curl -X GET "https://api.sendgrid.com/v3/messages?limit=10" \ -H "Authorization: Bearer YOUR_API_KEY"

# Check for specific email: curl -X GET "https://api.sendgrid.com/v3/messages?query=to_email%3Arecipient%40example.com" \ -H "Authorization: Bearer YOUR_API_KEY"

# In Dashboard: # Activity -> Email Activity

# Filter by: # - Processed # - Delivered # - Opened # - Clicked # - Bounced # - Dropped # - Spam Report

# Check suppression list: curl -X GET "https://api.sendgrid.com/v3/suppression/bounces" \ -H "Authorization: Bearer YOUR_API_KEY"

# Check blocks: curl -X GET "https://api.sendgrid.com/v3/suppression/blocks" \ -H "Authorization: Bearer YOUR_API_KEY" ```

Step 5: Test Email Sending

```bash # Test with curl: curl -X POST https://api.sendgrid.com/v3/mail/send \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "personalizations": [{ "to": [{"email": "recipient@example.com"}], "subject": "Test Email" }], "from": {"email": "sender@yourdomain.com"}, "content": [{ "type": "text/plain", "value": "This is a test email" }] }'

# Response should be 202 Accepted

# Test with Python: import sendgrid from sendgrid.helpers.mail import Mail

sg = sendgrid.SendGridAPIClient(api_key='SG.xxx') message = Mail( from_email='sender@yourdomain.com', to_emails='recipient@example.com', subject='Test Email', html_content='<strong>Test content</strong>' ) response = sg.send(message) print(response.status_code) # Should be 202 ```

Step 6: Check Spam Folders

```bash # Email might be delivered to spam

# Check spam score: # Use mail-tester.com or similar

# Improve deliverability:

# 1. Use proper From name: "Company Name <noreply@yourdomain.com>"

# 2. Add plain text version: "content": [ {"type": "text/plain", "value": "Plain text version"}, {"type": "text/html", "value": "<html>...</html>"} ]

# 3. Avoid spam triggers: # - ALL CAPS # - Too many links # - Spam trigger words (FREE, CLICK NOW, etc.)

# 4. Include unsubscribe link: "content": [{ "type": "text/html", "value": "... <a href='{{{unsubscribe}}}'>Unsubscribe</a>" }]

# 5. Set proper headers: "headers": { "List-Unsubscribe": "<mailto:unsubscribe@yourdomain.com>" } ```

Step 7: Check Rate Limits

```bash # Check account limits: curl -X GET "https://api.sendgrid.com/v3/user/credits" \ -H "Authorization: Bearer YOUR_API_KEY"

# Free tier limits: # - 100 emails/day # - Limited to 30 days trial

# Check sending limits: curl -X GET "https://api.sendgrid.com/v3/user/email_limits" \ -H "Authorization: Bearer YOUR_API_KEY"

# If hitting limits: # - Upgrade plan # - Wait for limit reset (daily)

# Check for throttling in response: # 429 Too Many Requests

# Implement retry logic: import time from sendgrid.exceptions import SendGridException

max_retries = 3 for attempt in range(max_retries): try: response = sg.send(message) break except SendGridException as e: if e.status_code == 429: time.sleep(60) continue raise ```

Step 8: Check Suppression Lists

```bash # List bounces: curl -X GET "https://api.sendgrid.com/v3/suppression/bounces" \ -H "Authorization: Bearer YOUR_API_KEY"

# Remove from bounce list: curl -X DELETE "https://api.sendgrid.com/v3/suppression/bounces/recipient@example.com" \ -H "Authorization: Bearer YOUR_API_KEY"

# Check blocks: curl -X GET "https://api.sendgrid.com/v3/suppression/blocks" \ -H "Authorization: Bearer YOUR_API_KEY"

# Check spam reports: curl -X GET "https://api.sendgrid.com/v3/suppression/spam_reports" \ -H "Authorization: Bearer YOUR_API_KEY"

# Check unsubscribes: curl -X GET "https://api.sendgrid.com/v3/suppression/unsubscribes" \ -H "Authorization: Bearer YOUR_API_KEY"

# Delete from suppression list: curl -X DELETE "https://api.sendgrid.com/v3/suppression/blocks/recipient@example.com" \ -H "Authorization: Bearer YOUR_API_KEY" ```

Step 9: Use Webhooks for Tracking

```python # Set up webhook to track email events: # Settings -> Mail Settings -> Event Notification

# Webhook events: { "email": "recipient@example.com", "event": "delivered", # or bounced, dropped, etc. "sg_event_id": "xxx", "sg_message_id": "xxx", "reason": "...", # For bounces "status": "..." # HTTP status }

# Events to monitor: # - processed - Email received by SendGrid # - delivered - Email delivered to recipient server # - opened - Email opened # - clicked - Link clicked # - bounced - Email bounced # - dropped - Email not sent # - deferred - Delivery deferred # - spamreport - Marked as spam # - unsubscribe - Recipient unsubscribed

# Flask webhook handler: from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/webhook', methods=['POST']) def webhook(): events = request.json for event in events: print(f"Event: {event['event']} for {event['email']}") return jsonify({'status': 'ok'}) ```

Step 10: SendGrid Verification Script

```bash # Create verification script: cat << 'EOF' > /usr/local/bin/check-sendgrid.sh #!/bin/bash

API_KEY=$1

echo "=== API Key Test ===" curl -s -X GET "https://api.sendgrid.com/v3/user/account" \ -H "Authorization: Bearer $API_KEY" | jq .

echo "" echo "=== Sender Authentication ===" curl -s -X GET "https://api.sendgrid.com/v3/whitelabel/domains" \ -H "Authorization: Bearer $API_KEY" | jq '.[] | {domain: .domain, valid: .valid}'

echo "" echo "=== Verified Senders ===" curl -s -X GET "https://api.sendgrid.com/v3/verified_senders" \ -H "Authorization: Bearer $API_KEY" | jq '.results[] | .email'

echo "" echo "=== Recent Activity ===" curl -s -X GET "https://api.sendgrid.com/v3/messages?limit=5" \ -H "Authorization: Bearer $API_KEY" | jq '.messages[] | {to: .to_email, status: .status}'

echo "" echo "=== Suppression Counts ===" echo "Bounces: $(curl -s -X GET "https://api.sendgrid.com/v3/suppression/bounces" -H "Authorization: Bearer $API_KEY" | jq 'length')" echo "Blocks: $(curl -s -X GET "https://api.sendgrid.com/v3/suppression/blocks" -H "Authorization: Bearer $API_KEY" | jq 'length')" echo "Spam Reports: $(curl -s -X GET "https://api.sendgrid.com/v3/suppression/spam_reports" -H "Authorization: Bearer $API_KEY" | jq 'length')"

echo "" echo "=== Recommendations ===" echo "1. Verify sender domain authentication" echo "2. Check SPF/DKIM/DMARC records" echo "3. Review suppression lists" echo "4. Test with different recipient" echo "5. Check spam folder" echo "6. Review email content for spam triggers" EOF

chmod +x /usr/local/bin/check-sendgrid.sh

# Usage: /usr/local/bin/check-sendgrid.sh SG.xxx ```

SendGrid Checklist

CheckExpected
API key validAccount info returned
Sender verifiedIn verified senders list
Domain authenticatedDNS records present
SPF recordv=spf1 include:sendgrid.net
DKIM CNAMERecords resolve
Not in suppressionRecipient not blocked

Verify the Fix

```bash # After fixing SendGrid issues

# 1. Test API key curl -X GET "https://api.sendgrid.com/v3/user/account" -H "Authorization: Bearer KEY" // Account info returned

# 2. Check sender verification curl -X GET "https://api.sendgrid.com/v3/verified_senders" -H "Authorization: Bearer KEY" // Sender listed

# 3. Send test email curl -X POST https://api.sendgrid.com/v3/mail/send ... // 202 Accepted

# 4. Check activity # Dashboard -> Activity // Email shows delivered

# 5. Check recipient inbox // Email in inbox (check spam)

# 6. Test various recipients // Emails delivered to all ```

  • [Fix Email Not Sending](/articles/fix-email-not-sending)
  • [Fix Email SPF Record Not Validating](/articles/fix-email-spf-record-not-validating)
  • [Fix Email Bounce Error](/articles/fix-email-bounce-error)