Express email verification
Verify emails in Express with Verifly
Add a live email verification step to any Express app with a route or middleware that proxies Verifly. It is an API-first, pay-as-you-go service — call it server-side with fetch, keep your key out of the browser, and reject disposable or dead mailboxes before you create the account.
import express from 'express'
const app = express()
app.use(express.json())
const KEY = process.env.VERIFLY_KEY // server-side only, never in the browser
async function verifyEmail(req, res, next) {
const { email } = req.body
const url = `https://verifly.email/api/v1/verify?email=${encodeURIComponent(email)}`
const r = await fetch(url, { headers: { Authorization: `Bearer ${KEY}` } })
const data = await r.json()
if (data.result === 'undeliverable' || data.disposable) {
return res.status(400).json({ error: 'Please use a valid, permanent email' })
}
next()
}
app.post('/register', verifyEmail, (req, res) => {
// create user here — email already screened
res.status(201).json({ ok: true })
})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 Express 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 in Express, keep the key on the server
Express is the layer where you own the request before it becomes a user, so it is the right place to screen an email. A regex or a library like validator.js only confirms user@dead-domain.com is well-formed; it cannot tell you the mailbox bounces. Verifly runs a live SMTP probe over one HTTPS call and returns a JSON verdict with a result field of deliverable, undeliverable, or risky plus flags for disposable, role, catch_all, and smtp.
The critical rule is that the check runs server-side. Your Verifly key is a Bearer secret read from process.env — it must never ship to the browser or appear in a client bundle. Model the check as Express middleware: a small verifyEmail function that fetches Verifly, and mount it in front of any route that accepts an address. If you have a frontend that wants a live check, expose your own thin endpoint that proxies Verifly, so the browser talks to your server and your server holds the key.
Pricing suits a Node service well — no seat, just credits from $2 per 1,000 checks down to $0.60 per 1,000 at volume. Self-register for 100 free credits with no card at the autonomous register endpoint, put the key in your environment, and you are calling in minutes.
A proxy endpoint for your frontend
When a React or plain-JS frontend wants to check an address as the user types, do not hand it the key. Instead, expose a minimal proxy route on your Express server. The browser calls /api/check-email, your server calls Verifly with the secret key, and only the safe verdict comes back. Fail open on errors so a Verifly hiccup never breaks your form, and consider a short debounce on the client to avoid spending credits on every keystroke.
app.get('/api/check-email', async (req, res) => {
const email = String(req.query.email || '')
try {
const url = `https://verifly.email/api/v1/verify?email=${encodeURIComponent(email)}`
const r = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.VERIFLY_KEY}` },
signal: AbortSignal.timeout(8000),
})
const data = await r.json()
// only return what the browser needs — never the key
res.json({ result: data.result, disposable: !!data.disposable })
} catch {
res.json({ result: 'unknown' }) // fail open: don't break the form
}
})Batch-verify a list from an admin route
For cleaning a list rather than a single signup — an admin import, a CSV upload, or rows from your own database — do not loop the single endpoint. POST them together to /api/v1/verify/batch, which takes a JSON body with an emails array and returns a results array in the same order. Map it back against your source rows and keep only the deliverable addresses.
app.post('/admin/clean', async (req, res) => {
const { emails } = req.body // e.g. ['a@x.com', 'b@y.com', ...]
const r = await fetch('https://verifly.email/api/v1/verify/batch', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VERIFLY_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ emails }),
})
const { results } = await r.json()
const kept = results
.filter((x) => x.result === 'deliverable' && !x.disposable && !x.role)
.map((x) => x.email)
res.json({ kept, total: results.length })
})FAQ
Frequently asked questions
Can I call Verifly directly from the browser in an Express app?
No. Your Verifly key is a Bearer secret. Call it from your Express server — either as middleware in front of a route or through a thin proxy endpoint — so the key stays in process.env and never reaches the client bundle.
How do I make verification a reusable middleware?
Write a small async function that reads req.body.email, fetches /api/v1/verify with the Bearer key, and either calls next() or responds 400. Mount it in the route chain before your handler, e.g. app.post('/register', verifyEmail, createUser).
My frontend needs a live check — how without leaking the key?
Expose a proxy route like GET /api/check-email that the browser calls. Your server forwards to Verifly with the secret key and returns only the verdict fields. Debounce on the client so you do not spend a credit per keystroke.
How do I verify a whole list of contacts?
POST them to /api/v1/verify/batch as a JSON emails array and read the results array back, filtering on the result field. Batching is faster and cheaper than looping the single endpoint in Node.
What fields come back from the API?
Each result has result (deliverable, undeliverable, or risky), is_valid, and boolean flags disposable, role, catch_all, and smtp. Branch on result and use the flags to strip throwaway or generic role addresses.
What does Express email verification cost?
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 when you self-register. There is no subscription, so a low-traffic register route costs only the credits it spends.