API Documentation

Authenticate with an nxv_ API key, verify single or bulk emails, poll jobs, export results, and receive signed completion webhooks. Integrate in about 10 minutes with the quickstart below.

Download OpenAPIOpenAPI 3 spec at https://api.nexiphorverifier.com/openapi/v1.json

Quickstart

Create a sandbox key (nxv_test_) in Dashboard → API Keys, then run one of these — no credits charged, deterministic fake results:

# 1) Create a sandbox key in Dashboard → API Keys (prefix nxv_test_)
# 2) Verify one address (waits up to 15s for a result)
curl -X POST "https://api.nexiphorverifier.com/api/v1/verify/single?wait=true&waitTimeoutMs=15000" \
  -H "Authorization: Bearer nxv_test_YOUR_SANDBOX_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com"}'

Response (synchronous wait): 200 when the job finishes in time. Job status is Completed; the verdict lives on result.status (Valid / Invalid / Risky / Unknown). Use result.score, result.reasonCode, and result.verificationMethod (sandbox here; live SMTP uses values like smtp_positive). On live keys, Unknown / Failed are refunded.

{
  "jobId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "status": "Completed",
  "email": "user@example.com",
  "result": {
    "email": "user@example.com",
    "isValid": true,
    "status": "Valid",
    "score": 100,
    "reason": "Mailbox exists",
    "reasonCode": "mailbox_exists",
    "verificationMethod": "sandbox",
    "confidenceLevel": "VeryHigh"
  }
}

Swap to a live nxv_ key when you are ready for real SMTP probes. Prefer an official client? See SDKs below.

Authentication

Create a key in Dashboard → API Keys. Send it as a Bearer token (preferred) or X-Api-Key header. Keys use scopes verify:single, verify:bulk, finder:single, and finder:bulk. There is no rotate endpoint — revoke a key and create a new one (optionally with the same scopes and IP allowlist).

Authorization: Bearer nxv_your_key_here
# or
X-Api-Key: nxv_your_key_here

Sandbox keys

Create a sandbox key (prefix nxv_test_) to integrate without burning credits or hitting SMTP. Results are deterministic fakes (verificationMethod: "sandbox"). Bulk sandbox is capped at 100 emails. Sandbox does not run real syntax/IDN short-circuits — addresses are hashed into Valid / Invalid / Risky / Unknown buckets. Use a live key to exercise invalid_syntax and Unicode local-part rejection.

Authorization: Bearer nxv_test_...

Single verify

POST https://api.nexiphorverifier.com/api/v1/verify/single returns 202 Accepted with a job id by default. Poll until complete, or opt into a synchronous wait.

curl -X POST https://api.nexiphorverifier.com/api/v1/verify/single \
  -H "Authorization: Bearer nxv_..." \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com"}'

# Poll
curl https://api.nexiphorverifier.com/api/v1/verify/single/{jobId} \
  -H "Authorization: Bearer nxv_..."

Accepted response shape:

{
  "jobId": "uuid",
  "status": "Queued",
  "email": "user@example.com",
  "pollUrl": "/api/v1/verify/single/{jobId}",
  "hubUrl": "/hubs/verification"
}

Synchronous wait (optional): add ?wait=true or header X-Nexiphor-Wait: true. Optional waitTimeoutMs (5 000–30 000, default 20 000). Returns 200 with the completed job payload if finished in time; otherwise 202 and you must poll. Bulk jobs stay async only.

curl -X POST "https://api.nexiphorverifier.com/api/v1/verify/single?wait=true&waitTimeoutMs=15000" \
  -H "Authorization: Bearer nxv_..." \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com"}'

Result fields

Completed single-job and bulk export rows share the same verification shape. status is one of Valid, Invalid, Risky, or Unknown. score is a confidence/deliverability score (higher is better for Valid / safe catch-all). Unknown and Failed verifications are refunded. The example below is a live SMTP outcome (verificationMethod: "smtp_positive"); sandbox keys return "sandbox" instead.

{
  "email": "user@example.com",
  "isValid": true,
  "status": "Valid",
  "score": 100,
  "reason": "Mailbox exists",
  "reasonCode": "mailbox_exists",
  "suggestedEmail": null,
  "typoDetected": false,
  "provider": null,
  "providerName": null,
  "verificationMethod": "smtp_positive",
  "confidenceLevel": "VeryHigh",
  "checks": {
    "syntaxValid": true,
    "domainExists": true,
    "mxFound": true,
    "isDisposable": false,
    "smtpAccepted": true,
    "isCatchAll": false,
    "isRoleAccount": false,
    "isFreeEmail": true,
    "isToxicDomain": false,
    "isPossibleSpamTrap": false,
    "catchAllProvider": null
  },
  "telemetry": {
    "mxHost": "gmail-smtp-in.l.google.com",
    "smtpCode": 250,
    "smtpResponse": "2.1.5 OK",
    "tlsUsed": true
  }
}

Common reasonCode values:

  • invalid_syntax / invalid_domain / disposable_domain → Invalid
  • mx_not_found / smtp_rejected / provider_account_not_found → Invalid
  • mailbox_exists → Valid
  • catch_all_detected / provider_non_authoritative_rcpt → Risky
  • smtp_blocked / smtp_timeout / dns_timeout → Unknown (refunded)
  • toxic_domain / possible_spam_trap → Risky (also exported as is_toxic_domain / is_possible_spam_trap)

Bulk CSV/TSV/XLSX/JSON exports include these columns plus is_toxic_domain and is_possible_spam_trap. Prefer the valid_and_safe_catchall export preset when you want Valid plus high-confidence catch-alls (score ≥ 70 by default — reputable managed hosts such as Google Workspace / Microsoft 365). Catch-all confidence is provider-graded (about 50–75) and may be reduced when a domain historically flip-flops.

Internationalized domains (IDN) and locale

Unicode domain labels are supported. Before DNS and SMTP, addresses are normalized with Punycode (ACE). For example, test@日本語.jp is converted to an ASCII domain and then verified like any other address. Malformed Punycode labels fail closed as invalid_syntax.

SMTPUTF8 / Unicode local parts are not supported on live keys. Addresses such as ユーザー@example.com return invalid_syntax even when the domain is valid. Probes use ASCII SMTP; this keeps bulk lists aligned with deliverability on mainstream MX hosts. Sandbox keys skip this check (see Sandbox keys above).

Bare IP hostnames and reserved domains are rejected as invalid_domain. Dashboard UI text is English; date and number formatting may follow the browser locale.

Bulk verify

JSON list (requires fileName plus emails), paste text via /verify/bulk/paste, or CSV upload. Optional callbackUrl receives a completion webhook.

Concurrent bulk jobs: each plan includes soft parallel bulk limits (Business 4, Agency 6, Enterprise custom). Starting another bulk job while at the limit queues or waits — contact sales@nexiphorverifier.com for Business+ concurrency packs.

# JSON emails (fileName is required)
curl -X POST https://api.nexiphorverifier.com/api/v1/verify/bulk \
  -H "Authorization: Bearer nxv_..." \
  -H "Content-Type: application/json" \
  -d '{"fileName":"list.json","emails":["a@x.com","b@y.com"],"callbackUrl":"https://hooks.zapier.com/..."}'

# Paste text
curl -X POST https://api.nexiphorverifier.com/api/v1/verify/bulk/paste \
  -H "Authorization: Bearer nxv_..." \
  -H "Content-Type: application/json" \
  -d '{"text":"a@x.com\nb@y.com"}'

# CSV upload
curl -X POST https://api.nexiphorverifier.com/api/v1/verify/bulk/upload \
  -H "Authorization: Bearer nxv_..." \
  -F "file=@list.csv" \
  -F "callbackUrl=https://hooks.zapier.com/..."

# Poll summary
curl https://api.nexiphorverifier.com/api/v1/verify/bulk/{batchId}/summary \
  -H "Authorization: Bearer nxv_..."

Export

Authenticated download: GET https://api.nexiphorverifier.com/api/v1/verify/bulk/{batchId}/export

Webhooks include an absolute exportUrl and a short-lived exportToken. Send that token in the X-Export-Token header (query-string ?token= is rejected). Lifetime is up to 24h, or until zero-storage purge. No API key is required for that URL. The token is cleared when the batch is purged.

Webhooks

Prefer the Webhook Manager: register durable public http or https endpoints (ports 80/443 only; no localhost or private IPs) in Dashboard → Webhooks or use the Zapier / Make recipes for Catch Hook setup. Subscribe to batch.completed and verification.completed, inspect delivery history, and retry failures. Managed webhook create / rotate / test require a Pro plan (otherwise 403 with code: "plan_required"). Per-batch callbackUrl remains a one-shot override. Failed deliveries retry with backoff (1m, 5m, 15m, 1h; up to 4 attempts).

curl -X POST https://api.nexiphorverifier.com/api/v1/webhooks/endpoints \
  -H "Authorization: Bearer nxv_..." \
  -H "Content-Type: application/json" \
  -d '{"name":"Zapier","url":"https://hooks.zapier.com/...","events":["batch.completed"]}'

Generate a signing secret on the Webhooks page or in Settings → Privacy. Verify X-Nexiphor-Signature: sha256=... as HMAC-SHA256 of the raw body. Deduplicate with eventId / X-Nexiphor-Event-Id.

{
  "eventId": "batch.completed:aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "batchId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "status": "Completed",
  "totalCount": 1000,
  "completedCount": 1000,
  "results": { "valid": 820, "invalid": 100, "risky": 50, "unknown": 30 },
  "exportUrl": "https://api.nexiphorverifier.com/api/v1/verify/bulk/.../export",
  "exportToken": "<opaque>",
  "exportTokenHeader": "X-Export-Token",
  "exportExpiresAt": "2026-07-11T12:00:00Z",
  "completedAt": "2026-07-10T12:00:00Z"
}

Email Finder

Resolve a professional address from name + domain. Only SMTP-proven Valid results are returned — catch-all domains abort without a guess. Success costs 5 credits; misses cost 0. Requires API scopes finder:single / finder:bulk.

curl -X POST https://api.nexiphorverifier.com/api/v1/finder/single \
  -H "Authorization: Bearer nxv_..." \
  -H "Content-Type: application/json" \
  -d '{"firstName":"Ada","lastName":"Lovelace","domain":"example.com"}'

List recent single jobs: GET /api/v1/finder/single. Pro plan required. Webhook events include job.failed as an alias of verification.failed. After rotating a webhook signing secret,X-Nexiphor-Signature-Previous is sent for 24h.

Bulk CSV upload:

curl -X POST https://api.nexiphorverifier.com/api/v1/finder/bulk/upload \
  -H "Authorization: Bearer nxv_..." \
  -F "file=@people.csv"

Domain Intelligence

GET /api/v1/domains/lookup?domain=example.com (authenticated). Returns MX presence, provider hints, and a heuristic catch-all likelihood. This is not an SMTP RCPT probe of a specific mailbox — use single/bulk verify for mailbox-level truth.

curl "https://api.nexiphorverifier.com/api/v1/domains/lookup?domain=example.com" \
  -H "Authorization: Bearer nxv_..."

Result cache

Teams can enable a result cache TTL (days) under Settings. Cache hits return verificationMethod: "result_cache" and may include fromCache: true on the enqueue response. Pass forceRefresh: true on single/bulk verify to bypass cache. Owners/Admins can purge with POST /api/v1/teams/result-cache/purge.

Verification profiles

Apply a named profile (export/unknown defaults) with POST /api/v1/teams/verification-profiles/apply body {"profileId":"cold_email"}. List profiles via GET /api/v1/teams/verification-profiles.

Rate limits

Verify and finder endpoints are rate limited per API key (default ~120 requests per minute). Each key has its own bucket; JWT dashboard sessions share a per-user bucket. Exceeding the limit returns 429 with Retry-After and X-RateLimit-* headers. Opt-in synchronous single-verify waits use a tighter limit (~30/min). Polling job status uses a separate, higher quota.

SDKs

Official thin clients for Node and Python wrap single verify, bulk, status, and export.

Node @nexiphor/verifier on npm

npm i @nexiphor/verifier

import { createClient } from '@nexiphor/verifier';

const client = createClient({ apiKey: process.env.NXV_API_KEY });
const job = await client.verifyEmail('user@example.com', { wait: true });
console.log(job.status, job.result);

Python nexiphor-verifier on PyPI

pip install nexiphor-verifier

from nexiphor_verifier import create_client

client = create_client(api_key="nxv_test_...")
job = client.verify_email("user@example.com", wait=True)
print(job.status, job.result)

For codegen against the full surface, download the OpenAPI spec.

Errors

JSON error bodies typically include a message / Message (and sometimes a machine code). Request validation uses 400 with a failures / Failures map — not HTTP 422.

  • 400 — request validation failed (empty email, missing bulk fileName, private webhook URL, etc.). Malformed addresses such as not-an-email are still accepted as jobs; on live keys they complete as Invalid / invalid_syntax rather than failing at the HTTP layer
  • 401 — missing/invalid session or API key; expired export token
  • 403 — missing API key scope, or plan gate (requiredPlan for webhooks / integrations)
  • 402 — insufficient credits (live keys only)
  • 429 — rate limited (~120/min per API key by default). Check Retry-After and X-RateLimit-* headers.

Example validation error (empty email):

{
  "Message": "One or more validation failures occurred.",
  "Failures": {
    "Email": ["Email is required."]
  }
}

401 — invalid or missing API key:

{
  "message": "Invalid API key."
}

402 — insufficient credits (live keys):

{
  "code": "insufficient_credits",
  "message": "Insufficient credits to start this verification."
}

429 — rate limited (also set Retry-After):

{
  "message": "Too many requests. Please try again later."
}

Error handling

Check HTTP status before parsing the success body. Read message / Message, optional code, and validation Failures. On 429, sleep for Retry-After seconds (fallback 1s) and retry.

async function verifyOnce(email, apiKey) {
  const res = await fetch(
    "https://api.nexiphorverifier.com/api/v1/verify/single?wait=true",
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${apiKey}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ email }),
    }
  );
  const body = await res.json().catch(() => ({}));
  if (res.status === 429) {
    const retryAfter = Number(res.headers.get("Retry-After") || 1);
    await new Promise((r) => setTimeout(r, retryAfter * 1000));
    return verifyOnce(email, apiKey);
  }
  if (!res.ok) {
    const msg = body.message ?? body.Message ?? res.statusText;
    const code = body.code ? ` (${body.code})` : "";
    throw new Error(`HTTP ${res.status}${code}: ${msg}`);
  }
  return body;
}

Zero-storage

Enable in Settings or send X-Nexiphor-Zero-Storage: true. Results are kept only for your retention window (shortened after a successful webhook). Download before purgeAfterUtc.

Chrome extension

A browser extension is packaged in the Nexiphor frontend repo for one-click verify from CRM tabs. Chrome Web Store listing is pending founder submit — use API keys or the dashboard until the Store link is live. See also Dashboard → API Keys.

Base URL

Production: https://api.nexiphorverifier.com