Email Spam Tester

ইমেইল, ইনবক্স ও ডোমেইনের জন্য
স্প্যাম যাচাই API

আমাদের একটি বাস্তব বার্তা পাঠান, অথবা শুধু একটি ডোমেইন দিন। সবকিছুর উত্তর JSON-এ আসে।

কোনো কী নেই, কোনো অ্যাকাউন্ট নেই।

অনুরোধ

# reserve a single-use address

$ curl -sX POST '.../api/v1/inbox?lang=en'

# send your message to that address, then

$ curl -s '.../api/v1/tests/k7m2x9qplw4tz8vn3b6c/status'

# read the report

$ curl -s '.../api/v1/tests/k7m2x9qplw4tz8vn3b6c'

প্রতিক্রিয়া

200 OK

{
  "address": "test-k7m2x9qplw4tz8vn3b6c@t.email-spam-tester.com",
  "slug": "k7m2x9qplw4tz8vn3b6c",
  "expires_at": "2026-10-31T13:00:00Z"
}

200 OK

{
  "analysis_status": "checks_ready",
  "ai_status": "running",
  "checks_done": 42,
  "checks_total": 42
}

200 OK

{
  "score_ours": 86.4,
  "score_compat": 9.2,
  "subscores": {"auth": 100.0, "infra_spam": 78.1, "content": 92.0},
  "complete": true,
  "checks": [{"id": "auth.dmarc", "status": "warn"}],
  "fixes": [{"id": "dmarc-enforce", "gain_ours": 4.0}]
}

অনুরোধ

# create a test

$ curl -sX POST '.../api/v1/placement' \ -d '{"public_results_accepted":true}'

# put the marker in the subject, send once to all, then

$ curl -s '.../api/v1/placement/wR3kP9xLq2vT8mZc'

প্রতিক্রিয়া

201 Created

{
  "marker": "est-3f9a1c7e2b4d6a80c5e1f2d3",
  "status": "waiting",
  "results": [{"provider": "gmail", "placement": "pending"}],
  "fleet": {"status": "observing", "results": [
    {"provider": "yahoo", "placement": "pending"},
    {"provider": "outlook", "placement": "pending"}
  ]}
}

200 OK

{
  "status": "complete",
  "results": [{"provider": "gmail", "placement": "primary"}],
  "fleet": {"status": "finished", "results": [
    {"provider": "yahoo", "placement": "inbox"},
    {"provider": "outlook", "placement": "spam"}
  ]}
}

অনুরোধ

# no email needed, one GET

$ curl -s '.../api/v1/infra/check?domain=example.com'

# compare with your last run

$ curl -s '.../api/v1/infra/check?domain=example.com&diff=true' \ | jq '.diff'

প্রতিক্রিয়া

200 OK

{
  "domain": "example.com",
  "ok": false,
  "status": "fail",
  "score": 78,
  "coverage": {"answered": 7, "total": 8},
  "sections": {"ptr": {"status": "fail", "core": true}}
}

200 OK

{
  "changed": true,
  "worsened": true,
  "status_from": "pass",
  "score_from": 100,
  "changes": [{"path": "dkim.key.mail.sha256", "kind": "removed",
               "severity": "critical", "effect": "worsened"}]
}
এই পৃষ্ঠায়

ইমেইল পরীক্ষা

আমরা একটি বাস্তব বার্তা শুরু থেকে শেষ পর্যন্ত পড়ি: কনটেন্ট, স্বাক্ষর, হেডার এবং যে সার্ভার এটি পাঠিয়েছে। আপনি একটি স্কোর, সংশোধনের একটি তালিকা এবং সেগুলোর পেছনের প্রতিটি যাচাই পাবেন।

দ্রুত শুরু

তিনটি কল। কোনো কী, অ্যাকাউন্ট বা সাইনআপ নয়। দুটি অনুরোধ এবং একটি ইমেইল। তৃতীয় কলটি শুধু এ কারণে প্রয়োজন যে বিশ্লেষণে কয়েক সেকেন্ড সময় লাগে।

  1. একটি ঠিকানা সংরক্ষণ করুন

    ইনবক্স এন্ডপয়েন্টে POST করুন। আপনি এমন একটি ঠিকানা পাবেন যা ঠিক একটি বার্তা গ্রহণ করে এবং এক ঘণ্টার মধ্যে মেয়াদোত্তীর্ণ হয়, সঙ্গে পাবেন সেই স্লাগ যার ভিত্তিতে বাকি সবকিছু নির্ধারিত হয়।

    অনুরোধ

    # 1. reserve a single-use address
    curl -sX POST 'https://email-spam-tester.com/api/v1/inbox?lang=en'

    প্রতিক্রিয়া

    {
      "address": "test-<slug>@t.email-spam-tester.com",
      "slug": "<slug>",
      "expires_at": "2026-01-01T12:00:00Z"
    }
  2. এতে আসল বার্তাটি পাঠান

    SMTP-এর মাধ্যমে, যে প্ল্যাটফর্ম থেকে ক্যাম্পেইনটি পাঠানো হবে সেখান থেকে। পরীক্ষাগুলোর অর্ধেক আপনার পাঠানোর প্ল্যাটফর্মের যোগ করা হেডার পড়ে, তাই হাতে তৈরি করা বার্তা ভুল বিষয় পরিমাপ করে।

    অনুরোধ

    # 2. send your message to that address, from your own platform
    To: test-<slug>@t.email-spam-tester.com
  3. স্ট্যাটাস পোল করুন, তারপর প্রতিবেদনটি পড়ুন

    স্ট্যাটাস এন্ডপয়েন্টের আকার কয়েকশ বাইট এবং এটি আপনাকে জানায় পূর্ণ প্রতিবেদনটি এখন সংগ্রহ করা উপযুক্ত কি না। প্রতিবেদনটি নিজেই কয়েক হাজার লাইন দীর্ঘ।

    অনুরোধ

    # 3. poll
    curl -s 'https://email-spam-tester.com/api/v1/tests/<slug>/status'

    প্রতিক্রিয়া

    {
      "slug": "<slug>",
      "analysis_status": "checks_ready",
      "ai_status": "running",
      "checks_done": 42,
      "checks_total": 42
    }

    অনুরোধ

    # 4. read the report once analysis_status is checks_ready
    curl -s 'https://email-spam-tester.com/api/v1/tests/<slug>'

    প্রতিক্রিয়া

    {
      "report_url": "https://email-spam-tester.com/t/<slug>",
      "score_ours": 86.4,
      "score_compat": 9.2,
      "subscores": {"auth": 100.0, "infra_spam": 78.1, "content": 92.0, "compliance": 75.0},
      "complete": true,
      "checks": [
        {
          "id": "auth.dmarc",
          "status": "warn",
          "title": "DMARC result",
          "summary": "DMARC passes, but the policy is p=none.",
          "weight_ours": -4.0,
          "evidence": {"policy": "none", "aligned": "dkim"},
          "citations": {"standards": [{"title": "RFC 9989 §4.7", "quote": "..."}]}
        }
      ],
      "fixes": [
        {"id": "dmarc-enforce", "gain_ours": 4.0, "gain_compat": 0.0,
         "fix": {"title": "Move DMARC to quarantine", "severity": "medium"}}
      ]
    }

একটি ঠিকানা সংরক্ষণ করুন, আপনার তালিকায় যে বার্তাটি পাঠাতে যাচ্ছিলেন সেটি পাঠান, প্রতিবেদনটি পড়ুন। পৃষ্ঠায় দেখানো সবকিছু JSON-এ রয়েছে, যার মধ্যে আছে স্কোর, প্রমাণসহ ৪২টি পরীক্ষা এবং প্রতিটি ফলাফল যে মানদণ্ডের অনুচ্ছেদের ওপর ভিত্তি করে তার উল্লেখ।

প্রথম 100,000টি রিপোর্ট বিনামূল্যে, কোনো key বা account ছাড়াই। আপনি যদি script-এর বদলে agent সংযুক্ত করেন, তাহলে polling-এর চেয়ে MCP server বেশি উপযুক্ত।

এন্ডপয়েন্ট

মূল ঠিকানা https://email-spam-tester.com। সবকিছু JSON-এ উত্তর দেয়।

POST/api/v1/inbox

একবার ব্যবহারযোগ্য একটি ঠিকানা সংরক্ষণ করে। উভয় প্যারামিটারই ঐচ্ছিক।

langISO কোড হিসেবে যে ভাষায় সংশোধন পরিকল্পনা এবং প্রতিটি পরীক্ষার ফলাফল লেখা হবে। এটি এখনই নির্ধারিত হয়, কারণ বার্তাটি কয়েক মিনিট পরে SMTP-এর মাধ্যমে আসে এবং কে এটির জন্য অপেক্ষা করছে সে বিষয়ে কোনো ইঙ্গিত বহন করে না। অজানা কোড হলে ইংরেজি ব্যবহৃত হয়।
utm_source, utm_medium, utm_campaignআমাদের অ্যানালিটিক্সে পাঠানো হয়। আপনি নিজের ইন্টিগ্রেশন পরিমাপ করলে এটি উপযোগী।

GET/api/v1/tests/{slug}/status

বারবার পোল করা সাশ্রয়ী। দুটি স্ট্যাটাস ফিল্ড এবং পরীক্ষাগুলো কত দূর এগিয়েছে তা ফেরত দেয়, আর কিছু নয়।

রিপোর্টের পরিবর্তে এটি পোল করুন। analysis_status প্রথমে checks_ready-তে পৌঁছায়, এবং AI পরিকল্পনাটি প্রায় এক মিনিট পরে ai_status-এর অধীনে আসে, তাই যে কলার কেবল নির্ধারিত পরীক্ষাগুলো চায় তাকে পরিকল্পনার জন্য অপেক্ষা করতে হয় না।

GET/api/v1/tests/{slug}

সম্পূর্ণ রিপোর্ট। ঠিকানাটি সংরক্ষিত থাকা এবং তখনও কিছু না পৌঁছানো পর্যন্ত 202 দিয়ে উত্তর দেয়, পৌঁছানোর পর 200 দেয়।

GET/api/v1/tests/{slug}/message

বার্তাটি ঠিক যেভাবে ডেলিভার করা হয়েছিল: ওয়্যার ক্রমে প্রতিটি হেডার, উভয় বডি, সংযুক্তির তালিকা এবং র’ সোর্স। আলাদাভাবে আনা হয়, কারণ রিপোর্টটি পোল করা হয় এবং এটি এক মেগাবাইট পর্যন্ত হতে পারে।

কাঁচা বার্তাটি তার নিজস্ব সময়সূচি অনুযায়ী মুছে যায়, রিপোর্টের আগেই। এরপর এই endpoint উৎসের বদলে কারণ জানায়।

PUT/api/v1/tests/{slug}/locale

এখনও ব্যবহার করা হয়নি এমন একটি reservation-এর ভাষা পরিবর্তন করে। এমন একটি agent-এর জন্য, যা ঠিকানা চাওয়ার পর জানতে পারে তার user কোন ভাষা পড়ে। বার্তাটি পৌঁছে গেলে প্রত্যাখ্যাত হয়, কারণ ততক্ষণে analysis চলতে থাকে।

ইনবক্স প্লেসমেন্টবেটা

পরীক্ষামূলক ইনবক্সের একটি তালিকায় একটি বার্তা পাঠান এবং Gmail, Outlook, Yahoo ও অন্যান্য প্রদানকারীর কাছে এটি কোথায় পৌঁছেছে দেখুন: ইনবক্স, প্রোমোশনস বা স্প্যাম।

দ্রুত শুরু

  1. একটি পরীক্ষা তৈরি করুন

    প্লেসমেন্ট এন্ডপয়েন্টে POST করুন। আপনি একটি মার্কার এবং পরীক্ষামূলক ইনবক্সগুলো ফেরত পাবেন।

    অনুরোধ

    # 1. create a test
    curl -sX POST 'https://email-spam-tester.com/api/v1/placement' \
      -H 'Content-Type: application/json' \
      -d '{"public_results_accepted":true}'

    প্রতিক্রিয়া

    {
      "slug": "<slug>",
      "marker": "est-3f9a1c7e2b4d6a80c5e1f2d3",
      "status": "waiting",
      "deadline": "2026-10-31T12:30:00Z",
      "poll_after_seconds": 60,
      "results": [
        {"address": "<seed>", "provider": "gmail", "kind": "Gmail",
         "placement": "pending", "observed_at": null}
      ],
      "fleet": {
        "status": "observing",
        "results": [
          {"address": "<mailbox>", "provider": "yahoo", "available": true,
           "placement": "pending", "coverage": "pending"}
        ]
      }
    }
  2. প্রতিটি ইনবক্সে একটি বার্তা পাঠান

    বিষয়ে মার্কারটি রাখুন এবং আপনার স্বাভাবিক প্ল্যাটফর্ম থেকে সব ঠিকানায় একবারে পাঠান।

    অনুরোধ

    # 2. one message to every address, with the marker in the subject
    Subject: October invoice est-3f9a1c7e2b4d6a80c5e1f2d3
    To: <seed>, <mailbox>, ...
  3. ফোল্ডারগুলোর জন্য পোল করুন

    প্রতিটি ঠিকানায় তার প্রোভাইডার ও ফোল্ডারের তথ্য পূরণ হয়। স্ট্যাটাস waiting থাকা বন্ধ না হওয়া পর্যন্ত পোল করুন।

    অনুরোধ

    # 3. poll for the folders
    curl -s 'https://email-spam-tester.com/api/v1/placement/<slug>'

    প্রতিক্রিয়া

    {
      "status": "complete",
      "received": 2,
      "total": 2,
      "results": [
        {"address": "<seed>", "provider": "gmail", "kind": "Gmail",
         "placement": "primary", "observed_at": "2026-10-31T12:04:11Z"},
        {"address": "<seed>", "provider": "gmail", "kind": "Google Workspace",
         "placement": "promotions", "observed_at": "2026-10-31T12:04:11Z"}
      ],
      "fleet": {
        "status": "finished",
        "results": [
          {"address": "<mailbox>", "provider": "yahoo", "available": true,
           "placement": "inbox", "coverage": "ok"},
          {"address": "<mailbox>", "provider": "outlook", "available": true,
           "placement": "spam", "coverage": "ok"}
        ]
      }
    }

এন্ডপয়েন্ট

An optional test using public GMass seed inboxes and available project test mailboxes, alone or alongside the email score. Explain to the sender that GMass receives their email and publishes its subject, sender name and placement before creating a test. Use all test mailboxes for non-confidential test messages only.

GET /api/v1/placement/availability

Returns providers: the provider identifiers of currently healthy, admitted project mailboxes. No addresses or credentials are exposed. This read does not create a test or contact GMass. Actual recipients are allocated at creation and may differ as availability or capacity changes.

POST /api/v1/placement

Send JSON {"public_results_accepted":true}. Returns HTTP 201 with slug, marker, results (the seed addresses), deadline and expires_at. Limit: 20 tests per IP per UTC day.

For one-send combined testing, first reserve an email score address and include its score_slug in the placement creation JSON. Repeated creation for that score returns the same placement test. Send the marked email to the score address, every returned seed and every available fleet.results address; keep the main /t/<score_slug> report link. Creating a link after the score address was used or expired returns HTTP 409.

GET /api/v1/placement/by-score/{score_slug}

Read and refresh the placement run attached to the main email test. The response has the same format and polling limits as the standalone endpoint below. HTTP 404 means placement was not included or its stored results have been removed.

GET /api/v1/placement/{slug}

Use poll_after_seconds (normally 60 seconds) while the page is open. Upstream errors trigger a shared cooldown. Add the marker to the subject and send through the sender's normal platform to every seed. Observations are saved for 30 days. Collection ends after 30 minutes or when all seeds have results.

status: waiting, complete or timed_out. Each result has an address, provider, kind, placement and observed_at. Placement is primary, promotions, inbox, updates, social, forums, spam, unknown, pending or not_observed. Missing results do not mean Spam; Promotions is an inbox category. provider_error indicates an upstream outage without discarding saved observations. HTTP 404 means unknown, 410 expired, 429 quota exhausted, and 503 no test inboxes available at creation.

The optional fleet object contains independently collected project mailbox results, with status observing, settling or finished. Refresh every 30 seconds until it finishes, even when Gmail has completed. Each fleet result includes address, provider, available, placement, category and coverage. Exclude unavailable addresses from new sends; retain their saved observations. Category data may be unavailable even when the physical folder is known.

If GMass is unavailable at creation but project mailboxes can be allocated, provider is fleet and the top-level seed results are empty. Use fleet.results; do not interpret an empty Gmail list as successful delivery. Existing runs never gain new recipient addresses silently.

অবকাঠামো পরীক্ষা

একটি GET কোনো কিছু না পাঠিয়ে একটি ডোমেইনের mail setup পরীক্ষা করে, ফলাফল সংরক্ষণ করে এবং আপনার সর্বশেষ run-এর পর কী পরিবর্তন হয়েছে তা জানায়। কোনো key প্রয়োজন নেই। cron-এ, ডোমেইন broken হলে বা এর setup গুরুত্বপূর্ণভাবে পরিবর্তিত হলে নিচের line-টি non-zero code দিয়ে exit করে।

দ্রুত শুরু

  1. একটি GET করুন

    ডোমেইনটি দিন। ip আপনার প্রেরণকারী ঠিকানাগুলো যোগ করে, এবং selector যে DKIM সিলেক্টরগুলো পড়তে হবে সেগুলোর নাম দেয়।

    অনুরোধ

    curl -s 'https://email-spam-tester.com/api/v1/infra/check?domain=example.com&selector=mail&diff=true'
  2. ফলাফল পড়ুন

    ok হলো একটি স্ক্রিপ্টের জন্য উত্তর, এবং sections দেখায় কোন অংশটি ব্যর্থ হয়েছে।

    প্রতিক্রিয়া

    {
      "id": "ir_01JB8Y4T2M6K9QZ7H3V5N1XWDA",
      "domain": "example.com",
      "checked_at": "2026-10-31T12:00:00Z",
      "ok": false,
      "status": "fail",
      "score": 78,
      "coverage": {"answered": 7, "total": 8},
      "ips": [{"ip": "203.0.113.5", "sources": ["mx", "spf"], "managed": false}],
      "sections": {
        "dkim": {"status": "pass", "core": true, "findings": [], "data": {}},
        "ptr": {"status": "fail", "core": true, "findings": [], "data": {}}
      },
      "not_checked": [
        {"check": "dkim_signature", "reason": "needs_a_message"},
        {"check": "alignment", "reason": "needs_a_message"}
      ],
      "diff": {
        "baseline": {"id": "ir_01JB2...", "checked_at": "2026-10-24T12:00:00Z"},
        "first_run": false,
        "changed": true,
        "worsened": true,
        "status_from": "pass",
        "score_from": 100,
        "changes": [
          {"path": "dkim.key.mail.sha256", "section": "dkim", "kind": "removed",
           "severity": "critical", "effect": "worsened", "from": "9f2c...", "to": null}
        ],
        "not_compared": [
          {"path": "blocklist_ip.203.0.113.5.bl.spamcop.net.listed", "reason": "lookup_failed"}
        ]
      }
    }
  3. এটি cron-এ রাখুন

    diff=true ব্যবহার করলে প্রতিটি রান আপনার সর্বশেষ রানের সঙ্গে তুলনা করা হয়, এবং jq উত্তরটিকে একটি এক্সিট কোডে রূপান্তর করে।

    অনুরোধ

    # exits non-zero if the domain is broken or its setup changed
    curl -s 'https://email-spam-tester.com/api/v1/infra/check?domain=example.com&selector=mail&diff=true' \
      | jq -e '.ok and (.diff.changed | not)'
domainআবশ্যক। টুলগুলো যা গ্রহণ করে, এটিও তা গ্রহণ করে: একটি ডোমেইন, একটি URL, একটি ইমেল ঠিকানা।
ipআপনার পাঠানোর ঠিকানা। আরও ঠিকানার জন্য এটি পুনরাবৃত্তি করুন, সর্বোচ্চ 6টি পর্যন্ত।
selectorএকটি DKIM সিলেক্টর। আরও সিলেক্টরের জন্য এটি পুনরাবৃত্তি করুন, সর্বোচ্চ 10টি পর্যন্ত। আপনার পাঠানো প্রতিটি সিলেক্টর বিদ্যমান থাকতে হবে, নইলে DKIM বিভাগ ব্যর্থ হবে।
heloআপনার সার্ভার HELO-তে যে নাম দেয়, সেটি তার PTR-এর সঙ্গে তুলনা করা হয়।
accountঅনুরোধটি যাঁর জন্য। অনেক ব্যক্তিকে সেবা দেয় এমন key-এর সঙ্গে এটি আবশ্যক।
checksযে বিভাগগুলো চালানো হবে, কমা দিয়ে আলাদা করা। ডিফল্টভাবে সবগুলো।
difftrue এই রানটিকে ডোমেইনের আপনার সর্বশেষ রানের সঙ্গে তুলনা করে।
since7d, 12h অথবা একটি ISO 8601 তারিখ। ওই মুহূর্তের 12 ঘণ্টার মধ্যে আপনার সবচেয়ে কাছের রানের সঙ্গে তুলনা করে, অন্যথায় তার আগের সর্বশেষ রানের সঙ্গে। diff চালু করে।
baselineতুলনা করার জন্য আপনার কোনো একটি রানের id। diff চালু করে।
freshtrue 60-সেকেন্ডের ক্যাশ এড়িয়ে যায়।

উত্তর পড়া

শেষ হওয়া একটি পরীক্ষা 200 দিয়ে উত্তর দেয়, এবং verdict body-তে থাকে। status হলো প্রয়োজনীয় সেকশনগুলোর মধ্যে সবচেয়ে খারাপটি: spf, dkim, dmarc, ptr, blocklist_ip, blocklist_domain, mx এবং smtp। প্রয়োজনীয় কোনো সেকশনে unknown থাকলে তা warn হিসেবে গণ্য হয়। status pass বা warn হলে ok true হয়। উত্তর দেওয়া প্রয়োজনীয় সেকশনগুলোর ওপর score 0 থেকে 100 পর্যন্ত হয়, এবং error ও unknown-এর কারণে প্রযোজ্য weight-এর অর্ধেকের কম অবশিষ্ট থাকলে এটি null হয়। mta_sts, tls_rpt, bimi, dnssec, dane, ns এবং registration রিপোর্ট ও তুলনা করা হয়, কিন্তু এগুলো ok বা score-কে প্রভাবিত করে না।

একটি সেকশন pass, warn, fail, error (আমরা পরীক্ষা করতে পারিনি), unknown (সিদ্ধান্ত নেওয়ার মতো তথ্য খুব কম) বা skipped (প্রযোজ্য নয়) হতে পারে। skipped ফলাফলে কোনো প্রভাব ফেলে না, এবং কারণটি not_checked-এ থাকে। dkim_signature এবং alignment সবসময় থাকে, কারণ এগুলোর জন্য একটি বাস্তব message প্রয়োজন।

ip ও selector কী পরিবর্তন করে

PTR এবং blocklist ঠিকানাসংক্রান্ত, তাই একটি run-এর জন্য কিছু ঠিকানা প্রয়োজন। এটি আপনার দেওয়া ip মানগুলো ব্যবহার করে। কোনোটি না দিলে, এটি আপনার MX host এবং root SPF record-এর একক-ঠিকানার ip4: ও ip6: term থেকে সর্বোচ্চ চারটি নেয়। পরিচিত mail provider-এর ঠিকানাগুলো managed হিসেবে তালিকাভুক্ত হয় এবং পরীক্ষা না করেই রাখা হয়, আর কিছু অবশিষ্ট না থাকলে ptr ও blocklist_ip skipped উত্তর দেয়। DNS থেকে selector পড়া যায় না, তাই একটি run আপনার দেওয়া selector, আপনার আগের run-গুলোতে পাওয়া selector এবং প্রচলিত নামের একটি তালিকা চেষ্টা করে। কিছু না পাওয়া কোনো কিছু প্রমাণ করে না, এবং dkim unknown উত্তর দেয়। আপনি নিজের server থেকে পাঠালে, দুটিই দিন।

কী পরিবর্তিত হয়েছে

diff.changes-এর প্রতিটি এন্ট্রিতে একটি path, একটি kind (added, removed, changed), একটি severity (critical, warning, info) এবং একটি effect (worsened, improved, neutral) থাকে। শুধু critical এবং warning diff.changed সেট করে, তাই একই দৈর্ঘ্যের একটি পরিবর্তিত DKIM কী, একটি নতুন MX ঠিকানা অথবা একটি নবায়ন করা সার্টিফিকেট cron লাইনকে ব্যর্থ করে না। যেকোনো পরিবর্তনের effect অনুযায়ী diff.worsened নির্ধারিত হয়। প্রথম রানে তুলনা করার মতো কিছু থাকে না, এবং diff.first_run তা জানায়।

একটি baseline সবসময় আপনার নিজের একটি run, তাই আপনার দুটি run-এর মাঝখানে অন্য কেউ domain পরীক্ষা করলেও কিছু আড়াল হয় না। এবার আমরা যা পড়তে পারিনি, তার সর্বশেষ জানা মান রেখে সেটি diff.not_compared-এ যায়, যার অর্থ outage-এর সময় করা কোনো পরিবর্তন lookup আবার কাজ করলে দেখা যায়। ভিন্ন ip বা selector-সহ একটি run একই series-এ থাকে, এবং সেটি যা সম্পর্কে জানতে চায়নি সেটিও তুলনা করা হয় না।

নির্ধারিত পরীক্ষায় কেন key ব্যবহার করা উচিত

key ছাড়া আপনার history আপনার IP address-এর keyed hash-এর অধীনে থাকে, তাই address পরিবর্তিত হলে সেটি শেষ হয়ে যায়, এবং একই address-এর পেছনের লোকেরা এটি ভাগ করে: কোনো সহকর্মীর run আপনার baseline সরিয়ে দেয়। Authorization: Bearer-এ personal API key দিলে history key-টিকে অনুসরণ করে। key-সহ run 365 দিন রাখা হয় এবং শুধু সেই key দিয়েই খোলা যায়। key ছাড়া run 30 দিন রাখা হয় এবং যার কাছে সেগুলোর id আছে সে-ই খুলতে পারে।

প্রতি IP-তে মিনিটে 6টি এবং দিনে 50টি request, অথবা প্রতি key-তে 30টি এবং 500টি। এর বেশি হলে Retry-After-সহ উত্তর 429 হয়। 60 সেকেন্ডের মধ্যে একই request cache থেকে আসে, cached: true হিসেবে চিহ্নিত হয়, কোনো run লেখে না এবং গণনায় ধরা হয় না। সংরক্ষিত run পড়ার ক্ষেত্রে প্রতি client-কে মিনিটে 120টি request করার অনুমতি দেওয়া হয়। Retry-After-সহ 503-এর অর্থ অনেক বেশি পরীক্ষা চলছে, অথবা সংরক্ষিত run পড়ার সময় storage unavailable। 401-এর অর্থ ভুল key। 422-এ detail.code থাকে: invalid_domain, invalid_ip, invalid_selector, invalid_check, invalid_since, unknown_baseline অথবা account_required।

এন্ডপয়েন্ট

GET/api/v1/infra/check

পরীক্ষাটি চালায়। প্যারামিটারগুলো উপরের টেবিলে আছে।

GET/api/v1/infra/runs/{public_id}

একটি সংরক্ষিত রান, সম্পূর্ণ। এটি বিদ্যমান না থাকলে বা অন্য কোনো key-এর অন্তর্ভুক্ত হলে 404।

GET/api/v1/infra/domains/{domain}/runs

কোনো ডোমেইনের জন্য আপনার রানগুলো, সবচেয়ে নতুনটি প্রথমে: id, সময়, status, score এবং পরিবর্তনের সংখ্যা। limit (1 থেকে 100, ডিফল্টভাবে 30) এবং before, একটি run id, গ্রহণ করে।

GET/api/v1/infra/diff

দুটি সংরক্ষিত রানের মধ্যে একই তুলনা, ?from=<id>&to=<id>। দুটিতেই আপনার অ্যাক্সেস থাকতে হবে।

GET/api/v1/infra/checks

প্রতিটি সেকশন তার weight, score version এবং limits-সহ।

একক পরীক্ষা

প্রতিটি কল একটি প্রশ্নের উত্তর দেয় এবং কোনো ইতিহাস রাখে না। হেডার টুলটি ছাড়া, প্রতিটি টুল অবকাঠামো পরীক্ষার একটি অংশ নিজে চালায়; হেডার টুলটি আপনার ইতিমধ্যে পাওয়া একটি বার্তা পড়ে।

দ্রুত শুরু

  1. একটি পরীক্ষা বেছে নিন

    নিচের তালিকা থেকে টুলটি বেছে নিন এবং সেটি যে ডোমেইন, ঠিকানা বা IP চায় তা দিন।

    অনুরোধ

    curl -s 'https://email-spam-tester.com/api/v1/tools/spf?domain=example.com'
  2. স্ট্যাটাস ও ফলাফল পড়ুন

    প্রতিটি টুল একটি স্ট্যাটাস ও তার ফলাফল দেয়। এর পরের ফিল্ডগুলো টুলের ওপর নির্ভর করে।

    প্রতিক্রিয়া

    {
      "tool": "spf",
      "query": {"domain": "example.com"},
      "checked_at": "2026-10-31T12:00:00Z",
      "duration_ms": 412,
      "status": "fail",
      "findings": [
        {"code": "spf.too_many_lookups", "level": "fail",
         "params": {"limit": 10, "count": 12},
         "message": "RFC 7208 §4.6.4 allows at most 10 DNS lookups, and this record needs 12, so receivers return permerror."}
      ],
      "record": "v=spf1 include:_spf.example.net ~all",
      "lookups": {"count": 12, "limit": 10, "void": 0}
    }

এন্ডপয়েন্ট

এগুলোর সবই একটি status এবং findings-এর তালিকাসহ 200 উত্তর দেয়, প্রতিটি finding-এ একটি code এবং তার parameters থাকে। status হলো pass, warn, fail বা error, এবং অবকাঠামো পরীক্ষার সঙ্গে শেয়ার করা কোনো tool unknown বা skipped উত্তরও দিতে পারে। সময়সীমা পেরিয়ে যাওয়া lookup বা query প্রত্যাখ্যান করা list কখনো pass হিসেবে গণ্য হয় না। সব tool মিলিয়ে প্রতি IP-তে মিনিটে 30টি এবং দিনে 300টি request; এর বেশি হলে Retry-After সহ 429 উত্তর দেওয়া হয়।

GET/api/v1/tools/spf

কোনো ডোমেইনের SPF রেকর্ড: এর সিনট্যাক্স, অনুসরণ করা প্রতিটি include ও redirect, এবং দশটির সীমার বিপরীতে পুরো ট্রির DNS লুকআপের খরচ। ?domain=example.com পাঠান।

GET/api/v1/tools/dmarc

কোনো ডোমেইনের DMARC রেকর্ড, অথবা সেটি না থাকলে এর সাংগঠনিক ডোমেইনের রেকর্ড: ট্যাগগুলো, নীতি এবং প্রতিটি বহিরাগত রিপোর্ট ঠিকানা রিপোর্ট গ্রহণে সম্মত হয়েছে কি না। ?domain=example.com পাঠান।

GET/api/v1/tools/dkim

কোনো selector-এর জন্য প্রকাশিত DKIM কী: এটি বিদ্যমান ও ডিকোডযোগ্য কি না, এর ধরন ও আকার, এবং revoked ও testing ফ্ল্যাগ। ?domain=example.com&selector=s1 পাঠান।

GET/api/v1/tools/blocklist

একটি IPv4 ঠিকানা, একটি IPv6 ঠিকানা অথবা একটি ডোমেইনকে রিপোর্টে ব্যবহৃত DNS ব্লকলিস্টগুলোর বিপরীতে পরীক্ষা করা হয়, প্রতিটি তালিকার উত্তরসহ। ?target=203.0.113.5 পাঠান।

GET/api/v1/tools/mx

কোনো ডোমেইনের MX রেকর্ডগুলো প্রেরকেরা যে ক্রমে চেষ্টা করে সেই ক্রমে, প্রতিটি হোস্টের IPv4 ও IPv6 ঠিকানাসহ। Null MX, MX-বিহীন ডোমেইন, IP ঠিকানা বা CNAME এমন হোস্ট এবং যেসব হোস্ট resolve হয় না সেগুলো রিপোর্ট করা হয়, এবং MTA-STS ও TLS-RPT রেকর্ডও রিপোর্ট করা হয়। ?domain=example.com পাঠান।

GET/api/v1/tools/bimi

চারটি ধাপে কোনো ডোমেইনের BIMI সেটআপ: BIMI-এর প্রয়োজনীয় DMARC নীতি, default._bimi-এ থাকা রেকর্ড, Tiny PS প্রোফাইল অনুযায়ী যাচাই করা SVG লোগো এবং তার ধরন, ডোমেইন ও মেয়াদ শেষ হওয়ার সময়সহ mark certificate। ?domain=example.com পাঠান, এবং default ছাড়া অন্য selector-এর জন্য &selector= পাঠান।

GET/api/v1/tools/ptr

কোনো ঠিকানার PTR রেকর্ড এবং ওই নামগুলোর কোনোটি আবার সেই ঠিকানায় resolve করে কি না। ব্যর্থ lookup-এর স্ট্যাটাস error হয়; রেকর্ড না থাকলে স্ট্যাটাস fail হয়। ?ip=8.8.8.8 পাঠান।

GET/api/v1/tools/mta-sts

কোনো ডোমেইনের MTA-STS রেকর্ড এবং সেটি যে policy file-কে নির্দেশ করে: mode, তালিকাভুক্ত host এবং policy-তে বাদ পড়া যেকোনো MX host। ?domain=example.com দিন।

GET/api/v1/tools/tls-rpt

কোনো ডোমেইনের TLS-RPT রেকর্ড এবং যেসব ঠিকানায় এর report পাঠানো হয়। ?domain=example.com দিন।

GET/api/v1/tools/dnssec

কোনো ডোমেইনের zone signed, unsigned নাকি broken, তা এর DNSKEY এবং DS রেকর্ড থেকে নির্ধারণ করা হয়। খারাপ signature-কে resolver outage থেকে আলাদা করে দেখানো হয়। ?domain=example.com দিন।

GET/api/v1/tools/dane

MX host-গুলোর TLSA রেকর্ড, host উপস্থাপিত certificate পড়া গেলে সেটির সঙ্গে তুলনা করে। ?domain=example.com দিন।

GET/api/v1/tools/smtp

প্রথম দুটি MX host-এ STARTTLS এবং certificate: TLS version, issuer, expiry date এবং chain ও name বৈধ কি না। আমরা শুধু আপনার দেওয়া ডোমেইনের অধীন host-এ সংযোগ করি এবং mail provider বাদ দিই, আর প্রতিটি ঠিকানার জন্য একটি ফলাফল 6 ঘণ্টা শেয়ার করা হয়। কোনো mail পাঠানো হয় না। ?domain=example.com দিন।

GET/api/v1/tools/ns

কোনো ডোমেইনের name server এবং এর zone-এ SOA রেকর্ড আছে কি না। একটি name server থাকা একটি warning। ?domain=example.com দিন।

GET/api/v1/tools/registration

RDAP অনুযায়ী কোনো ডোমেইনের registration কখন শেষ হবে। 30 দিনের কম সময় বাকি থাকলে তা একটি warning। RDAP service নেই এমন zone-এর উত্তর skipped হয়। ?domain=example.com দিন।

POST/api/v1/tools/header

প্রাপ্ত কোনো বার্তার হেডার, JSON {"headers": "..."} হিসেবে বা text/plain হিসেবে পাঠানো: বিলম্ব, TLS ও ঘড়ির সময়ের পার্থক্যসহ ধাপে ধাপে রুট, প্রাপকের নিজস্ব Authentication-Results, From, DKIM d= ও Return-Path ডোমেইনগুলোর alignment এবং DKIM, ARC, unsubscribe ও spam filter হেডার। প্রথম ফাঁকা লাইনের পরের সবকিছু না পড়েই বাদ দেওয়া হয়। হেডারগুলো মেমরিতে প্রক্রিয়া করা হয় এবং সংরক্ষণ, cache বা log করা হয় না।

প্রতি রাতে ডোমেইনটি যাচাই করুন।
ইমেইলটি পাঠানোর আগে পরীক্ষা করুন।

প্রতিটি প্রোডাক্টের জন্য অভিন্ন

অ্যাক্সেস, কী, রিপোর্টের ফরম্যাট, স্ট্যাটাস, ত্রুটি, সঙ্গে মেশিনের জন্য OpenAPI এবং llms.txt।

প্রমাণীকরণ

কোনোটিই নেই। অনুরোধ করার জন্য কোনো কী নেই, সেট করার জন্য কোনো হেডার নেই, তৈরি করার জন্য কোনো অ্যাকাউন্ট নেই। আপনি যে স্লাগটি ফেরত পান সেটিই সক্ষমতা: যার কাছে এটি থাকবে সে ওই রিপোর্ট পড়তে পারবে, এবং অন্য কেউ এটি অনুমান করতে পারবে না।

এর অর্থ এটিও যে একটি স্লাগকে পাসওয়ার্ডের মতো বিবেচনা করা উচিত। লিংকটি যার কাছে থাকবে, রিপোর্টটি তাকে বিষয়ের লাইন, প্রেরক, বাউন্স ঠিকানা এবং বার্তার সম্পূর্ণ সোর্স দেখায়।

ব্যক্তিগত API কী

Everything above works without a key. A key is for automation platforms such as Zapier, where every user's calls come from the platform's servers and per-IP limits would be shared by all of them. With a key, limits count per key (50 tests a day, 3 with placement, 5 permanent mailboxes), the key's tests form a history, and the key can keep permanent mailboxes whose reports only it can read. Send the key as Authorization: Bearer est_live_… on any call on this page. The routes below also take account, a short name of your choosing ([A-Za-z0-9_-], up to 64 characters) that keeps separate lists under one key. GET /api/v1/installations/tests requires it; pass the same one on POST /api/v1/inbox so the test shows up there. The Zapier integration uses zapier.

POST /api/v1/keys

Send JSON {"email":"you@example.com"}. Returns HTTP 201 with key and email. The key is shown once; only its hash is kept. Asking again gives a new key and leaves the earlier ones working. Limit: 5 keys per IP per UTC day. The form on /zapier/ calls this.

GET /api/v1/keys/me

Returns email, key_prefix and created_at of the key in the header, or HTTP 401. Use it to check a key.

GET /api/v1/installations/tests

The key's tests, newest first: ?account=…&limit=50 (up to 100), and before=<slug> for the next page. Each item has slug, created_at, analysis_status (waiting or expired before an email arrived), score_ours, score_compat, placement and changes, the checks that moved since the previous test of the same sending domain. Read the full report at /api/v1/tests/{slug}.

POST /api/v1/installations/mailboxes

Send JSON {"domain":"example.com","account":"…"}. Returns the permanent mailbox of that sending domain: id, address, domain, state, daily_cap and created. HTTP 201 when it is new, 200 when it already existed, 409 when the key keeps as many mailboxes as it may. Subscribe the address to a newsletter or a sequence and every email that arrives is checked. GET on the same path with ?account=… lists them, and DELETE /api/v1/installations/mailboxes/{mailbox_id} stops one.

GET /api/v1/installations/mailboxes/{mailbox_id}/letters

The emails that arrived, oldest first, paged with after_id and next_after_id. settled=true ends the page before the first email still being checked, so paging never skips one. newest=true turns the order round, for a quick look at the latest; settled does nothing with it. Each has test_id, slug, received_at, analysis_status, score_ours, score_compat, subject, from_addr and kind.

GET /api/v1/installations/letters/{slug}

The report of one email in a permanent mailbox, the same JSON as /api/v1/tests/{slug} with report_url null: there is no public page for it. HTTP 410 once the report has expired.

রিপোর্ট পড়া

যে field-গুলোতে উত্তর থাকে, সম্ভবত আপনি যে ক্রমে সেগুলো চান সেই ক্রমে।

score_ours0 থেকে 100। আমাদের model। Authentication এবং infrastructure-এর ওজন সবচেয়ে বেশি, কারণ কোনো filter কপির একটি শব্দ পড়ার আগেই এগুলো delivery নির্ধারণ করে।
score_compat0 থেকে 10। লোকজন ইতিমধ্যে তুলনার জন্য ব্যবহার করে এমন SpamAssassin-ধাঁচের সংখ্যাটি পুনরুৎপাদন করে, যাতে রিপোর্টটি অন্য কোথাও কেউ যা দেখেছে তার সঙ্গে তুলনীয় হয়।
subscoresপ্রতিটি section-এর জন্য একই 0 থেকে 100 scale: auth, infra_spam, content, compliance।
checks[]সব ৪২টি, প্রতিটিতে একটি status, এক লাইনের finding, যে evidence-এর ভিত্তিতে সিদ্ধান্ত নেওয়া হয়েছে এবং দুই score-এ এর cost কত।
checks[].citationsফাইন্ডিংটি কোথা থেকে এসেছে: RFC-এর সংশ্লিষ্ট অনুচ্ছেদ, হুবহু উদ্ধৃত, এবং যে পৃষ্ঠায় Google তার নিজস্ব প্রয়োজনীয়তা উল্লেখ করেছে। তৈরি না করে হাতে বাছাই করা হয়েছে, তাই এগুলোর ওপর নির্ভর করা যায়।
সমাধানগুলি[]কী পরিবর্তন করতে হবে, ক্রমানুসারে, এবং প্রতিটি পরিবর্তনের জন্য কত পয়েন্ট পাওয়া যাবে। লাভ অনুমান করা হয় না, প্রতিবেদনটি পুনরায় স্কোর করে হিসাব করা হয়।
সম্পূর্ণকোনো পরীক্ষা চালানো না গেলে মিথ্যা। পরীক্ষা না করা কোনো আইটেম কখনোই উত্তীর্ণ নয়, তাই এটি সত্য না হওয়া পর্যন্ত স্কোরটিকে আশাবাদী ধরে নিন।
প্রতিবেদনের_urlমানুষের পড়ার উপযোগী পৃষ্ঠা। JSON-এর দেয়াল দেওয়ার পরিবর্তে এটি একজন ব্যক্তিকে দিন।

স্ট্যাটাস

একটি পরীক্ষা পাঁচটি অবস্থার একটিতে থাকে, এবং সেগুলোর দুটির মধ্যে পার্থক্য যতটা মনে হয় তার চেয়ে বেশি গুরুত্বপূর্ণ।

উত্তীর্ণপরীক্ষাটি চালানো হয়েছে এবং কোনো সমস্যা পাওয়া যায়নি।
সতর্কতাসমাধান করা উচিত। পয়েন্ট কমায়।
ব্যর্থআপনার ডেলিভারি ক্ষতিগ্রস্ত হবে। বেশি পয়েন্ট কাটবে।
এড়িয়ে যাওয়াপ্রযোজ্য হয়নি। স্ক্যান করার মতো কোনো অ্যাটাচমেন্ট নেই, ওজন করার মতো কোনো HTML অংশ নেই। এটি পাস নয়।
ত্রুটিআমরা নির্ধারণ করতে পারিনি। পাস হিসেবে গণনা না করে, স্কোর থেকে বাদ দেওয়া হয়েছে এবং ফ্ল্যাগ করা হয়েছে।

analysis_status-এর মান received, analyzing, checks_ready, failed। ai_status-এর মান pending, running, ready, fallback, error। নির্ধারক প্রতিবেদনটি checks_ready-এ চূড়ান্ত হয়; ai_status শুধু পরিকল্পনাটি যোগ করে।

কী ফিরে আসতে পারে

202ঠিকানাটি সংরক্ষিত এবং কোনো message আসেনি। body-তে ঠিকানা, মেয়াদ শেষ হওয়ার সময় এবং ভাষা থাকে, তাই যে caller reservation তৈরি করেনি, সেও এটি দেখাতে পারে।
404এমন কোনো slug নেই। কখনোই ছিল না, অথবা reservation-টি ভুলে যাওয়া হয়েছে।
410কোনো message আসার আগেই ঠিকানাটির মেয়াদ শেষ হয়েছে।
409message ইতিমধ্যে আসার পরে আপনি ভাষা পরিবর্তন করার চেষ্টা করেছেন।
429আপনার ঠিকানার জন্য বিনামূল্যের test-এর সীমা। এই service-এ বর্তমানে ব্যক্তি-পিছু কোনো সীমা নেই; ভবিষ্যতে কখনো এমন সীমা নির্ধারণ করা হলে, body-তে কখন সেটি reset হবে তা বলা থাকবে।

জানার মতো চারটি বিষয়

প্রতি ঠিকানায় একটি বার্তা

দ্বিতীয়টি প্রত্যাখ্যান করা হয়। আপনার আগের প্রতিবেদন অক্ষত থাকে, সেটিই উদ্দেশ্য।

ব্যবহারের জন্য এক ঘণ্টা

ঠিকানাটির মেয়াদ শেষ হয়; প্রতিবেদনের হয় না। প্রতিবেদনের লিংক স্থায়ী, তাই টিকিটে পেস্ট করা লিংক কাজ করতে থাকে।

প্ল্যাটফর্ম থেকে পাঠান

ব্যক্তিগত অ্যাকাউন্ট থেকে কোনো ক্যাম্পেইন পরীক্ষা করলে সেই অ্যাকাউন্টেরই পরিমাপ হয়। প্রমাণীকরণ বিভাগের প্রায় সবকিছুই পাঠানোর অবকাঠামো সম্পর্কে, টেক্সট সম্পর্কে নয়।

সমাধানের পর আবার পরীক্ষা করুন

প্রত্যাশিত উন্নতি প্রতিবার একটি সমাধান ধরে গণনা করা হয় এবং সেগুলো একত্রে প্রযোজ্য নয়। দ্বিতীয় প্রতিবেদনটিই প্রকৃত সংখ্যা।

মেশিনের জন্য

GET/api/health

Liveness। কোনো authentication নেই, কোনো side effect নেই।

প্রথমে একটি হাতে করে দেখুন

এতে প্রায় এক মিনিট সময় লাগে এবং JSON ঠিক কী বর্ণনা করে তা আপনাকে দেখায়।

একটি পরীক্ষার ঠিকানা নিন

স্যান্ডবক্স।

প্রতিটি এন্ডপয়েন্টের সঙ্গে একটি বোতাম রয়েছে, যা সেটিকে লাইভ সার্ভিসে চালায়। এখানে কোনো কিছুর জন্যই কী প্রয়োজন নেই, তাই এই পৃষ্ঠা থেকে পাঠানো আপনার অনুরোধটি বাস্তব: এটি একটি বাস্তব ঠিকানা সংরক্ষণ করে এবং এটি গণনায় ধরা হয়।

স্যান্ডবক্সে চেষ্টা করুন