最もコストの低いバウンスは、リストに入る前に防げたバウンスです。 サインアップフォームの背後にあるサーバーレス関数で、アドレスが入力された瞬間に検証すれば、入力ミス、存在しないドメイン、使い捨てアドレスを入口で止められます。 あとからお金をかけてクリーニングし、その間の到達率低下を被る必要もありません。 このガイドでは、実際に動くサーバーレスのルートでこのパターンを紹介します。
結論#
サインアップフォームから呼び出すサーバー側の関数に検証を置きます。
この関数がAPIキーを保持し、POST /verifyを呼び出して、すぐに返るローカルの判定に応じて処理します。
undeliverableは拒否し、did_you_meanの修正候補を提示し、disposableにはフラグを立て、それ以外は通します。
成否を分けるルールは2つあります。
ブラウザからAPIを呼ばないこと(キーはサーバー側に置く必要があります)、そしてAPIが遅い場合や停止している場合でもサインアップをブロックしないこと(フェイルオープン)です。
後からではなくサインアップ時に行う理由#
取得時の検証は予防であり、リストのクリーニングは事後対応です。 どの観点でも、予防のほうが優れています。 入力の段階で見つかった入力ミスは、1行の修正で救える購読者です。 サインアップ時に拒否した存在しないドメインは、バウンス率を悪化させるハードバウンスになりません。 入口でブロックした使い捨てアドレスは、指標を薄めたり請求額を増やしたりしません。 3か月後のリストクリーニングで見つかる同じアドレスは、すでに1回の送信とバウンス、そして送信者レピュテーションのわずかな低下というコストを発生させています。
パターン#
構成要素は3つです。
- フォームは、アドレスを自社のエンドポイントに送信します。検証APIに直接送ってはいけません。キーが露出してしまうためです。
- サーバーレス関数は、キーを環境変数に保持し、
POST /verifyを呼び出して、結果を判断に変換します。 - サインアップ処理は、その判断に従って、拒否、修正候補の提示、受け入れのいずれかを行います。
サーバーレスのルート#
Vercel上でサーバーレス関数としてデプロイされる、Next.js App RouterのRoute Handlerとして示します。
キーはQUALISEND_API_KEYに保存し、クライアントには一切見えません。
// app/api/validate-email/route.ts
import { NextResponse } from "next/server";
const BASE = "https://app.qualisend.com/api/v1";
export async function POST(req: Request) {
const { email } = await req.json();
try {
const res = await fetch(`${BASE}/verify`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.QUALISEND_API_KEY!}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ email }),
signal: AbortSignal.timeout(4000), // don't let a slow probe stall signup
});
// On any API error, fail open — a real user must not be blocked by our outage.
if (!res.ok) return NextResponse.json({ ok: true, degraded: true });
const { result } = await res.json();
if (result.status === "undeliverable") {
return NextResponse.json({
ok: false,
reason: result.reason, // invalid_email | invalid_domain | rejected_email
suggestion: result.did_you_mean, // e.g. "jane@gmail.com"
});
}
if (result.sub_flags?.disposable) {
return NextResponse.json({ ok: false, reason: "disposable" });
}
// deliverable, risky, or unknown — accept, but pass along a typo suggestion if any
return NextResponse.json({ ok: true, status: result.status, suggestion: result.did_you_mean });
} catch {
return NextResponse.json({ ok: true, degraded: true }); // timeout or network error → fail open
}
}
キーはプラットフォームの環境変数ツール(vercel env add QUALISEND_API_KEY)で保存します。
そうすれば実行時に注入され、クライアント側にバンドルされることはありません。
判定結果に応じた処理#
すぐに返るresultはローカルの判定(構文、DNS、使い捨て、役割アドレス、入力ミス)です。
サインアップ時に求められるのはまさにこれで、高速であり、判断にも十分です。
| 結果 | サインアップ時の処理 |
|---|---|
undeliverable | その場で拒否します。「このアドレスは配信できないようです」と表示します。 |
did_you_meanがある場合 | 修正候補を提示します。「jane@gmail.comではありませんか?」と表示します。最も効果の高い救済です。 |
disposableフラグ | サービスに求める厳格さに応じて、拒否するかフラグを立てます。詳しくは、役割アドレス、使い捨て、フリーメールをご覧ください。 |
risky / キャッチオール | 受け入れたうえでタグを付け、キャッチオールのセグメントへ慎重に送信できるようにします。 |
unknown | 受け入れます。インフラから応答がなかっただけなので、このために実在のサインアップを失ってはいけません。 |
deliverable | 受け入れます。 |
常にフェイルオープンにする#
絶対に守るべき点は1つです。
検証の呼び出しがエラーになった場合、タイムアウトした場合、2xx以外を返した場合は、サインアップを通してください。
検証APIは品質のフィルターであり、認証のゲートではありません。
一時的な障害を理由に有料の顧客をブロックするほうが、疑わしいアドレスを1件通すよりもはるかに悪い結果になります。
上のコードで!res.okの分岐とcatchがどちらもok: trueを返しているのは、このためです。
劣化状態で通したサインアップはログに残し、次回のリストのクリーニングで確認してください。
よくある質問
01サインアップ時に検証すると、フォームが遅くなりますか?
正しく実装すればほとんど遅くなりません。ローカルの判定はすぐに返り、上のコードの4秒のタイムアウトが最悪の場合の上限になります。それを超えたらフェイルオープンにして、そのままサインアップを受け付けます。ユーザーにはほぼ即座に「もしかして」の修正候補が表示されます。SMTPプローブの結果をポーリングするのではなく、すぐに返るローカルの結果に基づいて処理するため、動作の遅いメールサーバーを待つことはありません。
02サインアップ時に使い捨てメールアドレスをブロックすべきですか?
プロダクトによります。有料サービスやレピュテーションに敏感なサービスでは、入口で使い捨てアドレスをブロックする価値があります。そうしたアドレスは、もともと二度と連絡を受けるつもりがありません。手間の少ない一般消費者向けのサインアップでは、手順を増やすより、フラグを立ててあとで確認するほうがよい場合もあります。上のルートは使い捨てアドレスを拒否しますが、ファネルに合わせてフラグを立てるだけに緩めることもできます。
03サインアップ時にSMTPで確認した完全な結果は必要ですか?
通常は必要ありません。すぐに返るローカルの判定で、入力ミス、存在しないドメイン、使い捨てアドレスなど、問題のあるサインアップの大半を待ち時間なしで防げます。SMTPで確認した完全な結果(GET /jobsまたは一括ジョブ)は、リストのクリーニング用に取っておいてください。そこでは待ち時間は問題にならず、存在しないメールボックスをすべて見つけることが重要です。
04APIキーを秘密に保つにはどうすればよいですか?
検証APIは、サーバーレス関数、ルートハンドラー、バックエンドなどのサーバー側のコードからのみ呼び出し、キーは環境変数に保存してください。クライアント側のJavaScriptには決して置かないでください。キーがブラウザのバンドルに含まれてしまう場合、置き場所が間違っています。
導入の準備はできましたか。無料プランには、この流れを試せる100クレジットが含まれています。APIリファレンスには、/verifyエンドポイントと、7言語のコピーしてそのまま使えるサンプルがあります。



