Email Verification API - JavaScript & Node.js Integration Guide 2026 | BounceZero
BlogDeveloper Guides

Email Verification API
JavaScript & Node.js Guide 2026

Production-ready code examples for integrating BounceZero email verification into JavaScript and Node.js applications - single address lookup, async batch with concurrency control, Express.js middleware, React signup handler, CSV streaming enrichment, and webhook processing.

By BounceZero Team |July 2026 |10 min read

Single Verification - Node.js (fetch)

Node.js 18+ includes native fetch. No dependency needed.

verify.js Node 18+
const API_KEY = process.env.BOUNCEZERO_API_KEY;
const BASE_URL = "https://api.bouncezero.io/v1";

async function verifyEmail(email) {
  const res = await fetch(`${BASE_URL}/verify`, {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ email }),
  });

  if (!res.ok) {
    throw new Error(`BounceZero API error: ${res.status}`);
  }

  return res.json();
}

// Usage
const result = await verifyEmail("[email protected]");
console.log(result);
// {
//   email: "[email protected]",
//   result: "valid",       // "valid" | "invalid" | "unknown" | "catch-all"
//   score: 0.97,           // 0.0-1.0 deliverability score
//   mx_valid: true,
//   is_disposable: false,
//   is_role_address: false,
//   provider: "Google Workspace"
// }

Single Verification - Browser (server-side proxy)

⚠ Never call BounceZero directly from browser JavaScript.

Your API key embedded in frontend code is extractable from browser DevTools. Always proxy through a backend endpoint you control.

Frontend (React/vanilla JS) > calls your backend
// Frontend: call YOUR backend endpoint, not BounceZero directly
async function validateEmailOnServer(email) {
  const res = await fetch("/api/validate-email", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ email }),
  });
  return res.json();
}

// Your backend (Express) proxies to BounceZero:
// POST /api/validate-email > BounceZero > return { valid, disposable }

Express.js Middleware - Validate at Signup

middleware/validateEmail.js Express 4+
const API_KEY = process.env.BOUNCEZERO_API_KEY;

async function validateEmail(req, res, next) {
  const { email } = req.body;

  if (!email) {
    return res.status(400).json({ error: "Email is required." });
  }

  try {
    const apiRes = await fetch("https://api.bouncezero.io/v1/verify", {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ email }),
    });

    const data = await apiRes.json();

    if (data.result === "invalid") {
      return res.status(422).json({ error: "That email address is not valid." });
    }
    if (data.is_disposable) {
      return res.status(422).json({ error: "Please use a work or personal email address." });
    }

    req.emailVerification = data; // attach for downstream use
    next();
  } catch (err) {
    // On API failure, fail open - do not block signup
    console.error("BounceZero error:", err.message);
    next();
  }
}

module.exports = { validateEmail };

// Usage in router:
// app.post("/signup", validateEmail, signupHandler);

Async Batch Verification - Concurrency-Controlled

Use p-limit to process arrays of emails in parallel without hammering rate limits.

batch-verify.js npm i p-limit
import pLimit from "p-limit";

const API_KEY = process.env.BOUNCEZERO_API_KEY;
const limit = pLimit(10); // max 10 concurrent requests

async function verifySingle(email) {
  const res = await fetch("https://api.bouncezero.io/v1/verify", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ email }),
  });
  return { email, ...(await res.json()) };
}

async function verifyBatch(emails) {
  const tasks = emails.map((email) => limit(() => verifySingle(email)));
  return Promise.all(tasks);
}

// Usage
const emails = ["[email protected]", "[email protected]", /* ... up to 10k */];
const results = await verifyBatch(emails);

const valid    = results.filter((r) => r.result === "valid");
const invalid  = results.filter((r) => r.result === "invalid");
const catchAll = results.filter((r) => r.result === "catch-all");

console.log(`Valid: ${valid.length}, Invalid: ${invalid.length}, Catch-all: ${catchAll.length}`);

React Signup Form - Real-time Validation

SignupForm.jsx React 18+
import { useState, useCallback } from "react";
import { useDebounce } from "use-debounce";

export function SignupForm() {
  const [email, setEmail]         = useState("");
  const [emailError, setEmailError] = useState(null);
  const [checking, setChecking]   = useState(false);
  const [debouncedEmail]          = useDebounce(email, 600);

  const checkEmail = useCallback(async (address) => {
    if (!address || !address.includes("@")) return;
    setChecking(true);
    try {
      // Call YOUR backend, not BounceZero directly
      const res  = await fetch("/api/validate-email", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ email: address }),
      });
      const data = await res.json();

      if (data.result === "invalid") {
        setEmailError("This email address doesn't look valid.");
      } else if (data.is_disposable) {
        setEmailError("Please use a work or personal email.");
      } else {
        setEmailError(null);
      }
    } catch {
      setEmailError(null); // fail open
    } finally {
      setChecking(false);
    }
  }, []);

  // Trigger on debounced change
  useState(() => { checkEmail(debouncedEmail); }, [debouncedEmail]);

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="[email protected]"
      />
      {checking && <span>Checking...</span>}
      {emailError && <span className="error">{emailError}</span>}
      <button type="submit" disabled={!!emailError || checking}>
        Create Account
      </button>
    </form>
  );
}

CSV Enrichment - Node.js Stream

Process large CSV files row-by-row without loading the full file into memory.

enrich-csv.js npm i csv-parse csv-stringify p-limit
import { createReadStream, createWriteStream } from "fs";
import { parse }     from "csv-parse";
import { stringify } from "csv-stringify";
import pLimit        from "p-limit";

const API_KEY = process.env.BOUNCEZERO_API_KEY;
const limit   = pLimit(10);

const parser    = createReadStream("input.csv").pipe(parse({ columns: true }));
const stringifier = stringify({ header: true });
stringifier.pipe(createWriteStream("output.csv"));

for await (const row of parser) {
  const result = await limit(() =>
    fetch("https://api.bouncezero.io/v1/verify", {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ email: row.email }),
    }).then((r) => r.json())
  );

  stringifier.write({
    ...row,
    bz_result:      result.result ?? "error",
    bz_score:       result.score ?? "",
    bz_disposable:  result.is_disposable ? "true" : "false",
    bz_mx_valid:    result.mx_valid ? "true" : "false",
  });
}

stringifier.end();
console.log("Done - results in output.csv");

API Response Reference

Field Type Values Use for
result string valid / invalid / unknown / catch-all Primary routing decision - suppress invalid
score number 0.0 - 1.0 Threshold-based filtering: score > 0.7 = safe to send
is_disposable boolean true / false Block throwaway addresses at signup
is_role_address boolean true / false Flag info@ / admin@ / sales@ for suppression in personal campaigns
mx_valid boolean true / false Domain has mail server - basic domain health check
provider string “Gmail” / “Outlook” / etc. Personalise outreach or route to provider-specific cadence

New to verification? Start with the complete email verification guide.

Frequently Asked Questions

How do I verify an email address with JavaScript?

Use the Fetch API or axios to POST to https://api.bouncezero.io/v1/verify with your API key in the Authorization header. In the browser, always call through a backend proxy - never embed your API key in client-side code where it’s extractable from DevTools.

How do I verify a list of emails in Node.js without rate limiting?

Use p-limit to cap concurrent requests: const limit = pLimit(10); then Promise.all(emails.map(e => limit(() => verifyEmail(e)))). This runs 10 in parallel at any time. For very large lists (50k+), use the BounceZero bulk upload endpoint instead of individual API calls.

Should I verify emails in the browser or on the server?

Always on the server. Your API key embedded in frontend JavaScript is extractable from browser DevTools. Set up a POST /api/validate-email endpoint in Express (or Next.js API routes) that proxies to BounceZero and returns only the fields your frontend needs.

Get your API key and start verifying in 2 minutes.

100 free credits on signup. No credit card. Instant access to the verification API. $3/1K thereafter - credits never expire.

AL

Written by

Ayoub Lebda

Founder, BounceZero - Email-infrastructure engineer

Ayoub built BounceZero's 5-stage validation pipeline, its dedicated BGP-announced IP infrastructure, and the Patroni HA PostgreSQL cluster behind every verification. Previously built high-volume email delivery infrastructure. Trained at 1337 Benguerir (École 42 network, 2019). Open-source: bgp_analyzer.

Email verification & deliverability explained

Deep-dive guides on how email verification and inbox placement work