React email verification

Verify emails in React without leaking your key

Never call Verifly from the browser — your Bearer key would be exposed in the network tab. Instead, call your own server route that proxies Verifly, and let your React component read the clean verdict.

React calls YOUR route, not VeriflyAPI
// React (browser): hits your own backend, no key here
const res = await fetch('/api/verify-email?email=' + encodeURIComponent(email))
const { result } = await res.json() // deliverable | undeliverable | risky

Real-time SMTP mailbox checks

Single, batch, and async bulk verification

Disposable, role account, and catch-all detection

Pay-as-you-go credits with no subscription lock-in

Search fit

Built for React email verification

Use Verifly when you need a simple API, predictable pricing, and clean JSON results before emails hit your product, CRM, or campaign tool.

Verify a signup email from React without exposing your API key
Proxy Verifly through a Next.js Route Handler or Express route
Debounce an onBlur check and show a deliverable / undeliverable message
Keep the vf_ key server-side while React only sees the verdict

The one rule: never call Verifly from the browser

It is tempting to fetch https://verifly.email/api/v1/verify straight from a React component with your key in the Authorization header. Don't. Anything the browser sends is visible in the network tab and readable in your bundled JavaScript. A public API key is a public key — someone will copy it and burn your credits. Prefixing an env var with NEXT_PUBLIC_ or REACT_APP_ does not make it secret; it does the opposite, inlining it into the client bundle.

The correct pattern is a thin server-side proxy. Your React code calls a route on your own backend. That route holds the Verifly key in a server-only environment variable, calls Verifly, and returns only the verdict to the browser. The key never leaves your server, and the client only ever sees deliverable / undeliverable / risky plus the flags you choose to forward.

Verifly runs a live SMTP mailbox check and returns that verdict with disposable, role, and catch-all flags. Below are the two proxies you'll actually use: a Next.js Route Handler for App Router apps, and an Express route for a plain React SPA with a Node backend.

Next.js Route Handler proxy (App Router)

  1. Grab a free Verifly key at verifly.email/api/v1/autonomous/register — 100 credits, no card, no captcha.
  2. Put it in .env.local as VERIFLY_API_KEY — NO NEXT_PUBLIC_ prefix, so it stays server-only.
  3. Add a Route Handler at app/api/verify-email/route.ts that reads the key and calls Verifly.
  4. Return only the fields the UI needs; the key stays on the server.
  5. From React, fetch('/api/verify-email?email=...') — the browser never sees the key.
app/api/verify-email/route.ts (server-only)
import { NextResponse } from 'next/server'

export async function GET(req: Request) {
  const email = new URL(req.url).searchParams.get('email')
  if (!email) {
    return NextResponse.json({ error: 'email required' }, { status: 400 })
  }

  const upstream = await fetch(
    'https://verifly.email/api/v1/verify?email=' + encodeURIComponent(email),
    { headers: { Authorization: `Bearer ${process.env.VERIFLY_API_KEY}` } }
  )

  if (!upstream.ok) {
    return NextResponse.json({ result: 'unknown' }, { status: 200 })
  }

  const data = await upstream.json()
  // forward only what the browser needs — never the key
  return NextResponse.json({
    result: data.result,
    disposable: data.disposable,
    role: data.role,
    catch_all: data.catch_all,
  })
}

The React component (and an Express alternative)

On the client, verify on blur with a little debounce so you don't fire on every keystroke, and show the verdict inline. The component only ever talks to /api/verify-email — your own origin — so there is no key to leak. If your React app is a standalone SPA served by a Node backend instead of Next.js, the same proxy is a three-line Express route; the browser code is identical.

EmailField.tsx + optional Express route
// EmailField.tsx (client) — talks only to your own route
import { useState } from 'react'

export function EmailField() {
  const [status, setStatus] = useState<string | null>(null)

  async function check(email: string) {
    if (!email) return
    const res = await fetch('/api/verify-email?email=' + encodeURIComponent(email))
    const data = await res.json()
    setStatus(data.result)
  }

  return (
    <div>
      <input type="email" onBlur={(e) => check(e.target.value)} />
      {status === 'undeliverable' && <span>Please use a real email address.</span>}
    </div>
  )
}

// --- Express alternative for a plain React SPA ---
// app.get('/api/verify-email', async (req, res) => {
//   const r = await fetch(
//     'https://verifly.email/api/v1/verify?email=' +
//       encodeURIComponent(String(req.query.email)),
//     { headers: { Authorization: `Bearer ${process.env.VERIFLY_API_KEY}` } }
//   )
//   const d = await r.json()
//   res.json({ result: d.result, disposable: d.disposable })
// })

FAQ

Frequently asked questions

Why can't I just call Verifly directly from React?

Because the request runs in the browser, your Authorization header is visible in the network tab and your key is inlined into the shipped JavaScript bundle. Anyone can extract it and spend your credits. A public client can never hold a secret key — always proxy through your own server.

Doesn't NEXT_PUBLIC_ or REACT_APP_ make the key safe?

No — the opposite. Those prefixes exist to deliberately expose a value to the browser bundle. Your Verifly key must live in a plain server-only variable (VERIFLY_API_KEY with no public prefix) that only the Route Handler or Express route reads.

Where does verification actually run in this setup?

On your server. React calls your /api/verify-email route, that route adds the Bearer key and calls Verifly, and only the verdict comes back to the browser. The client sees deliverable / undeliverable / risky and any flags you forward, never the key.

Should I verify on every keystroke?

No — that wastes credits and hammers your proxy. Verify on blur or after a short debounce once the field looks complete. For a hard gate, also re-verify server-side at submit so a client that skipped the check still can't slip an undeliverable address through.

What if the proxy call fails?

Fail gracefully. The example returns result: 'unknown' on an upstream error so a verifier outage doesn't block your form; the UI can treat unknown as a soft pass. Keep a definitive server-side check at submit if a verified email is mandatory.

What does it cost to verify from a React app?

Pricing is pay-as-you-go from $2 per 1,000 checks down to $0.60 per 1,000 at volume, with 100 free credits to start and no monthly minimum, so you only pay for the addresses your proxy actually checks.