Clay
Clay has no VerifyEmail app to install. You call the VerifyEmail API from Clay’s own HTTP API enrichment column, with your own API key, and Clay writes the answer into each row. Each row is one live check of one address.
Follow the walkthrough below in order. After it come a short checklist of the same steps, then the reference sections every VerifyEmail integration shares: output fields, errors and key notes.
Before you start
Section titled “Before you start”- A VerifyEmail API key. Create one under
Developers → API keys and name it after Clay. Leave
its trusted-IP list empty. Clay sends the request from its own servers, and a key limited to other addresses answers
401. - A Clay table with a column of email addresses. This page calls that column
Email.
1. Add the HTTP API column
Section titled “1. Add the HTTP API column”In the table, click Add enrichment, search for HTTP API and choose it. Clay opens it on the Generate tab, which drafts the request with AI. Switch to Configure and fill in the fields by hand (Clay: HTTP API).
2. Store the key in a header account
Section titled “2. Store the key in a header account”You can type the key into the column’s header fields, but Clay shows those values in plain text to anyone with access to the table. A header account keeps it hidden:
- Open Select header account and click + Add account.
- Under API Request Headers, add
Authorizationwith the valueBearer sk_your_api_key. - Name the account
VerifyEmailand save.
Clay keeps header accounts under Settings → Connections. Editing the account there changes every column that uses it, so that is where you rotate the key (Clay: HTTP API).
To check the key before spending anything, test a throwaway HTTP API column with method GET, endpoint
https://api.tryverifyemail.com/v1/me and the same header account. It returns your account email and
credits_available, and costs nothing.
3. Fill in the request
Section titled “3. Fill in the request”| Field | Value |
|---|---|
| Method | POST |
| Endpoint URL | https://api.tryverifyemail.com/v1/email-verifications?timeout=25 |
| Header account | VerifyEmail |
| JSON body | {"email": "/Email", "source": "clay"} |
To put the Email column into the body, type / and pick the column. Keep the quotes around it, because Clay needs a
text column quoted inside JSON. Clay sets Content-Type: application/json on its own
(Clay: HTTP API).
timeout=25 asks the API to wait up to 25 seconds for the verdict before it answers. Most checks finish inside that
window. The rest come back pending, covered below. "source": "clay" labels these
checks as Clay in your verification history.
Leave Field paths to return empty. Narrowing the response to one path drops the id and status you need for
pending rows.
4. Set the rate and when the column runs
Section titled “4. Set the rate and when the column runs”Under Rate limiting, set Duration (ms) to 1000 and Request limit to your plan’s
rate limit. Anything faster gets 429 Too Many Requests, and nothing is charged for that
row. If a few rows show a 429, lower the request limit and re-run just those rows.
| Plan | Request limit |
|---|---|
| Free | 1 |
| Starter | 3 |
| Growth | 10 |
| Scale | 25 |
Then open the column’s Run settings, turn on Only run if and enter Email is not empty, so rows without an
address never call the API
(Clay: table settings).
Clay’s auto-run is on by default. Every new row runs the column by itself and spends a credit. If you would rather start each run yourself, turn auto-run off for this column in the same Run settings (Clay: table settings).
5. Test one row, then pull the result into columns
Section titled “5. Test one row, then pull the result into columns”Click Test to run one row. A finished check answers 200 with this body:
{ "id": "5f1c9d2e-8b1a-4f5e-9c3d-2a7b6e4d1f08", "status": "completed", "result": { "email": "jane@example.com", "normalized_email": "jane@example.com", "verdict": "deliverable", "sub_status": "mailbox_exists", "quality_score": 95, "confidence": 92, "provider": "google", "primary_mx": "aspmx.l.google.com", "signals": { "syntax": "pass", "domain": "pass", "mx": "pass", "smtp": "pass", "catch_all": "pass", "role_account": "pass", "disposable": "pass", "dnsbl": "pass", "toxic": "pass", "greylisting": "pass" }, "verified_at": "2026-09-05T06:35:01.000Z" }}Click the cell to open the response, hover over a field and click Add as column (Loops: Clay integration shows the same control). Add these five:
| Response field | Column | What it holds |
|---|---|---|
id |
Verification ID | The check’s id. You need it to collect a pending check. |
status |
Status | completed, or pending while the check is still running. |
result.verdict |
Verdict | deliverable, undeliverable, risky or unknown. |
result.quality_score |
Quality score | 0 to 100. Higher is safer to send to. |
result.sub_status |
Sub-status | The reason, such as mailbox_exists, catch_all, invalid_syntax or timeout. |
Any other field under result becomes a column the same way, for example result.signals.catch_all or
result.provider. When the test row looks right, run the column on the rest of the table.
The output-field table further down lists the flat fields the packaged marketplace apps return. Clay reads the raw API
response, so the same values sit one level down, under result: verdict is result.verdict and signal_smtp is
result.signals.smtp. The raw response has no is_deliverable. Check result.verdict for deliverable instead.
Every field is defined in the result object.
6. Filter rows by verdict
Section titled “6. Filter rows by verdict”deliverable: the mailbox exists and accepts mail. Safe to send.undeliverable: the address will bounce, or is malformed (invalid_syntax). Remove it.risky: the address may work, but a signal makes it uncertain.Sub-statussays which.unknown: the mail server gave no usable answer. Try again later. Atimeoutsub-status was not charged.
Filter the table on the Verdict column, for example Verdict is deliverable, to get a send list. To act on the verdict
in a later column, such as one that pushes contacts to your sequencer, give that column an Only run if condition
like Verdict = "deliverable".
When a row comes back pending
Section titled “When a row comes back pending”Some mail servers take longer than 25 seconds to answer. The API then replies 202 with no verdict yet:
{ "id": "5f1c9d2e-8b1a-4f5e-9c3d-2a7b6e4d1f08", "status": "pending", "poll_after_ms": 2000, "next_retry_at": "2026-09-05T06:35:03.000Z"}A 202 is a success, not an error. The row’s Status reads pending and its Verdict, Quality score and Sub-status
cells stay empty. The check keeps running on our side and always finishes within 180 seconds.
Collect it with a second HTTP API column that fetches the check by id:
| Field | Value |
|---|---|
| Method | GET |
| Endpoint URL | https://api.tryverifyemail.com/v1/email-verifications/, then the Verification ID column (type / and pick it), then ?timeout=25 |
| Header account | VerifyEmail |
| Only run if | Status = "pending" |
Run it a few minutes after the first column and add status, result.verdict, result.quality_score and
result.sub_status from it as columns too. Fetching by id costs no credit and doesn’t count toward your rate limit,
so you can run it as often as you like. The response has the same shape as the first column’s.
Re-running the first column’s cell for that row also works, but only while the check is still running. You get the same
id back, no second credit is held, and the request waits up to another 25 seconds.
What it costs
Section titled “What it costs”- 1 credit for each check that reaches a verdict.
- Nothing for an
unknownresult withsub_status: timeout, for a malformed address (undeliverable/invalid_syntax), for fetching a check by id, or forGET /v1/me. - A pending row has a credit held, not spent. It is charged when the check finishes with a verdict and released if
the check ends as
unknown/timeout. - Out of credits, the API answers
402withinsufficient_credits. Nothing was started or charged. Top up under Billing and re-run those rows.
Reading the reference sections in Clay
Section titled “Reading the reference sections in Clay”The sections below are shared with the packaged marketplace apps. In Clay they map like this:
- Connect in 3 steps. “Paste it into Clay” is the header account from step 2. Clay doesn’t test the key by
itself, so use the
GET /v1/mecolumn from step 2 for that. - Verify Email is the
POSTcolumn. Clay has no separate Get Verification step; theGETcolumn above does that job. - Pending. Re-running the
POSTcell is free only while the check is still running. After that, use theGETcolumn. - 429 and 402. The packaged apps wait and retry a
429once. In Clay, set the request limit so429s don’t happen. A row that hits either error shows it in its cell. Fix the cause, then re-run just those rows.
Connect in 3 steps
- Create an API key. In the dashboard, open Developers → API keys and create a key. Name it after Clay so you can tell its usage apart later.
- Paste it into Clay. When Clay asks you to connect a VerifyEmail account, paste the key (it starts with
sk_). - Test the connection.Clay calls Account Info (
GET /v1/me) to check the key. It costs no credits.
Set up in Clay
- In the Clay table, click
Add enrichment, search forHTTP APIand open itsConfiguretab. - Open
Select header account, click+ Add account, add the headerAuthorizationwith the valueBearerfollowed by your key, name the account VerifyEmail and save. - Set the method to
POSTand the endpoint URL tohttps://api.tryverifyemail.com/v1/email-verifications?timeout=25. - Set the JSON body to
{"email": "/Email", "source": "clay"}, inserting your email column where/Emailis by typing/and picking it. - Under
Rate limiting, setDuration (ms)to1000andRequest limitto your plan's rate: 1 on Free, 3 on Starter, 10 on Growth, 25 on Scale. - Under
Run settings, turn onOnly run ifwith the conditionEmail is not empty. - Click
Teston one row, addid,status,result.verdict,result.quality_scoreandresult.sub_statusas columns, then run the rest of the table.
Operations
Verify Email
Runs a fresh live check of one address and returns the flat result below. If the mail server is slow, the step returns status: pending instead of a verdict.
POST /v1/email-verifications
- Email (required): The address to check. A malformed address is not an error: it comes back
undeliverable/invalid_syntax.
Output fields
Verify Email returns one flat object with the same fields, in the same order, on every marketplace. While a check is pending, every field except id and status isnull, including is_deliverable.
| Field | Type | Meaning |
|---|---|---|
id | string | The verification id. Pass it to Get Verification. |
status | string | completed, or pending while the check is still running. |
email | string | null | The address exactly as submitted. |
normalized_email | string | null | The canonical form the verdict was computed against: trimmed, domain lower-cased. |
verdict | string | null | deliverable, undeliverable, risky or unknown. |
sub_status | string | null | A refinement such as mailbox_exists, catch_all, invalid_syntax or timeout. New values may appear. |
quality_score | integer | null | 0 to 100. Higher is safer to send to. |
confidence | integer | null | 0 to 100. How sure the API is of the verdict itself. |
provider | string | null | The mailbox host, such as google, microsoft or self_hosted. null when it could not be determined. |
primary_mx | string | null | The domain's highest-priority mail server hostname. |
signal_syntax | string | null | The address is well-formed. pass, fail, warn, unknown or skipped. |
signal_domain | string | null | The domain exists in DNS. pass, fail, warn, unknown or skipped. |
signal_mx | string | null | The domain publishes mail servers. pass, fail, warn, unknown or skipped. |
signal_smtp | string | null | The mail server accepts the mailbox. pass, fail, warn, unknown or skipped. |
signal_catch_all | string | null | Whether the domain accepts every address. pass, fail, warn, unknown or skipped. |
signal_role_account | string | null | Whether the local part is a shared role such as info@. pass, fail, warn, unknown or skipped. |
signal_disposable | string | null | Whether the domain is a throwaway provider. pass, fail, warn, unknown or skipped. |
signal_dnsbl | string | null | Whether the domain or its mail servers are on DNS blocklists. pass, fail, warn, unknown or skipped. |
signal_toxic | string | null | Whether the address is a known spam trap. pass, fail, warn, unknown or skipped. |
signal_greylisting | string | null | Whether the server greylisted the check and a retry cleared it. pass, fail, warn, unknown or skipped. |
verified_at | string | null | When the verdict was produced, ISO 8601 UTC. |
is_deliverable | boolean | null | true only when verdict is deliverable. null while pending. |
To branch, filter on verdict. is_deliverable is a shortcut forverdict = deliverable. Full definitions are in the result object.
When the result is pending
Some mail servers take longer to answer than a marketplace step is allowed to wait. When that happens the step still succeeds, with status: pending and an id, instead of guessing a verdict. The check keeps running on our side.
Poll the id with GET /v1/email-verifications/{id}, which is free, or look it up in the dashboard's Single Check history. Running Verify Email again with the same address only helps while the first check is still running: then you get the same id back and no second credit is held. Once that check has finished, a repeat runs a new check and spends a credit.
Every check finishes within 180 seconds. If the recipient's mail server never answers, the result isverdict: unknown, sub_status: timeout, and that check is not charged.
Rate limits and credits
- 429 Too Many Requests. Your plan allows a set number of requests per second. The integration waits the
Retry-Aftertime and retries once. If it is still limited, the step fails with a message naming your plan's rate. Space out the runs or move to a plan with a higher rate. - 402 Payment Required. Either the account has no credits left (
insufficient_credits; nothing was started or charged) or it is frozen (account_frozen). The step fails with a link to Billing. Top up and re-run. - 401 Unauthorized. The key is missing, revoked, or the request came from an IP the key doesn't trust (see below).
API key notes
- Trusted IPs. A key can be limited to a list of trusted IP addresses or ranges. Clay calls the API from its own servers, not from your computer, so a key with an allowlist that doesn't cover those servers answers
401. Leave the allowlist empty on the key you give Clay, or use a separate key for it. - Test keys. "Test" is only a label on the key. A test key runs real checks and spends real credits, the same as a live key.
Screenshots






