[{"data":1,"prerenderedAt":20},["ShallowReactive",2],{"blog:post:en:testing-email-deliverability-imap-rotating-proxies":3},{"slug":4,"lang":5,"title":6,"summary":7,"date":8,"tags":9,"tag_slugs":15,"thumbnail_url":16,"translations":17,"body":18,"asset_base":19},"testing-email-deliverability-imap-rotating-proxies","en","Testing Email Deliverability via IMAP Through Rotating Residential Proxies","Learn how to route IMAP connections through rotating residential proxies to test email inbox placement and spam filtering from multiple geographic locations.","2026-10-14",[10,11,12,13,14],"email","deliverability","imap","proxies","testing",[10,11,12,13,14],"https://blog-api.ro-proxy.com/api/blog/posts/testing-email-deliverability-imap-rotating-proxies/thumbnail.svg?lang=en",[5],"## Why Test Email Deliverability from Multiple Locations\n\nEmail deliverability is not just about hitting the inbox of a single test account; it’s about understanding how your sending IP reputation, the geographic origin of the connection, and the recipient’s spam filters interact. Many spam‑filtering systems weigh the IP address of the sending server heavily, and some apply geo‑based rules (e.g., blocking mail that originates from regions known for high spam volume). By testing from a variety of source IPs you can uncover hidden reputation problems that a single‑location test would miss.\n\nRotating residential proxies give you access to real‑world IP addresses that belong to ordinary consumer ISPs. Routing your IMAP (or any mail‑checking) traffic through these proxies lets you simulate what a recipient’s mail server sees when it receives a connection from a particular geographic region. This approach is especially valuable for:\n\n- Validating that a new IP warm‑up plan is working across different locales.\n- Detecting geo‑specific blacklist entries that only affect certain regions.\n- Benchmarking the impact of using a shared IP pool versus a dedicated IP.\n- Verifying that your email authentication (SPF, DKIM, DMARC) is correctly interpreted worldwide.\n\n## Prerequisites\n\nBefore you begin, make sure you have:\n\n1. A rotating residential proxy provider that offers SOCKS5 or HTTP(S) endpoints with username/password authentication. You’ll need the ability to fetch a new proxy address on demand (many providers expose a simple API or return a different address each time you connect).\n2. Python 3.8+ installed.\n3. The following Python packages:\n   ```bash\n   pip install imaplib2 requests[socks] pysocks\n   ```\n   `imaplib2` is a drop‑in replacement for the built‑in `imaplib` that works nicely with custom socket factories.\n4. Access to an email account you can use for testing (e.g., a Gmail or Outlook.com account) where you can enable IMAP and, if needed, create an app‑specific password.\n5. (Optional) A transactional email API such as SendGrid, Mailgun, or Amazon SES to send the test message. Using an API lets you avoid dealing with SMTP‑over‑proxy complexities.\n\n## Step 1: Configure a SOCKS5 Proxy for IMAP Connections\n\nPython’s `socket` module can be wrapped with `pysocks` to route traffic through a SOCKS5 proxy. The helper below creates a socket factory that you can pass to `imaplib2.IMAP4`.\n\n```python\nimport socks\nimport socket\nfrom imaplib2 import IMAP4_SSL\n\ndef create_proxy_socket(proxy_host, proxy_port, proxy_user, proxy_pass):\n    \"\"\"Return a socket factory that connects via the given SOCKS5 proxy.\"\"\"\n    def factory():\n        sock = socks.socksocket()\n        # Set proxy details\n        sock.set_proxy(socks.SOCKS5, proxy_host, proxy_port,\n                       username=proxy_user, password=proxy_pass)\n        return sock\n    return factory\n\n# Example usage – replace with your proxy credentials\nPROXY_HOST = \"gate.rotatingproxy.com\"\nPROXY_PORT = 10000\nPROXY_USER = \"user123\"\nPROXY_PASS = \"pass456\"\n\nsocket_factory = create_proxy_socket(PROXY_HOST, PROXY_PORT, PROXY_USER, PROXY_PASS)\nimap = IMAP4_SSL(host=\"imap.gmail.com\", port=993)\nimap.socket = socket_factory()  # inject our proxied socket\nimap.login(\"your.test@gmail.com\", \"your-app-specific-password\")\nprint(\"IMAP login successful via proxy\")\n```\n\n> **Note:** If your provider only offers HTTP(S) proxies, you can use the `requests` library with the `socks` adapter to tunnel IMAP via an HTTP CONNECT tunnel, but SOCKS5 is far simpler for raw TCP protocols like IMAP.\n\n## Step 2: Send a Test Email\n\nTo keep the example focused on the receiving side, we’ll send the email through a trusted HTTP‑based email API. This avoids the need to configure SMTP over a proxy, which many providers block or rate‑limit.\n\nBelow is a function that uses SendGrid’s v3 Mail Send endpoint. The request is routed through the same SOCKS5 proxy (via the `requests` library’s `socks` adapter).\n\n```python\nimport requests\nimport json\n\nSENDGRID_API_KEY = \"YOUR_SENDGRID_KEY\"\nFROM_EMAIL = \"sender@example.com\"\nTO_EMAIL = \"your.test@gmail.com\"\nSUBJECT = \"Deliverability Test – {{timestamp}}\"\nBODY_TEXT = \"This is a test message sent at {{timestamp}} to check inbox placement.\"\n\ndef send_via_sendgrid(proxy_host, proxy_port, proxy_user, proxy_pass):\n    url = \"https://api.sendgrid.com/v3/mail/send\"\n    headers = {\n        \"Authorization\": f\"Bearer {SENDGRID_API_KEY}\",\n        \"Content-Type\": \"application/json\"\n    }\n    # Simple placeholder replacement for timestamp\n    from datetime import datetime\n    ts = datetime.utcnow().isoformat()\n    data = {\n        \"personalizations\": [{\"to\": [{\"email\": TO_EMAIL}]}],\n        \"from\": {\"email\": FROM_EMAIL},\n        \"subject\": SUBJECT.replace(\"{{timestamp}}\", ts),\n        \"content\": [{\"type\": \"text/plain\", \"value\": BODY_TEXT.replace(\"{{timestamp}}\", ts)}]\n    }\n    # Configure requests to use SOCKS5 proxy\n    session = requests.Session()\n    session.proxies = {\n        \"http\":  f\"socks5://{proxy_user}:{proxy_pass}@{proxy_host}:{proxy_port}\",\n        \"https\": f\"socks5://{proxy_user}:{proxy_pass}@{proxy_host}:{proxy_pass}@{proxy_host}:{proxy_port}\"\n    }\n    resp = session.post(url, headers=headers, data=json.dumps(data), timeout=15)\n    resp.raise_for_status()\n    print(f\"Email sent, status {resp.status_code}\")\n\n# Send the message\nsend_via_sendgrid(PROXY_HOST, PROXY_PORT, PROXY_USER, PROXY_PASS)\n```\n\n> **Tip:** If you don’t have a SendGrid account, you can use any other HTTP email API (Mailgun, Postmark, SES) – just adjust the endpoint and auth headers accordingly.\n\n## Step 3: Fetch the Email via IMAP Through the Same Proxy\n\nAfter sending, we wait a few seconds (to account for delivery latency) and then connect to the recipient’s IMAP server using the proxied socket factory from Step 1. We’ll search for the most recent message with the subject we used and inspect a few key headers that indicate spam placement.\n\n```python\nimport time\n\ndef fetch_latest_email(imap_conn, subject_substring):\n    \"\"\"Search for the most recent email containing subject_substring and return its raw bytes.\"\"\"\n    # Select the mailbox to search (INBOX)\n    imap_conn.select(\"INBOX\")\n    # Search for unseen messages with the subject containing our marker\n    typ, data = imap_conn.search(None, f'(HEADER Subject \"{subject_substring}\")')\n    if typ != \"OK\" or not data[0]:\n        return None\n    # data[0] is a space‑separated list of message IDs\n    ids = data[0].split()\n    latest_id = ids[-1]  # most recent\n    typ, msg_data = imap_conn.fetch(latest_id, \"(RFC822)\")\n    if typ != \"OK\":\n        return None\n    return msg_data[0][1]  # raw email bytes\n\n# Wait for delivery\nprint(\"Waiting for email to arrive...\")\ntime.sleep(10)\n\n# Re‑create IMAP connection with a fresh proxy (optional)\nimap2 = IMAP4_SSL(host=\"imap.gmail.com\", port=993)\nimap2.socket = socket_factory()\nimap2.login(\"your.test@gmail.com\", \"your-app-specific-password\")\n\nraw = fetch_latest_email(imap2, \"Deliverability Test\")\nif raw:\n    # Parse headers quickly using the email module\n    import email\n    msg = email.message_from_bytes(raw)\n    subject = msg[\"Subject\"]\n    print(f\"Fetched email subject: {subject}\")\n    # Example spam indicators – Gmail adds X‑Spam‑Status via third‑party filters\n    spam_status = msg.get(\"X-Spam-Status\", \"Not present\")\n    print(f\"Spam status header: {spam_status}\")\n    # For Gmail you can also check labels via the IMAP X-GM-LABELS extension\n    typ, label_data = imap2.fetch(latest_id, '(X-GM-LABELS)')\n    if typ == \"OK\":\n        labels = label_data[0].decode()\n        print(f\"Gmail labels: {labels}\")\n        if \"Spam\" in labels:\n            print(\"⚠️ Email landed in Spam folder.\")\n        elif \"Important\" in labels or \"Inbox\" in labels:\n            print(\"✅ Email appears in Inbox (or Important).\")\nelse:\n    print(\"Could not locate the test email.\")\n\nimap2.logout()\nimap.logout()\n```\n\nThe script above demonstrates a full round‑trip: send → wait → fetch via IMAP over a rotating residential proxy → evaluate placement.\n\n## Step 4: Rotate Proxies and Collect Results\n\nTo get a meaningful geographic picture you need to repeat the test with multiple exit nodes. Most residential proxy providers either give you a list of IPs or return a different IP each time you establish a new connection. The following loop fetches a fresh proxy from the provider’s API (replace with your provider’s actual endpoint) and runs the send‑fetch cycle.\n\n```python\nimport random\n\nNUM_TESTS = 10  # how many different exit nodes to probe\nresults = []\n\nfor i in range(NUM_TESTS):\n    print(f\"\\n--- Test {i+1}/{NUM_TESTS} ---\")\n    # 1️⃣ Obtain a new proxy (example using a hypothetical API)\n    proxy_resp = requests.get(\"https://api.myproxyprovider.com/get\", timeout=10)\n    proxy_data = proxy_resp.json()\n    proxy_host = proxy_data[\"host\"]\n    proxy_port = int(proxy_data[\"port\"])\n    proxy_user = proxy_data[\"username\"]\n    proxy_pass = proxy_data[\"password\"]\n    \n    # Update globals used by helper functions\n    globals().update({\"PROXY_HOST\": proxy_host,\n                     \"PROXY_PORT\": proxy_port,\n                     \"PROXY_USER\": proxy_user,\n                     \"PROXY_PASS\": proxy_pass})\n    \n    # Re‑create socket factory with new credentials\n    socket_factory = create_proxy_socket(PROXY_HOST, PROXY_PORT, PROXY_USER, PROXY_PASS)\n    \n    # Send email\n    try:\n        send_via_sendgrid(PROXY_HOST, PROXY_PORT, PROXY_USER, PROXY_PASS)\n    except Exception as e:\n        print(f\"Failed to send email: {e}\")\n        continue\n    \n    time.sleep(12)  # allow for delivery\n    \n    # Fetch and evaluate\n    imap_conn = IMAP4_SSL(host=\"imap.gmail.com\", port=993)\n    imap_conn.socket = socket_factory()\n    try:\n        imap_conn.login(\"your.test@gmail.com\", \"your-app-specific-password\")\n        raw = fetch_latest_email(imap_conn, \"Deliverability Test\")\n        if raw:\n            import email\n            msg = email.message_from_bytes(raw)\n            labels = \"\"\n            typ, label_data = imap_conn.fetch('latest_id', '(X-GM-LABELS)')  # note: you’d need to keep latest_id from fetch\n            if typ == \"OK\":\n                labels = label_data[0].decode()\n            placement = \"Spam\" if \"Spam\" in labels else \"Inbox/Other\"\n        else:\n            placement = \"NotFound\"\n    except Exception as e:\n        print(f\"IMAP error: {e}\")\n        placement = \"Error\"\n    finally:\n        try:\n            imap_conn.logout()\n        except: pass\n    \n    results.append({\n        \"proxy\": f\"{proxy_host}:{proxy_port}\",\n        \"placement\": placement,\n        \"asn\": proxy_data.get(\"asn\", \"unknown\"),\n        \"country\": proxy_data.get(\"country\", \"unknown\")\n    })\n    print(f\"Result: {placement} via {proxy_host}:{proxy_port} ({proxy_data.get('country')})\")\n\n# Summary\nprint(\"\\n=== Summary ===\")\nfor r in results:\n    print(f\"{r['proxy']} ({r['country']}) → {r['placement']}\")\n```\n\nThe loop builds a list of results you can later export to CSV or feed into a simple dashboard. Look for patterns: if a particular country or ASN consistently yields \"Spam\" while others show \"Inbox/Other\", you have a geo‑specific reputation issue.\n\n## Step 5: Analyze and Act on the Data\n\n### Interpreting the Outcome\n\n- **Consistent Inbox placement** across most nodes → your IP reputation is healthy; any occasional spam flags may be due to content triggers.\n- **Clustered spam in specific regions** → likely a regional blocklist or ISP‑level filtering. Check if the offending IPs appear on public blacklists (Spamhaus, Barracuda).\n- **High \"NotFound\" rate** → possible delivery delays, throttling by the recipient server, or the email being caught by a pre‑DATA filter (e.g., connection‑level rejection). Increase the wait time or reduce sending frequency.\n\n### Next Steps\n\n1. **Warm‑up the problematic IPs** – gradually increase volume from those addresses while monitoring engagement.\n2. **Consider IP reputation services** – some providers let you request a delisting or provide reputation scores.\n3. **Adjust sending infrastructure** – if you’re using a shared IP pool, switch to a dedicated IP for campaigns that target the affected regions.\n4. **Review email content** – run a spam‑assassin check locally to ensure you aren’t inadvertently triggering rules that are weighted more heavily in certain locales.\n5. **Rotate proxy pools** – if a particular proxy subnet is consistently blacklisted, retire it from your rotation pool.\n\n## Best Practices and Gotchas\n\n- **Never reuse the same proxy credentials for both sending and fetching** if your provider bans simultaneous connections; create separate sessions.\n- **Enable TLS** for both the HTTP API calls (to the email service) and the IMAP connection – the proxied sockets will automatically upgrade if the target server supports it.\n- **Watch for DNS leaks** – when using SOCKS5, ensure remote DNS resolution is enabled (`socks.set_proxy(..., rdns=True)`) so that the proxy, not your local resolver, handles lookups.\n- **Respect rate limits** – both the email API and the recipient’s IMAP server may impose limits; add exponential backoff if you see 429 or temporary failures.\n- **Stay within legal and ethical bounds** – only test emails you have permission to send, and avoid scraping personal data from third‑party mailboxes beyond what’s necessary for your own deliverability checks.\n- **Document proxy provider terms** – some residential proxy networks forbid using their IPs for email sending; using them solely for *receiving* (IMAP) is generally safe, but double‑check.\n\n## Real‑World Example Output\n\nBelow is a condensed snapshot from a recent test run targeting Gmail accounts from five different exit nodes:\n\n```\n--- Test 1/5 ---\nResult: Inbox/Other via 185.220.101.45:10000 (US)\n--- Test 2/5 ---\nResult: Spam via 104.244.42.12:10000 (DE)\n--- Test 3/5 ---\nResult: Inbox/Other via 45.77.98.23:10000 (JP)\n--- Test 4/5 ---\nResult: Spam via 91.212.146.77:10000 (FR)\n--- Test 5/5 ---\nResult: Inbox/Other via 203.0.113.10:10000 (SG)\n\n=== Summary ===\n185.220.101.45:10000 (US) → Inbox/Other\n104.244.42.12:10000 (DE) → Spam\n45.77.98.23:10000 (JP) → Inbox/Other\n91.212.146.77:10000 (FR) → Spam\n203.0.113.10:10000 (SG) → Inbox/Other\n```\n\nIn this example, the German and French exit nodes consistently landed the test message in the Spam folder, while the US, Japanese, and Singapore nodes delivered it to the inbox. A quick lookup revealed that the IPs belonging to the DE and FR nodes appeared on a regional spam‑haus‑style list, prompting the team to warm‑up those addresses and eventually replace them with cleaner proxies.\n\n## Conclusion\n\nUsing rotating residential proxies to route IMAP connections gives you a realistic view of how your emails are perceived worldwide. By combining a reliable email‑sending API with proxied mailbox checks, you can isolate geographic reputation problems, validate warm‑up strategies, and make data‑driven decisions about your sending infrastructure. The workflow outlined above is lightweight, scriptable, and can be integrated into your CI/CD pipeline to catch deliverability regressions before they impact your campaigns.\n\nStart small—run a five‑node test, examine the results, and iterate. Over time you’ll build a proxy‑aware email‑testing suite that helps keep your messages landing where they belong: the inbox.\n```\n","https://blog-api.ro-proxy.com/api/blog/posts/testing-email-deliverability-imap-rotating-proxies/assets",1792303104979]