JavaScript SDK

sortance.js

One file, no dependencies. Copy it into your project and call isValidEmail. It runs on Node 18 or later, Bun, Deno and Cloudflare Workers. It is for your server only: it needs your secret API key, which must never reach a browser.

Install

curl -o sortance.js https://sortance.com/sdk/sortance.js
curl -o sortance.d.ts https://sortance.com/sdk/sortance.d.ts   # optional, for TypeScript

export SORTANCE_API_KEY=sk_live_...

Create the key on the API keys screen (Settings) of the dashboard. The SDK reads SORTANCE_API_KEY by default.

Use

const sortance = require('./sortance');

if (!(await sortance.isValidEmail(email))) {
  return res.status(400).json({ error: 'Use an address that can receive mail.' });
}

const provider = await sortance.getEmailProvider(email);   // 'gmail', 'outlook', … or null

// Work addresses only (no Gmail, Outlook, Yahoo, Apple or other free providers)
if (!(await sortance.isBusinessEmail(email))) {
  return res.status(400).json({ error: 'Use your work email address.' });
}

// Block a competitor's domain for your account only
await sortance.blockDomains(['competitor.example'], 'no trials');

In an ES module project ("type": "module"), save the file as sortance.cjs instead and import it: import { isValidEmail } from './sortance.cjs'. Deno and Workers bundlers take the same .cjs file. For a second key or different settings, create a client:

const client = sortance.createClient({
  apiKey: process.env.OTHER_KEY,
  timeoutMs: 3000,          // default 5000
  failOpen: true,           // default; see below
  onError: (err, helper) => console.warn(helper, err.code),
});

Drop-in functions

These keep the names and return values of a typical email-utils.js, so replacing one is a change to the import line. They never throw on a Sortance outage: a validation service that is down must not block your sign-ups. That includes a wrong or revoked key, so set onError to log what was swallowed, or pass failOpen: false to make them throw instead.

FunctionReturnsCallsIf Sortance cannot be reached
isValidEmail(email)booleanPOST /v1/checkLocal syntax check (true when well formed)
isDisposableEmail(email)booleanPOST /v1/domains/checkfalse
isDomainDisposable(domain)booleanPOST /v1/domains/checkfalse
getEmailProvider(email)'gmail' | 'outlook' | … | 'unknown' | nullPOST /v1/checknull
isBusinessEmail(email)booleanPOST /v1/checktrue (an outage must not turn away a work address)
emailType(email)'business' | 'free' | 'role' | 'disposable' | 'relay' | 'unknown'POST /v1/check'unknown'
isValidContactListEmail(email)booleannone, runs locallynot applicable

isValidEmail is true when the syntax is valid, the domain has mail servers, and the domain is neither disposable nor on your domain or address blocklist. It does not open an SMTP connection; for a bounce probability, use verifyEmail. isDisposableEmail is true for Sortance’s disposable list and for your own blocklist, and false for anything on your allowlist. isValidContactListEmail is a strict local check for list imports: it also rejects +tags, all-digit names and common provider typos such as gmial.com.

emailType returns the address’s email_type: business (Google Workspace, Microsoft 365 or the company’s own mail server), free (Gmail, Outlook, Hotmail, Yahoo, Apple iCloud and other consumer providers, sometimes called social email), role (a shared mailbox such as info@ or sales@), disposable, relay (forwarding services such as Apple Hide My Email) or unknown (no mail servers, or nothing matched). When several apply, the first match in this order wins: disposable, relay, role, free, business. So info@gmail.com is role. isBusinessEmail is true only for business. Broken syntax answers locally, with no call: false and 'unknown'.

Full answers

These return the API’s JSON and throw a SortanceError (with status, code and traceId) when a call fails.

FunctionCallsWhat it is for
checkEmail(email)POST /v1/checkSyntax, MX, disposable, your domain and address rules and email_type, with reason codes. Free; rate-limited.
verifyEmail(email, context?)POST /v1/verifyBounce probability and recommendation. Counts against your monthly quota.
screenEmail(email, name?)POST /v1/screenTypo, placeholder and fake-name screening. Counts against the screening quota.
checkDomain(domain)POST /v1/domains/checkWhat happens to one domain on your account: blocked, allowed, disposable, suspected or none.
blockDomains(domains, note?)POST /v1/domains/blocklistAdd to your blocklist. Batches of 1,000 are sent for you.
unblockDomains(domains)DELETE /v1/domains/blocklistRemove from your blocklist.
allowDomains(domains, note?)POST /v1/domains/allowlistAdd to your allowlist.
unallowDomains(domains)DELETE /v1/domains/allowlistRemove from your allowlist.
listBlockedDomains()GET /v1/domains/blocklistYour blocklist.
listAllowedDomains()GET /v1/domains/allowlistYour allowlist.
checkAddress(email)POST /v1/addresses/checkWhat happens to one address on your account: its rule, your domain rule and the effective outcome.
blockAddresses(emails, note?)POST /v1/addresses/blocklistBlock single addresses. Batches of 1,000 are sent for you. Stored as a keyed hash and a masked form.
unblockAddresses(emails)DELETE /v1/addresses/blocklistRemove addresses from your blocklist.
allowAddresses(emails, note?)POST /v1/addresses/allowlistAllow single addresses past the disposable lists and your domain blocks.
unallowAddresses(emails)DELETE /v1/addresses/allowlistRemove addresses from your allowlist.
listBlockedAddresses(page?)GET /v1/addresses/blocklistYour blocked addresses, masked (j•••@acme.com), newest first. Pass { limit, cursor } for the next page.
listAllowedAddresses(page?)GET /v1/addresses/allowlistYour allowed addresses, masked, paged the same way.
verifyList(csv, { name, policy, allowPartial }?)POST /v1/bulk/jobs, PUT …/upload, POST …/startBulk verification: uploads a CSV (string or Buffer, up to 50 MB and 100,000 rows) and starts it. Returns { jobId, job }. Each unique address counts as one verification.
waitForList(jobId, { pollMs, timeoutMs }?)GET /v1/bulk/jobs/:idPolls (every 5 s by default) until the job is done, failed, cancelled or paused on quota. Returns the job.
downloadList(jobId, segment?)GET /v1/bulk/jobs/:id/results.csvOne results file as a string: all (default), deliverable, undeliverable, review, business, or allow / deny with a policy.

The three list functions never fail open: a bulk job has no safe default, so an outage, a missing email column (no_email_column) or a list that does not fit this month’s quota (402 quota_exceeded, unless allowPartial) throws.

const { jobId } = await sortance.verifyList(fs.readFileSync('leads.csv'), { name: 'leads.csv' })
const job = await sortance.waitForList(jobId)
if (job.state === 'done') {
  fs.writeFileSync('leads.deliverable.csv', await sortance.downloadList(jobId, 'deliverable'))
}

Reason codes are listed in the email check reference, and blocklist behaviour in Blocklists.

What the SDK sends

The address always travels in a POST body, never in a URL, so it does not end up in access logs. Sortance does not store raw addresses, with one exception you opt into: a CSV sent with verifyList and its results are kept as files for 7 days, then deleted (see trust).