JavaScript & TypeScript SDK

The official JavaScript and TypeScript SDK has no dependencies: it uses only the standard fetch, AbortSignal and Web Crypto APIs and ships its own TypeScript types. It is tested on Node.js 24 and needs Node.js 24 or later.

Install

npm install @email-validation/sdk

Validate an address

import { EmailValidation } from '@email-validation/sdk';

const ev = new EmailValidation({ apiKey: process.env.EV_API_KEY! });

const result = await ev.validate('accepted@sandbox.email-validation.org');
console.log(result.status, result.reason, result.action); // deliverable accepted send

Options: ipAddress, timeout (seconds the check may take, 5–15) and signal (an AbortSignal). The result type lists every field with its type; status, reason and action are string unions, so a switch over them is checked by the compiler.

Handle errors

import {
  EmailValidationError,
  InsufficientCreditsError,
  RateLimitError,
} from '@email-validation/sdk';

try {
  await ev.validate(address);
} catch (err) {
  if (err instanceof InsufficientCreditsError) {
    // buy credits; nothing was charged
  } else if (err instanceof RateLimitError) {
    console.log(`retry in ${err.retryAfterS}s`);
  } else if (err instanceof EmailValidationError) {
    console.error(err.code, err.status, err.requestId);
  } else {
    throw err;
  }
}

Every error extends EmailValidationError with status (0 when no answer arrived), code, requestId and retryAfterS. Subclasses: InvalidRequestError, AuthenticationError, InsufficientCreditsError, PermissionDeniedError, NotFoundError, ConflictError, RateLimitError, ServiceUnavailableError, ServerError and ConnectionError.

Retries and timeouts

const ev = new EmailValidation({
  apiKey: process.env.EV_API_KEY!,
  timeoutMs: 30_000, // per attempt
  maxRetries: 2, // after 429 / 503 answers
  maxRetryWaitMs: 10_000, // longer Retry-After values are returned to you instead of waited out
});

429 and 503 answers are retried with backoff, honouring Retry-After. A daily key cap is never retried, and a validation is never retried after a network failure, because it may already have been charged.

Credits, usage and status

const credits = await ev.credits(); // { balance, debt, pending, lots }
const usage = await ev.usage({ from: '2026-11-01', to: '2026-11-30', groupBy: 'day' });
const status = await ev.status();

Bulk jobs

import { readFile } from 'node:fs/promises';

// From a list (JSON, up to 100,000 addresses) …
let job = await ev.bulk.create({ emails: ['accepted@sandbox.email-validation.org', 'catch_all@sandbox.email-validation.org'] });

// … or from a CSV file (up to 100 MB)
job = await ev.bulk.create({ file: await readFile('customers.csv') }, { column: 'email', hasHeader: true });

job = await ev.bulk.wait(job.id, { onProgress: (j) => console.log(j.status, j.processed) });

if (job.status === 'completed') {
  const res = await ev.bulk.results(job.id, { filter: ['deliverable', 'risky'] });
  console.log(await res.text()); // or stream res.body for large files
} else if (job.status === 'ready') {
  // it could not start: job.error_code is e.g. 'insufficient_credits' (buy credits, then ev.bulk.start(job.id))
}

wait() returns when the job is completed, failed, cancelled or expired, or when it is still ready with an error_code: it could not start, for example insufficient_credits, and does not move on by itself. Pass timeoutMs to give up earlier.

Also: ev.bulk.get(id), ev.bulk.start(id) for jobs created with start: false (call it before wait()), and ev.bulk.cancel(id).

Verify webhooks

import { verifyWebhook, WebhookVerificationError } from '@email-validation/sdk';

// In any fetch-style handler (a Request in, a Response out):
export default {
  async fetch(request: Request, env: { EV_WEBHOOK_SECRET: string }) {
    try {
      const event = await verifyWebhook(await request.text(), request.headers, env.EV_WEBHOOK_SECRET);
      if (event.type === 'bulk.completed') console.log('job done', event.data.job_id);
      return new Response(null, { status: 204 });
    } catch (err) {
      if (err instanceof WebhookVerificationError) return new Response('invalid signature', { status: 400 });
      throw err;
    }
  },
};

verifyWebhook accepts the raw body as a string or bytes, and headers as a Headers object or a plain object such as Node's req.headers. event.type can also be webhook.test, sent when you press Send test in the dashboard: answer it with a 2xx like any other event. signWebhook creates a valid signature for local tests.

Constants

STATUSES, REASONS, REASONS_BY_STATUS, ACTIONS, ERROR_CODES, WEBHOOK_EVENTS and sandboxAddress(reason) mirror the API's vocabulary. See statuses and reasons and test addresses.

Updated · Maintained by the email-validation.org team