MTA-STS and TLS-RPT check API
Looks up _mta-sts, fetches the policy from https://mta-sts.<domain>/.well-known/mta-sts.txt with certificate verification and no redirects, parses mode, mx and max_age, compares the domain's MX hosts with the policy and validates the TLS-RPT record.
GET https://api.probekit.dev/v1/mail/mta-sts
Updated 2026-10-09
Examples use the Base (eip155:8453) network.
Input
Query parameters.
| Name | Type | Required | Limits | Description |
|---|---|---|---|---|
domain | string | Yes | 1–1024 characters | The domain to check, for example example.com. |
Example request
curl -i 'https://api.probekit.dev/v1/mail/mta-sts?domain=example.com'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.get('https://api.probekit.dev/v1/mail/mta-sts', params={'domain': 'example.com'})
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/mta-sts?domain=example.com");
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",
"data": {
"domain": "example.com",
"mx_hosts": [
"mx1.example.com",
"mx2.example.com"
],
"policy": {
"max_age": 604800,
"mode": "enforce",
"mx": [
"*.example.com"
],
"version": "STSv1"
},
"policy_fetched": true,
"policy_id": "20261024T120000",
"record": "v=STSv1; id=20261024T120000",
"tlsrpt_record": "v=TLSRPTv1; rua=mailto:[email protected]",
"tlsrpt_rua": [
"mailto:[email protected]"
],
"uncovered_mx": []
},
"endpoint": "mail.mta_sts",
"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"
}
Response fields
| Field | Meaning |
|---|---|
request_id | Unique id of this request. Quote it when you contact support. |
endpoint | The endpoint id, for example mail.spf. |
schema_version | Version of the response format, currently 1.0. |
checked_at | When the check ran (ISO 8601, UTC). |
status | ok, info, warning or error from the worst finding, or unknown when the check was incomplete. unknown is not an error in the domain. |
data | The check's own data. Its fields are listed below. |
findings | What was found: code, severity, message, explanation and evidence. |
meta | Duration, cache use, resolver and how long the result stays fresh. |
Fields in data
| Field | Type |
|---|---|
domain | string |
record | string or null |
policy_id | string or null |
policy_fetched | boolean |
policy | MtaStsPolicy or null |
mx_hosts | array of string |
uncovered_mx | array of string |
tlsrpt_record | string or null |
tlsrpt_rua | array of string |
Status codes
| Status | Code | Meaning | Do 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 |
| 402 | — | Payment is required, or the attached payment was refused. The body states the price. | No |
| 400 | bad_request | The request is malformed. | No |
| 409 | payment_in_progress, payment_bound_to_other_request | This payment is already being processed, or it is bound to a different request. No new payment is taken. | No |
| 422 | invalid_input | The 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 |
| 429 | rate_limited | Too many requests from this IP address or payer. | No |
| 502 | check_failed | The check could not be completed. | No |
| 503 | busy, unavailable, payments_unavailable | The service is busy or temporarily unavailable. | No |
| 504 | deadline_exceeded | The check ran out of time. | No |
Errors use the common error body. See errors.
Findings this endpoint can return
Check (1)
- Warning
CHECK_QUERY_BUDGET_EXCEEDEDThe check stopped after reaching its DNS query limit.
DNS (2)
- Warning
DNS_RESOLVER_FAILUREThe DNS resolver returned a server failure. - Warning
DNS_RESOLVER_TIMEOUTThe DNS resolver did not answer in time.
MTA-STS (10)
- Error
MTASTS_INVALID_POLICYThe MTA-STS policy file is not valid. - Error
MTASTS_INVALID_RECORDThe MTA-STS record has invalid syntax. - Warning
MTASTS_LOW_MAX_AGEThe MTA-STS policy has a max_age below one day. - Info
MTASTS_MODE_NONEThe MTA-STS policy is in mode none. - Warning
MTASTS_MODE_TESTINGThe MTA-STS policy is in testing mode. - Error
MTASTS_MULTIPLE_RECORDSMore than one MTA-STS record is published. - Error
MTASTS_MX_NOT_COVEREDAn MX host is not covered by the enforced MTA-STS policy. - Warning
MTASTS_MX_NOT_COVERED_TESTINGAn MX host is not covered by the MTA-STS policy in testing mode. - Info
MTASTS_NOT_FOUNDThe domain publishes no MTA-STS record. - Error
MTASTS_POLICY_UNAVAILABLEThe MTA-STS policy file could not be fetched.
TLS-RPT (2)
- Warning
TLSRPT_INVALIDThe TLS-RPT record is not valid. - Info
TLSRPT_NOT_FOUNDThe domain publishes no TLS-RPT record.