Verification

Verify one address

Verifies a single address and answers with the verdict. Charges 1 credit, or 0.5 when the result is served from cache. The verdict does not change the price: an unknown, risky or catch_all result costs 1 credit, and is never served from cache.

If verification does not finish inside the request window the response is still 200, with status: "pending" and a job_id instead of a result. Poll GET /v1/jobs/{id} for the result. Calling this again while that check is still running joins it rather than charging again (below); once it has its answer, a repeat is a new request at the usual price.

Without a key, an identical request that arrives while the first is still in flight is not charged again. The same account asking for the same normalized address with the same deep_verify, include_ai, enrich, skip_cache and include_activity (timeout_ms does not count) joins the earlier request's verification: it is answered with that verification's result when it is ready inside this request's wait, or with the pending shape carrying the same job_id, and either way with credits_used: 0 and idempotent_replay: true. If that attempt ends without an answer while this request waits (it was refunded), this request gets a retryable 503 with nothing_charged: true instead. A concurrent pair is charged once. A verification counts as in flight until its answer exists, and for at most 15 minutes after its charge (the age at which job reconciliation refunds an unanswered one). A request that arrives after the answer existed is priced exactly as before: 0.5 credits from cache, or 1 for a fresh check. Another account's verification is never joined.

Send Idempotency-Key to make a retry safe at any time.

post/v1/verify

Request

  • Base URL: https://api.kawaa.com
  • URL: https://api.kawaa.com/v1/verify
  • Auth: one of:
    • API key in header X-Api-Key
    • HTTP bearer
    • OAuth 2 (authorization code, authorization URL https://api.kawaa.com/oauth/authorize, token URL https://api.kawaa.com/oauth/token, scopes: verification:write)

Headers

Idempotency-Keystring

Makes a charging call safe to retry: a repeat under the same key replays the original outcome instead of buying the work again. The window depends on the route: the receipt behind it expires, and after that the same key is a new paid request. It is 30 days for a deliverability check, at least 30 days for a single find, 24 hours for an activity lookup, and 15 minutes for a bulk find. What a replay returns also differs: a batch replays its JOB, an activity lookup replays its RESULT. A different request under the same key is 409 IDEMPOTENCY_KEY_CONFLICT, before any charge.

How long a key stays safe differs per route, and for the verification routes it depends on the plan. POST /v1/verify, /v1/verify/batch and /v1/verify/file keep their receipts for the job's data-retention period, never less than 30 days, so longer on plans that keep results longer. The job itself is kept only for the plan's data-retention period, which is 7 days on Free: a repeat that arrives after the job's retention period has ended but while its receipt is still live is refused with 410 IDEMPOTENT_JOB_EXPIRED and charged nothing, rather than bought a second time; send a new key to run it again. Once the receipt expires the same key claims a fresh target and creates another paid job. POST /v1/deliverability/check keeps its receipt 30 days. POST /v1/find keeps its receipt at least 30 days and its stored address only for the plan's retention period; a repeat after the address expires but before the receipt does return 410 IDEMPOTENT_JOB_EXPIRED without charging. The activity lookups keep receipts about 24 hours, and POST /v1/find/bulk 15 minutes. Without a key, GET /v1/activity/{email} and GET /v1/activity/domain/{domain} still answer an identical repeat from the same account free for 15 minutes after the lookup that was charged (POST /v1/find/bulk does the same for its content); a key buys a fresh read inside that window. An unkeyed POST /v1/verify that arrives while an identical one from the same account is still being verified (at most 15 minutes after its charge) joins it and is not charged; once that verification has its answer, a repeat is charged as usual. Only a key makes a later retry free.

Retry with the key you have rather than minting a new one. A key is not a refund (it prevents a second charge, it does not undo the first), but it stays good far longer than the shortest advertised retention suggests, and dropping one early is how a retry becomes a second paid job.

The characters are checked after trimming. Surrounding whitespace is trimmed from the header first; the resulting key must be 1-255 printable ASCII characters (0x21-0x7e) other than a comma, with no internal spaces or anything outside that range. Leading and trailing whitespace is accepted and is not part of the key. Because OpenAPI constraints apply to the raw header rather than the post-trim value, this schema deliberately has no minLength, maxLength, or pattern; those constraints would make generated clients reject values the API accepts. Invalid post-trim values get 400 before the route starts. A UUID is the obvious choice. Sending the header twice with different raw values is also a 400. A key may not contain a comma, because a header sent more than once reaches the API joined by commas; a comma is refused with the same 400, before anything is charged. A receipt claimed under a comma-bearing key before 2026-10-03 still replays for its window.

Request body

emailstring required

The address to verify. Normalized before it is validated, so this is deliberately not constrained with format: email: the API trims surrounding whitespace, lower-cases the address and punycodes an internationalized domain, and only then checks its syntax. So " User@Example.com " and user@münchen.de are both accepted and are charged like any other address, while a strict email-format validator would refuse the raw strings. The response echoes the NORMALIZED form, so compare against that, not against what you sent.

include_activityboolean

Attach your own account's engagement history for the address as activity. domain_reputation is reserved and not sent today, because Kawaa stores no domain reputation score. Adds NO charge of its own: the call debits once (1 credit fresh, 0.5 from cache) whether or not you ask for it.

Response

A verdict, or status: "pending" when the check is still running.

successunknown required

Example response

{
  "data": {
    "sub_status": "mailbox_exists",
    "ai": {
      "typo_method": "DirectMapping"
    },
    "suggestion": "jane@gmail.com"
  }
}

Changes

No recorded changes to this operation across all 1 revision of this API.