# Validate an email list with Tuski's email validation API

Tuski's email validation API checks a list of addresses as one job, using real probe sends and bounce signals behind each verdict. A job is priced when it is created, at $0.01 per address per address pay as you go. Send `POST https://agents.tuski.ai/zcj/8hgbbddeuc61/api/v1/email-validation/jobs` with an `Idempotency-Key` header and the addresses, pay the 402 price quote and retry, then poll the job and fetch its results at no charge.

## At a glance

- **Endpoint:** `POST https://agents.tuski.ai/zcj/8hgbbddeuc61/api/v1/email-validation/jobs`. One call creates one job.
- **Body:** either `emails` (1 to 100,000 addresses) or `csv_text` (raw CSV whose first row is a header and which includes an email column), never both. `fresh_probe` is optional. The `Idempotency-Key` header is required.
- **Price:** $0.01 per address per address pay as you go. Prepaid credit starts from $0.0036 per address.
- **Payment:** a 402 price quote that you pay and retry. No account or API key is needed.
- **Response:** `201 Created` with `job_id`, `total`, `queued`, `projected_hours`, `projected_finish_at` and `long_queue`.
- **Results:** job status at `GET /api/v1/email-validation/jobs/{job_id}` and verdicts at `GET /api/v1/email-validation/jobs/{job_id}/results`, as JSON pages or CSV. Both reads cost nothing.
- **Full purchase flow:** [the buyer skill](https://agents.tuski.ai/zcj/8hgbbddeuc61/SKILL.md)

## Create a validation job and fetch the verdicts

1. Create the job. Pick an `Idempotency-Key` for this list and keep it, because it identifies the job if you need to resend.

   ```bash
   curl -s -X POST https://agents.tuski.ai/zcj/8hgbbddeuc61/api/v1/email-validation/jobs \
     -H 'Content-Type: application/json' \
     -H 'Idempotency-Key: crm-import-0001' \
     -d '{"emails": ["jane@example.com", "sales@example.com", "j.smith@example.com"]}'
   ```

   The storefront answers `402` with the price quote for the job. Nothing is charged until you pay it.

2. Pay the quote and send the identical request again, with the same headers and body plus the payment proof. The [buyer skill](https://agents.tuski.ai/zcj/8hgbbddeuc61/SKILL.md) has the payment steps. The response is `201 Created`, with a `Location` header and the job object described below. The create call returns before any validation finishes; an example call answered in 0.9 seconds. On a credit plan, the `zc-billing` response header reports `chargedUsd` and `remainingCreditUsd`, and that example drew $0.018 from prepaid credit.

3. Check the job's status when `projected_finish_at` comes round. Each check costs nothing.

   ```bash
   curl -s "https://agents.tuski.ai/zcj/8hgbbddeuc61/api/v1/email-validation/jobs/<job_id>"
   ```

   The response carries `job_id`, `status`, `eta` and `sent`, plus counters per outcome: `valid`, `invalid`, `unreachable`, `mailbox_full`, `blocked`, `soft_temp`, `other_bounce`, `routing_error` and `unknown`. Through the storefront the call answers at once, since Tuski ignores its `wait` long-poll parameter on ZeroClick-signed requests, so space the checks out until `status` is `done`.

4. Fetch the results. This call also costs nothing.

   ```bash
   curl -s "https://agents.tuski.ai/zcj/8hgbbddeuc61/api/v1/email-validation/jobs/<job_id>/results?format=json&limit=1000&offset=0"
   ```

   A JSON page holds `rows`, `count`, `limit`, `offset` and `has_more`. While `has_more` is true, request again with `offset` increased by `limit`. With `format=csv`, the body has no `has_more`. Read the `X-Total-Count` header instead, and follow the `Link: <...>; rel="next"` header when more rows remain. `limit` takes 1 to 5000. The `status` query parameter filters rows by each address's status value, such as `valid`, `invalid` or `mailbox_full`; the API reference lists the accepted values.

## Job response fields

| Field | What it holds |
| --- | --- |
| `job_id` | The job's integer id, used in the status and results paths |
| `total`, `queued` | The job's address counts |
| `projected_hours` | Hours until the job is expected to finish. It is `null` when no mailbox can send. |
| `projected_finish_at` | The expected finish time (UTC, ISO 8601) |
| `long_queue` | `true` when the job is projected to run longer than 48 hours |
| `idempotency` | `explicit` (the key came from your header) or `auto` |
| `idempotent_hit` | `true` when the call replayed an existing job |
| `status` | The existing job's status. It appears on a replay only. |
| `next_step` | What to do next. It appears on an auto-keyed replay only. |

## Verdicts and score

Tuski assigns every address one verdict from a set of eight: deliverable, catch-all, role, full box, invalid, no MX, typo and burner. A result row carries it in its `verdict` field, written in the API's own form, such as `catch_all` or `full_box`. Two of them in detail:

- `no_mx` marks a domain with no mail server.
- `typo` marks a misspelling of a well-known mail provider's domain.

Each address also gets a quality score from 0 to 100. The [API reference](https://agents.tuski.ai/zcj/8hgbbddeuc61/services/email-validation/openapi.json) lists every value the `verdict` field can take.

## What a job costs

A job is priced when it is created, per address.

| Addresses | Plan | Cost |
| --- | --- | --- |
| 1,000 | Pay per address | $10 |
| 1,000 | Credits 500 (minimum purchase $3) | $6 |
| 10,000 | Credits 10,000 (minimum purchase $55) | $55 |
| 50,000 | Credits 50,000 (minimum purchase $180) | $180 |

A credit plan is bought with `POST https://agents.tuski.ai/zcj/8hgbbddeuc61/plans/{planId}/purchase` and an agent bearer credential, which the storefront issues through [auth.md](https://agents.tuski.ai/zcj/8hgbbddeuc61/auth.md). You find each plan's purchase URL in the [catalog](https://agents.tuski.ai/zcj/8hgbbddeuc61/manifest.json). Once you hold a plan, jobs draw down its balance.

## Limits

- The body takes exactly one of `emails` or `csv_text`, plus `fresh_probe`, and no other fields. The whole body is capped at 25 MB and `emails` at 100,000 entries.
- Verdicts are never in the create response. Addresses that pass the pre-send checks get a probe send, and the job waits for bounce signals, so results come later.
- No field sets probe content. Tuski writes every probe message itself.
- By default, an address checked recently can keep its remembered result. Set `"fresh_probe": true` to send a new probe to every address.
- A 402 you do not pay charges nothing, so you can safely stop there. Resending the same request with the same `Idempotency-Key` replays the original job instead of creating a second one.

## Errors

| Status or code | Meaning | Action |
| --- | --- | --- |
| `402` | The price quote | Pay it and retry the identical request, or stop |
| `422` on create | `Idempotency-Key` is missing or the body is invalid | Add the header and send exactly one of `emails` or `csv_text` |
| `409` | Idempotency conflict | Use a new key for a new list, and reuse a key only to replay the same request |
| `413` | The request or the CSV export is too large | Split the list, or page the export with `limit` and `offset` |
| `503` on create | Temporarily unavailable | Retry shortly with the same `Idempotency-Key` |
| `404 job_not_found` | No such job for this caller | Check `job_id` |
| `422 unknown_status_filter` | The results `status` filter is not a recognized value | Use one of the listed outcomes, or omit it |
| `429` | Rate limited | Wait for `Retry-After`, then retry |
| `usage_exhausted` | The prepaid credit has run out | Top up with `POST https://agents.tuski.ai/zcj/8hgbbddeuc61/extend`, or pay per job |

## Frequently asked questions

### What does a validation job cost?

$0.01 per address per address pay as you go, or less on a Credits plan, priced when the job is created. Polling status and fetching results cost nothing on every plan.

### How long does an email validation job take?

A job waits for real bounce signals, so its verdicts come well after the create call. The create response gives the estimate in `projected_hours` and `projected_finish_at`, and `long_queue` is `true` when the job is projected to run longer than 48 hours.

### Can I send a CSV instead of a JSON list?

Yes. Send the CSV as raw text in `csv_text`, with a header row and an email column, for example `{"csv_text": "email\njane@example.com\nsales@example.com\n"}`. The API takes text in the JSON body, not a file upload.

### What if the create response is lost?

Resend the identical request with the same `Idempotency-Key`. The API replays the original job with `200` and `idempotent_hit: true` instead of creating a new one, and you can then poll it by `job_id`.

### Do I need an account or API key to use the email validation API?

No. Pay-as-you-go jobs are paid through the 402 quote alone. Only buying or using a credit plan needs an agent credential, which the storefront issues for free.

## Keep reading

- [Buyer skill](https://agents.tuski.ai/zcj/8hgbbddeuc61/SKILL.md): the full purchase flow
- [How to pay](https://agents.tuski.ai/zcj/8hgbbddeuc61/payment.md): x402, MPP, card and credit plans
- [Agent identity](https://agents.tuski.ai/zcj/8hgbbddeuc61/auth.md): the bearer credential for credit plans
- [Email Validation API reference](https://agents.tuski.ai/zcj/8hgbbddeuc61/services/email-validation/openapi.json): every field and verdict value
- [Error reference](https://agents.tuski.ai/zcj/8hgbbddeuc61/errors.md): payment and access errors

---

Updated 2026-10-22. All Tuski guides: https://agents.tuski.ai/guides.md. Storefront: https://agents.tuski.ai/zcj/8hgbbddeuc61/llms.txt.
