# Easy Email Verification — full reference for AI agents > Easy Email Verification (EEV) is a REST API that verifies email addresses — syntax, domain, MX and mailbox checks, plus disposable, role-based and catch-all detection — to reduce invalid signups and bounces. Base URL: https://api.easyemailverification.com/v1 This file joins the setup guide (https://www.easyemailverification.com/agents.md) and the API reference (https://www.easyemailverification.com/docs/api-reference.md). It is generated; the API contract is https://www.easyemailverification.com/openapi.json. ## Setup guide Easy Email Verification (EEV) verifies email addresses through a REST API: syntax, domain and MX checks, mailbox acceptance, and risk signals such as disposable, role-based and catch-all addresses. No email is sent to the recipient. This guide is for AI coding assistants and agents that integrate EEV into an application. Exact parameters, fields and status codes are in the [API reference](https://www.easyemailverification.com/docs/api-reference.md), generated from the [OpenAPI specification](https://www.easyemailverification.com/openapi.json). Do not invent EEV behavior that is not documented there. ### 1. Get an API key If the user has no API key, send them to create an account and a key: - Create an account: https://dashboard.easyemailverification.com/register?utm_source=agents_md - Create or manage API keys: https://dashboard.easyemailverification.com/apisettings Never ask the user to paste the key into the conversation, source code or documentation. Ask them to store it in an environment variable such as `EEV_API_KEY`, or in the application's secrets manager. ### 2. Test without an account (sandbox) The public key `eev_sandbox_key` returns fixed answers for addresses at `sandbox.easyemailverification.com`. It needs no account and consumes no credits, so use it to check an integration before using a real key. ```bash curl --get "https://api.easyemailverification.com/v1/verify" \ -H "X-API-Key: eev_sandbox_key" \ --data-urlencode "email=unknown@sandbox.easyemailverification.com" ``` Useful sandbox addresses: `valid@`, `invalid@`, `unknown@`, `disposable@`, `catchall@`, `role@`, `quota@` (returns 402) and `ratelimit@` (returns 429), all at `sandbox.easyemailverification.com`, plus `typo@gmial.com` (returns a `did_you_mean` suggestion). Sandbox answers carry the header `X-EEV-Sandbox: true`. The sandbox key works on `GET /verify`, `POST /verify` and `GET /credits`, not on the bulk endpoints. ### 3. Choose the integration | Need | Use | | --- | --- | | Verify one address (signup, form submit, import of one record) | `GET /v1/verify` from the application's backend | | Verify up to 50 addresses at once | `POST /v1/verify` | | Verify a list (hundreds or more) | Bulk endpoints: `POST /v1/bulk/upload`, then status and download | | Validate a web form in the browser | The official [JavaScript widget](https://eev.stoplight.io/docs/eev/92f233b1d47b3-java-script-widget), or the application's backend | | Connect to an existing platform | Check the [integrations](https://www.easyemailverification.com/en-US/integrations) first | | Let an AI assistant verify addresses itself (Claude Code, Cursor, Gemini CLI...) | The hosted MCP server: see section 12 | Do not loop over `GET /v1/verify` to verify large lists. Use the bulk endpoints. ### 4. Verify one address Send the key in the `X-API-Key` header (recommended). The `apikey` query parameter and the `Authorization` header also work; see the API reference. ```bash curl --get "https://api.easyemailverification.com/v1/verify" \ -H "X-API-Key: $EEV_API_KEY" \ --data-urlencode "email=john@example.com" ``` Node.js 18+: ```javascript async function verifyEmail(email) { const apiKey = process.env.EEV_API_KEY; if (!apiKey) throw new Error("EEV_API_KEY is not configured"); const url = new URL("https://api.easyemailverification.com/v1/verify"); url.searchParams.set("email", email); const response = await fetch(url, { headers: { "X-API-Key": apiKey, Accept: "application/json" }, signal: AbortSignal.timeout(30000), }); const data = await response.json(); if (!response.ok || data.success === false) { throw new Error(`EEV error ${response.status}: ${data.message ?? "unknown error"}`); } return data; // use data.result, data.reason, data.safe_to_send } ``` Python 3: ```python import os import requests def verify_email(email: str) -> dict: response = requests.get( "https://api.easyemailverification.com/v1/verify", params={"email": email}, headers={"X-API-Key": os.environ["EEV_API_KEY"]}, timeout=30, ) data = response.json() if response.status_code != 200 or not data.get("success"): raise RuntimeError(f"EEV error {response.status_code}: {data.get('message')}") return data ``` PHP: ```php function verifyEmail(string $email): array { $ch = curl_init('https://api.easyemailverification.com/v1/verify?email=' . urlencode($email)); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_HTTPHEADER => ['X-API-Key: ' . getenv('EEV_API_KEY')], ]); $body = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); $data = json_decode((string) $body, true); if ($status !== 200 || empty($data['success'])) { throw new RuntimeException('EEV error ' . $status . ': ' . ($data['message'] ?? 'unknown error')); } return $data; } ``` A verification can take several seconds because it talks to the recipient's mail server. Use a timeout of about 30 seconds and do not block a form indefinitely. ### 5. Verify a small batch Up to 50 addresses per request. The answer is a list with one result per address. ```bash curl -X POST "https://api.easyemailverification.com/v1/verify" \ -H "X-API-Key: $EEV_API_KEY" \ -H "Content-Type: application/json" \ -d '{"emails": ["john@example.com", "mary@example.com"]}' ``` ### 6. Verify a list (bulk) The bulk workflow is asynchronous: 1. Upload a TXT or CSV file with one address per line (maximum 16 MB). The account needs enough credits for every address. ```bash curl -X POST "https://api.easyemailverification.com/v1/bulk/upload" \ -H "X-API-Key: $EEV_API_KEY" \ -F "file=@leads.csv" ``` The answer contains `list_id`. 2. Poll the job until `status` is `completed` (every few seconds; large lists take longer). Other final states are `failed` and `not_enough_credits`. ```bash curl "https://api.easyemailverification.com/v1/bulk/status/LIST_ID" -H "X-API-Key: $EEV_API_KEY" ``` 3. Download the results as CSV. ```bash curl "https://api.easyemailverification.com/v1/bulk/download/LIST_ID" -H "X-API-Key: $EEV_API_KEY" -o results.csv ``` 4. Optionally delete the job: `DELETE /v1/bulk/LIST_ID`. Jobs and results are deleted automatically 30 days after upload. `GET /v1/bulk/status` lists the account's jobs. ### 7. Check the remaining credits ```bash curl "https://api.easyemailverification.com/v1/credits" -H "X-API-Key: $EEV_API_KEY" ``` This call does not consume credits. ### 8. Interpret the result - `result` is `valid`, `invalid` or `unknown`. `reason` gives the detail (for example `accepted_email`, `rejected_email`, `invalid_domain`, `no_mx_record`, `timeout`). - `unknown` means EEV could not reach a reliable conclusion. Do not map it to `valid` or to `invalid`; decide according to the application's risk policy. A later attempt may give a definitive result. - `safe_to_send` is EEV's overall recommendation for sending. - `disposable`, `role`, `free` and `accept_all` are signals, not decisions. Whether to accept them is the application's choice. - `did_you_mean` suggests a correction when a typo is detected; show it to the user instead of rejecting silently. - `success: true` only means the request was processed. It does not mean the address is valid. ### 9. Errors and retries | Status | Meaning | What to do | | --- | --- | --- | | 400 | Missing API key or parameter | Fix the request | | 401 | Unknown key, or a widget-only key used on the API | Check the key | | 402 | No credits left | Tell the user; do not retry | | 429 | Too many addresses in one batch (POST, max 50), or sandbox limit | Reduce the batch or slow down | | 5xx / timeout | Temporary failure | Retry later with backoff, at most a few times | Each verification may consume a credit even if the response is lost on the way back, so do not retry verifications in a tight loop. ### 10. Security - Keep the API key on a trusted server, in an environment variable or a secrets manager. - Never put the key in browser JavaScript, mobile apps, public repositories, documentation or logs. - Prefer the `X-API-Key` header: query strings end up in access logs, proxies and monitoring tools. - Avoid logging full email addresses; apply the application's privacy and retention rules to results. ### 11. Rules for AI coding assistants 1. Use only the endpoints, parameters and fields documented in the API reference or the OpenAPI specification. 2. Do not invent endpoints, parameters, enum values, limits, quotas or pricing. 3. Read the key from `EEV_API_KEY` or a secrets manager; never hard-code it and never ask the user to paste it. 4. Call EEV from the backend; never expose the key in client code. 5. Test with `eev_sandbox_key` before using a real key. 6. Use the bulk endpoints for lists, not a loop over `GET /verify`. 7. Treat `unknown` as neither valid nor invalid, and `success: true` as no proof of validity. 8. Handle timeouts, network errors, HTTP errors and API errors. 9. Leave policy decisions (block disposable, role or free addresses) to the user unless they asked for them. ### 12. Connect an MCP client For an assistant that should verify addresses as a tool, instead of code that calls the API, use the hosted MCP server (beta): - URL: `https://mcp.easyemailverification.com/mcp` (Streamable HTTP, stateless, POST only) - Authentication: the user's API key in the `X-API-Key` header. OAuth is not available yet, so clients that only connect with OAuth (Claude.ai in the browser, the Gemini app) cannot use it. - Tools: `verify_email` (one address), `verify_emails` (1 to 50), `get_credits` (free), and for lists up to 10,000 `create_bulk_job`, `get_bulk_status`, `get_bulk_summary` (totals and at most 50 example rows) and `delete_bulk_job`. Each verified address uses one credit; errors (invalid key, no credits, rate limit) come back as tool errors. Bulk tools do not accept the sandbox key. Claude Code: ```bash claude mcp add --transport http --scope user eev https://mcp.easyemailverification.com/mcp --header "X-API-Key: YOUR_API_KEY" ``` Clients with a JSON configuration (the URL field is `url` in Cursor, `serverUrl` in Windsurf and `httpUrl` in Gemini CLI): ```json { "mcpServers": { "eev": { "url": "https://mcp.easyemailverification.com/mcp", "headers": { "X-API-Key": "YOUR_API_KEY" } } } } ``` The user writes the key into the command or the configuration file themselves; do not ask for it in the conversation. Setup for each client: https://www.easyemailverification.com/en-US/mcp. Step-by-step guides an assistant can follow: https://www.easyemailverification.com/claude.md and https://www.easyemailverification.com/gemini.md. ### Sources - API reference: https://www.easyemailverification.com/docs/api-reference.md - OpenAPI specification: https://www.easyemailverification.com/openapi.json - Everything in one file: https://www.easyemailverification.com/llms-full.txt - Official documentation: https://eev.stoplight.io/docs/eev/122963476e10f-getting-started - Integrations: https://www.easyemailverification.com/en-US/integrations ## API reference ### Endpoints #### GET /verify Verify a single email address. Verifies one email address. Each verification consumes one credit, except when the receiving server could not be reached (`reason: no_connect`). `success: true` means the request was processed; it does not mean the address is deliverable. Use `result`, `reason` and `safe_to_send`. Do not treat `unknown` as `valid`. **API key:** `X-API-Key` header, `apikey` query, `Authorization` header. **Parameters** | Name | In | Required | Description | | --- | --- | --- | --- | | `email` | query | yes | Email address to verify. URL-encode it (for example `+` as `%2B`). | | `apikey` | query | no | API key. Optional when the key is sent in a header (`X-API-Key` recommended). | **Responses** | Status | Description | Body | | --- | --- | --- | | 200 | Verification result. | [VerificationResult](#verificationresult) `application/json` | | 400 | Missing API key (`message: "Apikey is null"`), empty `email`, or (sandbox key) an address outside the sandbox domain. When the `email` parameter is absent, the framework returns its default error body. | [VerificationError](#verificationerror) `application/json` | | 401 | Unknown API key, or a widget-only key. | [VerificationError](#verificationerror) `application/json` | | 402 | The account has no credits left. | [VerificationError](#verificationerror) `application/json` | | 429 | Sandbox key only (sandbox rate limit or the simulated `ratelimit@` address). | [VerificationError](#verificationerror) `application/json` | Example `200` response: ```json { "email": "john@gmail.com", "result": "invalid", "reason": "rejected_email", "disposable": false, "accept_all": false, "role": false, "free": true, "user": "john", "domain": "gmail.com", "mx_record": "gmail-smtp-in.l.google.com", "mx_domain": "google.com", "safe_to_send": false, "did_you_mean": "", "success": true, "message": "Processing time: 812 ms", "http_code": "200" } ``` #### POST /verify Verify a small batch of email addresses. Verifies up to 50 email addresses in one request and returns one result per address. Each address consumes one credit, except `reason: no_connect`. For larger lists use the bulk endpoints. **API key:** `X-API-Key` header, `apikey` header, `Authorization` header. **Parameters** | Name | In | Required | Description | | --- | --- | --- | --- | | `apikey` | header | no | API key. Optional when the key is sent in `X-API-Key` (recommended) or `Authorization`. | **Request body** (`application/json`): [BatchRequest](#batchrequest) ```json { "emails": [ "john@gmail.com", "mary@example.com" ] } ``` **Responses** | Status | Description | Body | | --- | --- | --- | | 200 | One result per address, in the same shape as `GET /verify`. | array of [VerificationResult](#verificationresult) `application/json` | | 400 | No API key, or (sandbox key) an address outside the sandbox domain. | [VerificationErrorList](#verificationerrorlist) `application/json` | | 401 | Unknown API key, or a widget-only key. | [VerificationErrorList](#verificationerrorlist) `application/json` | | 402 | The account has no credits left. | [VerificationErrorList](#verificationerrorlist) `application/json` | | 429 | Too many addresses in one request. | [VerificationErrorList](#verificationerrorlist) `application/json` | #### POST /bulk/upload Upload a list for bulk verification. Uploads a TXT or CSV file with one email address per line and starts an asynchronous verification job. Lines that contain `@` and are longer than 5 characters are counted. The account needs enough credits for every counted line. Maximum file size: 16 MB. Poll `GET /bulk/status/{id}` and download the result with `GET /bulk/download/{id}`. **API key:** `X-API-Key` header, `apikey` header, `Authorization` header. **Parameters** | Name | In | Required | Description | | --- | --- | --- | --- | | `apikey` | header | no | API key. Optional when the key is sent in `X-API-Key` (recommended) or `Authorization`. | **Request body** (`multipart/form-data`): object - `file`: TXT or CSV file, one email address per line. **Responses** | Status | Description | Body | | --- | --- | --- | | 200 | File accepted and job created. | [BulkUploadAccepted](#bulkuploadaccepted) `application/json` | | 400 | Missing API key, or the file has no valid email addresses. | [BulkError](#bulkerror) `application/json` | | 401 | Unknown API key, or a widget-only key. | [BulkError](#bulkerror) `application/json` | | 402 | Not enough credits for the number of addresses in the file. | [BulkError](#bulkerror) `application/json` | | 500 | The file could not be read. | [BulkError](#bulkerror) `application/json` | #### GET /bulk/status List the bulk jobs of the account. **API key:** `X-API-Key` header, `apikey` header, `Authorization` header. **Parameters** | Name | In | Required | Description | | --- | --- | --- | --- | | `apikey` | header | no | API key. Optional when the key is sent in `X-API-Key` (recommended) or `Authorization`. | **Responses** | Status | Description | Body | | --- | --- | --- | | 200 | Jobs of the account that owns the API key. | [BulkJobList](#bulkjoblist) `application/json` | | 400 | No API key, or the sandbox key (not supported on bulk endpoints). | [BulkError](#bulkerror) `application/json` | | 401 | Unknown API key, or a widget-only key. | [BulkError](#bulkerror) `application/json` | #### GET /bulk/status/{id} Get the status of a bulk job. **API key:** `X-API-Key` header, `apikey` header, `Authorization` header. **Parameters** | Name | In | Required | Description | | --- | --- | --- | --- | | `id` | path | yes | Job id (`list_id`) returned by `POST /bulk/upload`. | | `apikey` | header | no | API key. Optional when the key is sent in `X-API-Key` (recommended) or `Authorization`. | **Responses** | Status | Description | Body | | --- | --- | --- | | 200 | Job status. | [BulkJobStatus](#bulkjobstatus) `application/json` | | 400 | No API key, or the sandbox key (not supported on bulk endpoints). | [BulkError](#bulkerror) `application/json` | | 401 | Unknown API key, or a widget-only key. | [BulkError](#bulkerror) `application/json` | | 404 | Job not found, or it belongs to another account. | [BulkError](#bulkerror) `application/json` | #### GET /bulk/download/{id} Download the results of a completed bulk job. Returns the results as CSV with the header `Email,Result,Reason,Disposable,AcceptAll,Role,Free,User,Domain,MXRecord,DomainMX,IsSafeToSend,DidYouMean,Success,Message,HttpCode`. Jobs and their results are deleted automatically 30 days after upload. **API key:** `X-API-Key` header, `apikey` header, `Authorization` header. **Parameters** | Name | In | Required | Description | | --- | --- | --- | --- | | `id` | path | yes | Job id (`list_id`) returned by `POST /bulk/upload`. | | `apikey` | header | no | API key. Optional when the key is sent in `X-API-Key` (recommended) or `Authorization`. | **Responses** | Status | Description | Body | | --- | --- | --- | | 200 | CSV file (`Content-Disposition: attachment; filename=results.csv`). | string `text/plain` | | 400 | No API key, or the sandbox key (not supported on bulk endpoints). | [BulkError](#bulkerror) `application/json` | | 401 | Unknown API key, or a widget-only key. | [BulkError](#bulkerror) `application/json` | | 404 | Job not found, not completed yet, or it belongs to another account. | [BulkError](#bulkerror) `application/json` | #### DELETE /bulk/{id} Delete a bulk job and its results. **API key:** `X-API-Key` header, `apikey` header, `Authorization` header. **Parameters** | Name | In | Required | Description | | --- | --- | --- | --- | | `id` | path | yes | Job id (`list_id`) returned by `POST /bulk/upload`. | | `apikey` | header | no | API key. Optional when the key is sent in `X-API-Key` (recommended) or `Authorization`. | **Responses** | Status | Description | Body | | --- | --- | --- | | 200 | Job deleted. | [BulkDeleted](#bulkdeleted) `application/json` | | 400 | No API key, or the sandbox key (not supported on bulk endpoints). | [BulkError](#bulkerror) `application/json` | | 401 | Unknown API key, or a widget-only key. | [BulkError](#bulkerror) `application/json` | | 404 | Job not found, or it belongs to another account. | [BulkError](#bulkerror) `application/json` | #### GET /credits Remaining credits of the account. Does not consume credits. With the sandbox key it returns fixed values. **API key:** `X-API-Key` header, `apikey` query, `Authorization` header. **Parameters** | Name | In | Required | Description | | --- | --- | --- | --- | | `apikey` | query | no | API key. Optional when the key is sent in a header (`X-API-Key` recommended). | **Responses** | Status | Description | Body | | --- | --- | --- | | 200 | Remaining credits. | [Credits](#credits) `application/json` | | 400 | No API key. | [SimpleError](#simpleerror) `application/json` | | 401 | Unknown API key, or a widget-only key. | [SimpleError](#simpleerror) `application/json` | Example `200` response: ```json { "success": true, "credits_remaining": 1250, "subscription_remaining": 250, "persistent_remaining": 1000, "subscription_credits": 500 } ``` ### Schemas #### VerificationResult Result of one verification. All fields are always present. | Field | Type | Required | Description | | --- | --- | --- | --- | | `email` | string, nullable | yes | Address that was verified, normalized to lowercase. | | `result` | string, nullable | yes | Overall result. Check `reason` for details. One of: `valid`, `invalid`, `unknown`. | | `reason` | string, nullable | yes | Detail of the result. Known values: `accepted_email` (SMTP server accepted the address), `rejected_email` (mailbox does not exist), `invalid_email` (invalid syntax), `invalid_domain` (domain does not exist), `no_mx_record` (domain has no MX record), `invalid_mx_record`, `no_connect` (could not connect to the receiving server; no credit is consumed), `timeout`, `unavailable_smtp`, `unexpected_error`, `temporarily_blocked` (greylisted), `exceeded_storage` (mailbox full). New values may be added; treat unknown values as inconclusive. | | `disposable` | boolean | yes | Known temporary/disposable address. | | `accept_all` | boolean | yes | Catch-all domain; it accepts any mailbox, so the address cannot be confirmed. | | `role` | boolean | yes | Role address such as support@ or postmaster@. | | `free` | boolean | yes | Address from a popular free provider (Gmail, Outlook, Yahoo...). | | `user` | string, nullable | yes | Local part of the address. | | `domain` | string, nullable | yes | Domain part of the address. | | `mx_record` | string, nullable | yes | MX host that receives mail for the domain. | | `mx_domain` | string, nullable | yes | Domain of the MX host. | | `safe_to_send` | boolean | yes | Whether the address is considered safe for sending. | | `did_you_mean` | string | yes | Suggested correction when a typo is detected; empty string otherwise. | | `success` | boolean | yes | Whether the request was processed. It does not mean the address is deliverable. | | `message` | string, nullable | yes | Error description, or processing information on success. | | `http_code` | string, nullable | yes | HTTP status code repeated in the body, as a string. | #### VerificationError Error response of `GET /verify`. Same shape as a result, with `success: false`. Same fields as [VerificationResult](#verificationresult). #### VerificationErrorList Error response of `POST /verify`: a list with one error item. Array of [VerificationResult](#verificationresult). #### BatchRequest | Field | Type | Required | Description | | --- | --- | --- | --- | | `emails` | array of string | yes | Addresses to verify (maximum 50). | #### Credits | Field | Type | Required | Description | | --- | --- | --- | --- | | `success` | boolean | yes | | | `credits_remaining` | integer | yes | Total verifications left (subscription + prepaid). | | `subscription_remaining` | integer | yes | Left in the current subscription period. | | `persistent_remaining` | integer | yes | Prepaid credits that do not expire with the period. | | `subscription_credits` | integer | yes | Credits granted per subscription period. | #### SimpleError | Field | Type | Required | Description | | --- | --- | --- | --- | | `success` | boolean | yes | One of: `false`. | | `message` | string | yes | | | `http_code` | string | yes | | #### BulkError | Field | Type | Required | Description | | --- | --- | --- | --- | | `status` | string | yes | One of: `error`. | | `message` | string | yes | | #### BulkUploadAccepted | Field | Type | Required | Description | | --- | --- | --- | --- | | `status` | string | yes | One of: `accepted`. | | `list_id` | string | yes | Job id used by the other bulk endpoints. | | `filename` | string | yes | | | `uploaded` | integer | yes | Number of addresses counted in the file. | | `message` | string | yes | | #### BulkJobStatusValue `not_enough_credits`: processing stopped because the account ran out of credits. Type: string. One of: `queued`, `processing`, `completed`, `failed`, `deleted`, `not_enough_credits`, `unknown`. #### BulkJobStatus | Field | Type | Required | Description | | --- | --- | --- | --- | | `list_id` | string | yes | | | `status` | [BulkJobStatusValue](#bulkjobstatusvalue) | yes | | | `progress` | integer | yes | Percentage processed. | | `started_at` | string (date-time), nullable | no | Upload time. | | `estimated_finish` | string (date-time), nullable | no | Completion time once the job is completed; null before that. | | `totals` | object, nullable | no | Counters; null before processing starts. | #### BulkJobList | Field | Type | Required | Description | | --- | --- | --- | --- | | `status` | string | yes | One of: `ok`. | | `total_lists` | integer | yes | | | `lists` | array of object | yes | | #### BulkDeleted | Field | Type | Required | Description | | --- | --- | --- | --- | | `status` | string | yes | One of: `deleted`. | | `list_id` | string | yes | | | `message` | string | yes | |