---
name: probekit
description: Check a domain's email authentication (SPF, DMARC, DKIM, MX) and DNS records with ProbeKit's pay-per-request API, paid with x402 in USDC on Base. Use when the user asks whether a domain's mail setup is correct, why mail from a domain is rejected or lands in spam, or which DNS records a name has.
---

# ProbeKit

ProbeKit runs mail and DNS checks and returns structured JSON: an overall `status`, the check's `data`, and `findings` that explain what was found and why it matters.

## Choose a tool

| Task | Endpoint | Request | Price |
|---|---|---|---|
| Resolve selected record types with TTL and the resolver that answered. | `dns.lookup` | `GET https://api.probekit.dev/v1/dns/lookup` | $0.005 per request |
| One report covering MX, SPF, DMARC and DKIM for known selectors. | `mail.check` | `GET https://api.probekit.dev/v1/mail/check` | $0.03 per request |
| Run the mail configuration check on up to 20 domains in one request. | `mail.check.bulk` | `POST https://api.probekit.dev/v1/mail/check/bulk` | $0.024 per item, minimum $0.03 per request, at most 20 items |
| Check the DKIM public key record for a given selector. | `mail.dkim` | `GET https://api.probekit.dev/v1/mail/dkim` | $0.01 per request |
| Look up and explain a domain's DMARC policy and tags. | `mail.dmarc` | `GET https://api.probekit.dev/v1/mail/dmarc` | $0.01 per request |
| List a domain's mail servers with priorities, including null MX. | `mail.mx` | `GET https://api.probekit.dev/v1/mail/mx` | $0.01 per request |
| Parse and evaluate a domain's SPF policy, including the DNS lookup budget. | `mail.spf` | `GET https://api.probekit.dev/v1/mail/spf` | $0.01 per request |
| Run the items of a paid bulk request that failed for technical reasons again, free of charge. | `mail.check.bulk.retry` | `POST https://api.probekit.dev/v1/mail/check/bulk/retry` | Free |

- Use `mail.check` for a full overview of a domain's mail setup; use a single check when the user asks about one record.
- Use `mail.check.bulk` for 2 to 20 domains in one request.

## Payment (x402)

Network: Base (`eip155:8453`), asset USDC `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`.

1. Send the request without payment. The API answers `402 Payment Required`; the body and the `PAYMENT-REQUIRED` header state the exact price in `accepts[0].amount` (USDC atomic units, 6 decimals).
2. Check the amount before you sign. One request never costs more than $0.48.
3. Sign the EIP-3009 authorization with an x402 client and send the same request again with the `PAYMENT-SIGNATURE` header.
4. The result comes back with `200` and a `PAYMENT-RESPONSE` header (the settlement receipt).

If the answer is lost after payment, send the same request with the same signature again within 24 hours: the paid result is delivered again without a new charge.

## Input

- `dns.lookup`: `name` (string, required; 1–1024 characters), `types` (array of one of A, AAAA, CNAME, MX, NS, TXT, SOA, CAA, SRV, optional; 1–9 items; default ["A"])
- `mail.check`: `domain` (string, required; 1–1024 characters), `dkim_selectors` (array of string, optional; at most 5 items; each 1–200 characters; default [])
- `mail.check.bulk`: `domains` (array of string, required; 1–20 items; each 1–1024 characters)
- `mail.dkim`: `domain` (string, required; 1–1024 characters), `selector` (string, required; 1–200 characters)
- `mail.dmarc`: `domain` (string, required; 1–1024 characters)
- `mail.mx`: `domain` (string, required; 1–1024 characters)
- `mail.spf`: `domain` (string, required; 1–1024 characters)

Invalid input is answered with `422` before any price is given.

## Reading results

- `status` is the worst finding severity: `ok`, `info`, `warning` or `error`.
- `unknown` means the check was incomplete, for example after a DNS timeout. It is not an error in the domain: do not report it as a failure; try again later.
- Each finding has a `code`, `severity`, `message`, `explanation`, and often a `recommendation` and `evidence`. Every code is explained at https://probekit.dev/findings
- A result with negative findings is still a delivered result.

## When you are not charged

Errors never cost anything:

- `402`: no payment is taken.
- `400` (bad_request): The request is malformed.
- `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.
- `413` (payload_too_large): The body is larger than 64 KB.
- `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.
- `429` (rate_limited): Too many requests from this IP address or payer.
- `502` (check_failed): The check could not be completed.
- `503` (busy, unavailable, payments_unavailable): The service is busy or temporarily unavailable.
- `504` (deadline_exceeded): The check ran out of time.
- A bulk request where every item failed (answered `502`).

## Free retry of failed bulk items

1. A paid `mail.check.bulk` response where items failed for a technical reason (`error` on the item) holds a `retry_token`.
2. Within 15 minutes, send `POST https://api.probekit.dev/v1/mail/check/bulk/retry` with `{"retry_token": "<token>"}` and no `PAYMENT-SIGNATURE` header.
3. The answer is the whole bulk result with the retried items merged in. If items still fail and attempts are left, it holds a new `retry_token`; the old one stops working.

- At most 3 attempts in total. Only the failed items run, with the original domains; the input cannot be changed. Nothing is charged.
- Items with findings are results, not failures: there is nothing to retry.
- `400` (bad_request): The request is malformed.
- `404` (retry_token_invalid): The retry token is unknown, or a newer token from a later attempt has replaced it.
- `409` (retry_in_progress): An attempt with this retry token is already running.
- `410` (retry_token_expired): The retry token has expired (15 minutes after it was issued), all 3 attempts are used, or no failed items are left.
- `413` (payload_too_large): The body is larger than 64 KB.
- `422` (invalid_input): The body is invalid: it must hold only retry_token.
- `429` (rate_limited): Too many requests from this IP address.
- `503` (busy, unavailable): The service is busy or temporarily unavailable. The attempt does not count.

## Limits

- Only public DNS: names are resolved through public resolvers, so private or internal zones cannot be checked.
- No mailbox probing: ProbeKit never connects to mail servers to test addresses and never sends mail.
- DKIM needs a selector (for example `s=` in a DKIM-Signature header); keys cannot be listed without one.
- Bulk: at most 20 domains per request.
- Ordinary wallets (EOA) only: smart-contract wallets (ERC-1271/ERC-6492 signatures) are refused with `402` and nothing is charged.

## Troubleshooting

| Symptom | Cause | Fix |
|---|---|---|
| Status `unknown` | The check was incomplete, for example a DNS timeout | Not a failure of the domain; try again later |
| `422` | The input is invalid | Fix the input; see Input above |
| DKIM check asks for a selector | DKIM keys cannot be listed without one | Read `s=` from a DKIM-Signature header of a real message |
| Bulk is rejected | More than 20 domains | Split the request |

## More

- API reference: https://probekit.dev/docs/api
- OpenAPI 3.1: https://probekit.dev/openapi.json
- Payments: https://probekit.dev/docs/payments
- Findings reference: https://probekit.dev/findings
