Watch-IP

メールチェックAPIリファレンス

POST/v1/email/validate

1件のメールアドレスをチェックします:構文、既知の使い捨て/一時的なドメイン、ドメインのメールサーバー(MX)、入力ミスの可能性、ロールアドレスを確認し、理由を添えてaccept/review/rejectの判定を返します。{ "email": "user@example.com" }というJSON本文を送信してください。GET /v1/geoと同じ、公開可能でオリジンロックされたキーのモデルを使い、サインアップフォーム自身のJavaScriptから呼び出されるように設計されています。多数のアドレスや、サーバーサイドでのサインアップ判断には、このページの後半にある一括チェック、リストジョブ、サインアップチェックの各エンドポイントを使用してください。すべてのチェックはドメインとアドレスのレベルで行われます。メールボックスには一切接続しないため、どの結果もメールボックスの存在、所有者、メールが届くことを証明するものではありません。

認証

POST /v1/email/validateは、X-Api-Keyヘッダーでオリジンロックされた公開可能キーを受け付けます — GET /v1/geoと同じキー種別と許可オリジンのモデルです。このページのその他すべてのエンドポイント(一括チェック、リストジョブ、サインアップチェック)は多数のアドレスや任意のIPを受け付けるため、Authorization: Bearerでシークレットキーが必要であり、必ずバックエンドから呼び出してください。公開可能キーを使うと403が返ります。

レスポンスフィールド

email
送信したアドレス。送信された内容そのままが返されます。
domain
小文字化したドメイン(国際化ドメインはpunycode)。syntaxValidがfalseの場合はnull。
syntaxValid
アドレスが構文上有効かどうか。メールボックスの存在や配信可能性のチェックではありません。
isDisposable
ドメインが既知の使い捨て/一時的なメールプロバイダーに該当するかどうか — 直接、親ドメイン経由、または使い捨てドメインを扱うことが知られているメールサーバー経由で判定します。syntaxValidがfalseの場合は常にfalse。
normalizedEmail
前後の空白を除去して小文字化し、ドメインをpunycodeにしたアドレス。syntaxValidがfalseの場合はnull。
canonicalEmail
normalizedEmailから+タグを除いたもの(Gmailの場合はさらにドットを除去し、googlemail.comをgmail.comに統一) — 同一人物による二重登録を見つけるために保存してください。syntaxValidがfalseの場合はnull。
disposableSource
isDisposableがtrueになった理由:list(ドメイン自体がリストに掲載)、parent_domain(親ドメインが掲載、例:a.b.mailinator.com)、mx_host(ドメインのメールサーバーが既知の使い捨てドメインを扱っている)。isDisposableがfalseの場合はnull。
isFreeProvider
ドメインがgmail.comやoutlook.comのような一般消費者向けメールボックスプロバイダーかどうか。参考情報であり、判定を変えることはありません。
isRoleAccount
ローカル部がadmin@、info@、noreply@のようなロールアドレスかどうか。
didYouMean
アドレス全体に対する入力ミスの修正候補(john@gmial.com → john@gmail.com)、またはnull。自前の正常なメールサーバーを持つドメインは、一般的なプロバイダーと1文字違いでない限り「修正」されません。
mx.status
found(MXレコードあり)、implicit(MXはないがA/AAAAレコードあり)、none(どちらもなし)、null_mx(ドメインがメールを受け付けないと宣言)、no_domain(ドメインが存在しない)、unknown(検索が失敗またはタイムアウト)のいずれか。syntaxValidがfalseの場合、mxオブジェクト全体がnullになります。
mx.host
statusがfoundの場合は優先度値が最も低いMXホスト、それ以外はnull。個々のメールボックスが存在するかどうかについては何も示しません。
verdict
accept、review、reject — reasonsに含まれる条件のうち最も重いもの。最終決定ではなく、自社ポリシーのための推奨です。
reasons
該当したすべての条件(重いものから順)。verdictがacceptの場合は空です。下記の「判定と理由コード」を参照してください。

エラー

400
不正な形式のリクエスト本文。一括:empty_batchまたはbatch_too_large(100件超)。サインアップチェック:invalid_ip。リストジョブ:invalid_body、empty_job、job_too_large(100,000件超)。無効なメールアドレスは400にはならず、判定reject・理由invalid_syntaxの結果として返されます。
401
APIキーが欠落しているか無効です。
403
POST /v1/email/validate:リクエストのOrigin/Refererがキーの許可オリジンリストにない(CORSヘッダーが設定されないため、JSON本文に関係なくブラウザがレスポンスをブロックします)。シークレットキー用エンドポイント:公開可能キーが使われた。
404
job_not_found — このIDのリストジョブがアカウントに存在しません。
409
job_not_completed — ジョブがまだキュー待ちか処理中、または失敗しています。結果はジョブ完了後にのみダウンロードできます。
410
job_expired — ジョブのアップロード済みアドレスと結果は削除済みです(作成から7日後)。
413
body_too_large — 10 MBを超えるジョブのCSVアップロード。
415
JSONでもtext/csvでもないリストジョブの本文。
429
1分あたりのレート制限、またはプランの月間リクエスト上限を超える場合のmonthly_limit_exceeded — 見分け方はエラーとレート制限を参照してください。上限を超える一括チェックやリストジョブは、処理を始める前に全体が拒否されます。
503
job_storage_unavailable — リストジョブを保存できませんでした。課金は発生していません。再試行してください。

例

リクエスト

curl https://api.watch-ip.com/v1/email/validate \
  -H "X-Api-Key: YOUR_PUBLISHABLE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "John.Doe+news@Gmial.com"}'

レスポンス

{
  "email": "John.Doe+news@Gmial.com",
  "domain": "gmial.com",
  "syntaxValid": true,
  "isDisposable": false,
  "normalizedEmail": "john.doe+news@gmial.com",
  "canonicalEmail": "john.doe@gmial.com",
  "disposableSource": null,
  "isFreeProvider": false,
  "isRoleAccount": false,
  "didYouMean": "john.doe+news@gmail.com",
  "mx": {
    "status": "none",
    "host": null
  },
  "verdict": "reject",
  "reasons": [
    "no_mail_server",
    "typo_suspected"
  ]
}

判定と理由コード

各条件は最低限の判定を定め、最も重いものが採用されます。reasonsには該当したすべてが重い順に列挙されます。フリーメールのアドレスを許容するかどうかはお客様の判断に委ねられ、判定を変えることはありません。

invalid_syntax
Reject。アドレスが構文上無効です(長さの上限、使用可能な文字、ドットの位置、IPリテラルではなく実在ドメインであること)。引用符付きのローカル部も拒否されます — 規格上は有効ですが、サインアップで使われることは事実上ありません。
domain_not_found
Reject。ドメインがDNSに存在しません。
no_mail_server
Reject。ドメインがメールを受け付けないと宣言している(「null MX」)か、MXレコードもA/AAAAレコードもありません。
disposable_domain
Reject。ドメイン、その親ドメイン、またはドメインのメールサーバーが既知の使い捨て/一時的なメールプロバイダーに該当します — disposableSourceを参照してください。
typo_suspected
Review。didYouMeanに修正候補(例:gmial.com → gmail.com)が入っています。
role_account
Review。ローカル部がadmin@、info@、support@、noreply@のようなロールアドレスで、通常は個人用ではなく共有されています。
implicit_mx
Review。ドメインにMXレコードがなく、メールはA/AAAAアドレスにフォールバックします。実際のメールドメインではまれな構成です。
dns_unavailable
Review。DNS検索が失敗またはタイムアウトしたため、ドメインのメール設定が不明です(mx.status: unknown)。アドレスに不利な証拠ではありません。

一括チェック

POST/v1/email/batch

1回のリクエストで1〜100件のアドレスを、POST /v1/email/validateと同じチェックと結果形式でチェックし、入力順にアドレスごとの結果を返します。{ "emails": [...] }を送信してください。同じドメインのアドレスは1回のDNS検索を共有します。アドレスごとに1リクエストとして月間プラン上限に計上され、上限を超える一括チェックは429 monthly_limit_exceededで全体が拒否されます。1分あたりのレート制限に対しては、呼び出し全体が1リクエストとして数えられます。シークレットキー専用。

リクエスト

curl https://api.watch-ip.com/v1/email/batch \
  -H "Authorization: Bearer YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"emails": ["user@gmail.com", "someone@mailinator.com"]}'

レスポンス

{
  "results": [
    { "email": "user@gmail.com", "verdict": "accept", "reasons": [], ... },
    { "email": "someone@mailinator.com", "isDisposable": true, "disposableSource": "list", "verdict": "reject", "reasons": ["disposable_domain"], ... }
  ]
}

メールリストジョブ

POST/v1/email/jobs

最大100,000件のアドレスをバックグラウンドでチェックします。JSON { "emails": [...], "filename": "..." }、または最大10 MBのtext/csv本文を送信してください:見出しがemail(e-mail、email address、email_address、mailも可、大文字小文字は区別しない)の列が使われ、該当する見出しがなければ先頭列が使われます。区切り文字はカンマ、セミコロン、タブを自動検出します。ジョブは作成時に課金されます — 重複を除いたアドレス(完全一致)ごとに1リクエストなので、重複もチェックされますが課金は1回です — 月間上限を超えるリストは全体が拒否されます。再試行後もジョブの一部を処理できなかった場合、ジョブはfailedとなり、未処理分のクレジットは返還されます。ジョブオブジェクトとともに202を返します。シークレットキー専用。同じジョブはダッシュボードのEmail listsページからもアップロード・ダウンロードできます。

リクエスト

curl https://api.watch-ip.com/v1/email/jobs \
  -H "Authorization: Bearer YOUR_SECRET_KEY" \
  -H "Content-Type: text/csv" \
  --data-binary @newsletter.csv

レスポンス

{
  "id": "5b0d6f5e-8a51-4f7e-9d0e-2c1f3f7f8a11",
  "status": "queued",
  "source": "api",
  "filename": null,
  "total": 12840,
  "credits": 12512,
  "processed": 0,
  "summary": null,
  "createdAt": "2026-09-28T10:00:00.000Z",
  "completedAt": null,
  "expiresAt": "2026-10-05T10:00:00.000Z"
}

ジョブのステータス

GET/v1/email/jobs/{id}

同じジョブオブジェクトを返します。statusがcompletedになるまでポーリングしてください。ポーリングは課金されません。

status
queued → processing → completed。failed:再試行後もリストの一部を処理できませんでした。結果はなく、未処理分のクレジットは返還済みです。expired:アップロード済みアドレスと結果は作成から7日後に削除されました。
total
送信された行数(重複を含む)。
credits
課金されたリクエスト数:重複を除いたアドレスごとに1件。
processed
これまでにチェックされた行数。
summary
完了後:判定(accept/review/reject)ごと、および発生した理由コードごとの行数。それまではnull。
expiresAt
アップロード済みアドレスと結果が削除される日時。

ジョブの結果

GET/v1/email/jobs/{id}/results

送信された各行に対して1行ずつ、送信順(重複を含む)のCSVをダウンロードします。列:email、normalized_email、verdict、reasons、syntax_valid、is_disposable、disposable_source、is_free_provider、is_role_account、did_you_mean、mx_status、mx_host。reasonsはセミコロン区切り、空のセルはnullを意味し、構文上無効なアドレスではmx_statusは空です(検索を行っていないため)。=、+、-、@で始まる無効なアドレスには、表計算ソフトが数式として実行しないようにアポストロフィが先頭に付けられます。ジョブ完了までは409、期限切れ後は410。ダウンロードは課金されません。

リクエスト

curl https://api.watch-ip.com/v1/email/jobs/5b0d6f5e-8a51-4f7e-9d0e-2c1f3f7f8a11/results \
  -H "Authorization: Bearer YOUR_SECRET_KEY" -o results.csv

サインアップチェック

POST/v1/signup/check

サインアップフォームの送信時にバックエンドから、メールアドレスとIPに基づいてサインアップを1回の呼び出しでスコアリングします(ブラウザで計算した判定は偽装される可能性があります)。{ "email": "...", "ip": "...", "userAgent": "..." }を送信してください — userAgentは任意です。レスポンスには、メールチェックの完全な結果(email)、IPの国とASN、およびセキュリティデータを含むプランではVPN/Tor/データセンター/脅威の指標(ip)、公開登録データ(RDAP)に基づくメールドメインの経過日数(emailDomainAgeDays。不明な場合と著名な一般消費者向けプロバイダーではnull)、0〜100のスコア(下記の理由ごとの重みの合計、上限100)、および判定(60以上はreject、25以上はreview、それ以外はaccept)が含まれます。セキュリティデータのないプランでは、ip.securityはnullとなり、スコアはメールのシグナルのみを使い、reasonsにip_security_unavailableが含まれます。1リクエストとして計上されます。シークレットキー専用。

invalid_syntax
70
domain_not_found · no_mail_server · disposable_domain
各60
ip_tor · ip_threat
各40
ip_vpn
25
new_domain
25 — メールドメインの登録から30日未満
ip_datacenter
20 — IPがVPNやTorの出口でない場合のみ
automated_user_agent
20 — 指定されたuserAgentがボットやスクリプトのように見える
typo_suspected
15
role_account · implicit_mx
各10
dns_unavailable
5
ip_security_unavailable
0 — 参考情報:このプランにはセキュリティデータがありません

リクエスト

curl https://api.watch-ip.com/v1/signup/check \
  -H "Authorization: Bearer YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "jane@new-startup.io", "ip": "203.0.113.1"}'

レスポンス

{
  "email": { "email": "jane@new-startup.io", "verdict": "accept", "reasons": [], ... },
  "ip": {
    "ip": "203.0.113.1",
    "country": "US",
    "asn": 64500,
    "asOrganization": "Example Hosting",
    "security": { "isTor": false, "isVpn": false, "isDatacenter": true, "isThreat": false, "isSanctionedNetwork": false }
  },
  "emailDomainAgeDays": 12,
  "score": 45,
  "verdict": "review",
  "reasons": ["new_domain", "ip_datacenter"]
}

制限

チェックしたアドレスはすべて、プラン共通の月間上限に対して1リクエストとして計上されます — 単一チェック、一括チェックの各アドレス、リストジョブの重複を除いた各アドレス — サインアップチェックは1リクエストです。一括チェックとリストジョブは全体が受け付けられるか拒否されるかのいずれかで、部分的に処理されることはありません。1分あたりのレート制限はアイテム数ではなくHTTP呼び出し数で数えます。最大100件のIPまたはメールアドレスを含むバッチは1リクエストとして数えられ、各アイテムは引き続き月間リクエスト上限に個別に計上されます。リストジョブのアップロードと結果はジョブ作成から7日後に削除されます。DNS検索には短い時間枠があり、失敗やタイムアウトの場合はエラーではなく、mx.status unknownと理由dns_unavailableに縮退します。使い捨てドメインのデータは、コミュニティが管理する3つのオープンソースリストを統合したもので、数時間ごとに更新され、掲載ドメインを扱っていることが観測されたメールサーバーで拡張されます — データソースを参照してください。非常に新しい使い捨てプロバイダーは、まだ検出されない場合があります。すべてのチェックはドメインとアドレスのレベルで行われます。メールボックスには一切接続しないため、どの結果もメールボックスの存在、所有者、メールが届くことを証明するものではありません。

関連ページ