Log in Sign up
Docs/Validation/Email Validator

Email Validator v1.0 1 credit / call

GET|POST /api/v1/email-validator

Validate email addresses — syntax, MX/deliverability, disposable and role-based detection.

50 free/month Test keys supported OpenAPI spec Try it in the console
At a glance
Base path
/api/v1/email-validator
Methods
GET or POST — parameters are read identically from the query string or a JSON body
Auth
X-Api-Key header (never Authorization: Bearer)
Price
1 credit per call · about $0.0010 at 1,000 credits/USD
Free tier
50 credits, refilled every month — drawn down before your paid balance
Test mode
Supported — ksty_test_ keys call this API free of charge
Rate limit
60 calls/minute per key by default, raisable per key

Email Validator API

Validates an email address across four independent layers — syntax, domain deliverability (MX), disposable-domain detection and role-address detection — and returns each result separately so you decide how strict to be. Syntax and normalization always run locally; the MX check is a best-effort DNS lookup you can switch off per call.

What "valid" actually means

There is no single test that proves an address will receive mail — the only certain check is to send a message and watch for a bounce, which you cannot do at signup time. Validation is therefore a stack of cheaper signals, each ruling out a different class of bad address, and valid is a conservative summary of them. Treat the individual checks as the real output and valid as a sensible default.

POST /api/v1/email-validator
X-Api-Key: ksty_test_...
Content-Type: application/json

{"email": "jane.doe@example.com"}
{
  "email": "jane.doe@example.com",
  "normalized": "jane.doe@example.com",
  "valid": true,
  "checks": { "syntax": true, "mx": true, "disposable": false, "role": false },
  "domain": "example.com"
}

The four checks

Syntax

The address is parsed against the standard email grammar. This catches typos like a missing @, illegal characters or a malformed domain. It is deterministic and offline, so it always runs first — if syntax fails, nothing downstream can succeed.

MX / deliverability

The gateway looks up the domain's DNS records and asks a simple question: can this domain receive mail at all? A domain with an MX record (or a fallback A record) can accept mail; one with neither cannot, no matter how well-formed the address is. This proves the domain is deliverable — not that the specific mailbox exists, which no lookup can tell you.

example.com          -> DNS query for MX
                        -> mail.example.com (has MX)  => deliverable
typo-domain.invalid  -> no MX, no A                  => not deliverable

Disposable

The domain is matched against a curated list of throwaway / temporary-mailbox providers (Mailinator, Guerrilla Mail, 10 Minute Mail and similar). These addresses are syntactically perfect and often have valid MX, but exist only to slip past a signup — so a disposable hit forces valid to false.

Role-based

The local-part (the text before the @) is matched against common function addresses like info, support, sales or admin. These reach a team rather than a person. It is reported as a flag, not a failure — you might warn on it for a personal-account signup while happily accepting it on a contact form.

Accuracy & limitations

  • Mailbox existence is out of scope. A pass means the address is well-formed and its domain can receive mail — not that the specific inbox exists or is monitored.
  • MX is best-effort. If the DNS lookup is slow or inconclusive it returns null (unknown) rather than failing the call, and an unknown MX result does not on its own make an address invalid.
  • Lists are heuristic. The disposable and role lists are curated and self-contained; they catch the common cases, not every possible domain or alias.

The MX lookup adds network latency and can occasionally be inconclusive. For high-volume or offline validation — cleaning a large import, for example — send check_mx: false to run the fast, fully-local syntax and heuristic checks only.

When to use it

  • At signup, to reject typos and disposable addresses before they reach your database.
  • Cleaning an existing list, with check_mx: false for speed on large batches.
  • Gating sensitive flows where you want to flag (not necessarily block) role addresses.

Endpoints

GETPOST /api/v1/email-validator 1 credit

Validate a single email address

Parameters
ParameterTypeDescription
email required string The email address to validate
check_mx optional boolean Perform an MX/DNS lookup on the domain. Turn off for speed on large batches, or when running without outbound DNS. Defaults to true
Request
curl -X POST "https://ksty.ch/api/v1/email-validator" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email":"jane@example.com","check_mx":true}'
<?php
$ch = curl_init('https://ksty.ch/api/v1/email-validator');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'X-Api-Key: YOUR_API_KEY',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => '{    "email": "jane@example.com",    "check_mx": true}',
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
const response = await fetch('https://ksty.ch/api/v1/email-validator', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    "email": "jane@example.com",
    "check_mx": true
})
});
const data = await response.json();
import requests

response = requests.post(
    'https://ksty.ch/api/v1/email-validator',
    headers={'X-Api-Key': 'YOUR_API_KEY'},
    json={
    "email": "jane@example.com",
    "check_mx": true
},
)
data = response.json()
Response 200 OK
{
    "email": "Jane@Example.com",
    "normalized": "jane@example.com",
    "valid": true,
    "checks": {
        "syntax": true,
        "mx": true,
        "disposable": false,
        "role": false
    },
    "domain": "example.com"
}
Response fields
FieldTypeDescription
email string The address exactly as you sent it, trimmed.
normalized string Lower-cased form — store this if you deduplicate addresses.
valid boolean The overall verdict: correct syntax, not a known disposable domain, and the MX check did not come back negative. An inconclusive MX lookup does not make an address invalid.
checks.syntax boolean Whether the address parses as a valid address.
checks.mx boolean|null true when the domain accepts mail, false when it demonstrably does not, and null when the lookup was skipped or inconclusive. Treat null as "unknown", never as a failure.
checks.disposable boolean The domain is a known throwaway-mail provider.
checks.role boolean The local part is a role address (info, support, admin…). Reported but never counted against "valid" — plenty of real users sign up with one.
domain string Domain part of the normalized address, empty when the input had no "@".

Errors

Errors are JSON with a stable error.code — branch on the code, never on the message. Everything below carries the X-Request-Id of the failed call. Requests rejected by this API are not charged, and a 502 after a charge is refunded automatically.

CodeHTTPCause & what to do
invalid_request this API 400 No "email" parameter was sent, or it was an empty string. Send the address you want checked. Note that a malformed address is not an error — it comes back 200 with "valid": false, so you can tell "you called this wrong" apart from "this address is bad".
missing_key platform 401 No API key was sent. Send your key in the X-Api-Key header (or an api_key parameter).
invalid_key platform 401 The key is not recognised — mistyped, truncated or never issued. Copy the key again from the API keys page. Repeated bad keys from one address are throttled, so fix it rather than retrying.
insufficient_credits platform 402 The balance can't cover the call. Top up on the Billing page. Nothing is charged and the call never reaches the API.
key_suspended platform 403 The key is suspended (for example after an abuse review). The reason is shown on the API keys page; contact support to have it restored.
key_revoked platform 403 The key was revoked, or it belongs to a team you have left. Create or use another key.
key_expired platform 403 The key is past the expiry date set on it. Create a new key, or one without an expiry.
out_of_scope platform 403 The key is valid but not scoped to this API. Add this API to the key's scopes, or use a key scoped to all APIs.
sandbox_disabled platform 403 A test (ksty_test_) key was used on an API with no sandbox. Use a live key for this API.
ip_blocked platform 403 The key has an IP allowlist and the calling address isn't on it. Call from an allowed address, or edit the key's allowlist.
geo_blocked platform 403 Calls from this country aren't permitted for this API. Contact support if you believe this is wrong.
spend_cap_reached platform 403 The account has the credits, but this API's monthly spend cap is used up. Raise or clear the cap on the API's page in your dashboard — topping up won't help.
unknown_api platform 404 No public API has that slug. Check the slug against the catalog.
unknown_endpoint platform 404 The API has no endpoint with that name. Check the endpoint list on the API's reference page.
method_not_allowed platform 405 The endpoint accepts only GET or only POST. Use the method shown for the endpoint.
rate_limited platform 429 A rate limit was exceeded — per key, per account across all your keys, or the tighter per-account limit on AI APIs. Wait for Retry-After seconds. A second key shares the account limit, so it won't help.
api_unavailable platform 502 The API is temporarily unavailable. Nothing was charged. Retry later; check the status page.
upstream_error platform 502 The API failed while processing the call. Safe to retry — any credits taken are refunded automatically.

Response headers

Every successful call reports its own cost, so you never have to guess what a request spent or reconcile it later.

HeaderMeaning
X-Request-Id Identifier for this call — on every response, in every error body and in your usage log. Quote it in support requests.
X-Credits-Charged Credits taken for this call. Zero on test-mode calls and on errors raised before the work started.
X-Credits-Remaining Balance left afterwards, across per-API grants, promotional and paid credits.
X-RateLimit-Limit Requests allowed per minute by the limit you are closest to (key, account or AI tier).
X-RateLimit-Remaining Requests left in the current window of that limit.
X-RateLimit-Reset Unix time when that window resets.
Retry-After On a 429 only: seconds to wait before retrying.