Bulk mail configuration check API

Same engine as the single mail check. Results keep input order; duplicates count as items. Items that fail for technical reasons can be retried free for 15 minutes.

POST https://api.probekit.dev/v1/mail/check/bulk

Updated 2026-10-09

Examples use the Base (eip155:8453) network.

Bulk price

$0.024 per item, minimum $0.03 per request, at most 20 items. The price is for the whole batch and is known before you pay.

DomainsPrice
1$0.03
5$0.12
10$0.24
20$0.48

Input

A JSON body with content-type: application/json.

NameTypeRequiredLimitsDescription
domainsarray of stringYes1–20 items; each 1–1024 charactersThe domains to check. Duplicates count as separate items.

Example request

curl -i -X POST https://api.probekit.dev/v1/mail/check/bulk \
  -H 'content-type: application/json' \
  -d '{"domains": ["example.com", "example.org"]}'
import asyncio
import os

from eth_account import Account
from x402 import x402Client
from x402.http.clients import x402HttpxClient
from x402.mechanisms.evm import EthAccountSigner
from x402.mechanisms.evm.exact import register_exact_evm_client


async def main() -> None:
    signer = EthAccountSigner(Account.from_key(os.environ["EVM_PRIVATE_KEY"]))
    client = register_exact_evm_client(x402Client(), signer, networks="eip155:8453")
    async with x402HttpxClient(client) as http:
        response = await http.post('https://api.probekit.dev/v1/mail/check/bulk', json={'domains': ['example.com', 'example.org']})
        response.raise_for_status()
        print(response.json())


asyncio.run(main())
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";

const account = privateKeyToAccount(process.env.EVM_PRIVATE_KEY);
const fetchWithPayment = wrapFetchWithPaymentFromConfig(fetch, {
  schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }],
});

const response = await fetchWithPayment("https://api.probekit.dev/v1/mail/check/bulk", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({"domains": ["example.com", "example.org"]}),
});
console.log(await response.json());

curl: unpaid request — returns 402 with the price (PAYMENT-REQUIRED). The Python and JavaScript clients pay it and send the request again. Only ordinary wallets (EOA) can pay; smart-contract wallets get a 402 without charge. See payments.

Example response

{
  "checked_at": "2026-10-24T12:00:00Z",
  "endpoint": "mail.check.bulk",
  "failed_count": 0,
  "item_count": 2,
  "request_id": "req_aaaaaaaaaaaaaaaaaaaaaaaaaa",
  "results": [
    {
      "error": null,
      "index": 0,
      "item": "example.com",
      "result": {
        "checked_at": "2026-10-24T12:00:00Z",
        "data": {
          "dkim": [],
          "dmarc": {
            "adkim": "r",
            "applied_policy": "reject",
            "aspf": "r",
            "domain": "example.com",
            "org_domain": "example.com",
            "pct": 100,
            "policy": "reject",
            "record": "v=DMARC1; p=reject; rua=mailto:[email protected]",
            "record_domain": "example.com",
            "rua": [
              "mailto:[email protected]"
            ],
            "ruf": [],
            "subdomain_policy": "reject",
            "tags": [
              {
                "name": "v",
                "value": "DMARC1"
              },
              {
                "name": "p",
                "value": "reject"
              },
              {
                "name": "rua",
                "value": "mailto:[email protected]"
              }
            ]
          },
          "domain": "example.com",
          "mx": {
            "domain": "example.com",
            "hosts": [
              {
                "addresses": [
                  "192.0.2.25"
                ],
                "exchange": "mx1.example.com",
                "preference": 10,
                "resolved": true
              },
              {
                "addresses": [
                  "192.0.2.26"
                ],
                "exchange": "mx2.example.com",
                "preference": 20,
                "resolved": true
              }
            ],
            "implicit_mx": false,
            "null_mx": false
          },
          "spf": {
            "all_qualifier": "-",
            "dns_lookup_count": 1,
            "domain": "example.com",
            "lookup_count_complete": true,
            "record": "v=spf1 ip4:192.0.2.0/24 include:_spf.example.net -all",
            "tree": {
              "children": [
                {
                  "children": [],
                  "domain": "_spf.example.net",
                  "error": null,
                  "record": "v=spf1 ip4:198.51.100.0/24 -all",
                  "terms": [
                    {
                      "kind": "mechanism",
                      "name": "ip4",
                      "qualifier": null,
                      "raw": "ip4:198.51.100.0/24",
                      "value": "198.51.100.0/24"
                    },
                    {
                      "kind": "mechanism",
                      "name": "all",
                      "qualifier": "-",
                      "raw": "-all",
                      "value": null
                    }
                  ],
                  "via": "include"
                }
              ],
              "domain": "example.com",
              "error": null,
              "record": "v=spf1 ip4:192.0.2.0/24 include:_spf.example.net -all",
              "terms": [
                {
                  "kind": "mechanism",
                  "name": "ip4",
                  "qualifier": null,
                  "raw": "ip4:192.0.2.0/24",
                  "value": "192.0.2.0/24"
                },
                {
                  "kind": "mechanism",
                  "name": "include",
                  "qualifier": null,
                  "raw": "include:_spf.example.net",
                  "value": "_spf.example.net"
                },
                {
                  "kind": "mechanism",
                  "name": "all",
                  "qualifier": "-",
                  "raw": "-all",
                  "value": null
                }
              ],
              "via": "root"
            },
            "void_lookup_count": 0
          }
        },
        "endpoint": "mail.check",
        "findings": [],
        "meta": {
          "cache_hit": false,
          "duration_ms": 42,
          "fresh_until": "2026-10-24T12:05:00Z",
          "observed_at": "2026-10-24T12:00:00Z",
          "resolver": "example",
          "source": "dns"
        },
        "request_id": "req_aaaaaaaaaaaaaaaaaaaaaaaaaa",
        "schema_version": "1.0",
        "status": "ok"
      }
    },
    {
      "error": null,
      "index": 1,
      "item": "example.org",
      "result": {
        "checked_at": "2026-10-24T12:00:00Z",
        "data": {
          "dkim": [],
          "dmarc": {
            "adkim": null,
            "applied_policy": null,
            "aspf": null,
            "domain": "example.org",
            "org_domain": "example.org",
            "pct": null,
            "policy": null,
            "record": null,
            "record_domain": null,
            "rua": [],
            "ruf": [],
            "subdomain_policy": null,
            "tags": []
          },
          "domain": "example.org",
          "mx": {
            "domain": "example.org",
            "hosts": [],
            "implicit_mx": false,
            "null_mx": false
          },
          "spf": {
            "all_qualifier": null,
            "dns_lookup_count": 0,
            "domain": "example.org",
            "lookup_count_complete": true,
            "record": null,
            "tree": null,
            "void_lookup_count": 0
          }
        },
        "endpoint": "mail.check",
        "findings": [
          {
            "code": "DMARC_NOT_FOUND",
            "evidence": {
              "count": 1,
              "items": [
                {
                  "domain": "example.org",
                  "org_domain": "example.org"
                }
              ]
            },
            "explanation": "Without DMARC, receivers apply their own handling to mail that fails SPF and DKIM alignment, and the domain owner receives no reports.",
            "message": "No DMARC record was found for the domain or its organisational domain.",
            "recommendation": "Publish a DMARC record, for example with p=none and a rua address, and use the reports to plan further steps.",
            "severity": "warning"
          },
          {
            "code": "DNS_DOMAIN_NOT_FOUND",
            "evidence": {
              "count": 2,
              "items": [
                {
                  "name": "example.org"
                },
                {
                  "name": "example.org"
                }
              ]
            },
            "explanation": "The resolver answered NXDOMAIN: it found no records for this name. Another resolver or a recent registration or delegation change could give a different answer.",
            "message": "The DNS resolver reported that the domain does not exist.",
            "recommendation": "Check the spelling and that the domain is registered and delegated.",
            "severity": "error"
          }
        ],
        "meta": {
          "cache_hit": false,
          "duration_ms": 42,
          "fresh_until": "2026-10-24T12:05:00Z",
          "observed_at": "2026-10-24T12:00:00Z",
          "resolver": "example",
          "source": "dns"
        },
        "request_id": "req_aaaaaaaaaaaaaaaaaaaaaaaaaa",
        "schema_version": "1.0",
        "status": "error"
      }
    }
  ],
  "retry_token": null,
  "schema_version": "1.0",
  "succeeded_count": 2
}

Response fields

FieldMeaning
request_idUnique id of this request. Quote it when you contact support.
endpointThe endpoint id, mail.check.bulk.
schema_versionVersion of the response format, currently 1.0.
checked_atWhen the batch ran (ISO 8601, UTC).
resultsOne entry per input item, in input order: index, item, and either result (a full check response) or error.
item_countNumber of items in the request.
succeeded_countItems with a result.
failed_countItems that failed for a technical reason.
retry_tokenPresent when items failed for a technical reason: send it to /v1/mail/check/bulk/retry within 15 minutes to run those items again free of charge (3 attempts in total). Otherwise null.

Fields in results[].result.data

FieldType
domainstring
mxMxData or null
spfSpfData or null
dmarcDmarcData or null
dkimarray of DkimData

Items that failed for a technical reason can be run again free of charge with the free retry.

Status codes

StatusCodeMeaningDo you pay?
200—The result is delivered, also when it has negative findings.Yes: charged once per payment; redelivery of the same payment is free
200—Some items failed for a technical reason. They are reported per item and the whole batch is charged. If every item fails, the answer is 502.Yes
402—Payment is required, or the attached payment was refused. The body states the price.No
400bad_requestThe request is malformed.No
409payment_in_progress, payment_bound_to_other_requestThis payment is already being processed, or it is bound to a different request. No new payment is taken.No
413payload_too_largeThe body is larger than 64 KB.No
422invalid_inputThe input failed validation on a request that carries a payment; nothing is charged. Without a payment, invalid input gets a 402 whose error field names the problem.No
429rate_limitedToo many requests from this IP address or payer.No
502check_failedThe check could not be completed.No
503busy, unavailable, payments_unavailableThe service is busy or temporarily unavailable.No
504deadline_exceededThe check ran out of time.No

Errors use the common error body. See errors.

Findings this endpoint can return

Check (3)
DKIM (7)
DMARC (10)
DNS (3)
MX (5)
SPF (13)