ChartQuery

Detect Disposable Domains

Block throwaway email registrations in real time by checking whether a domain is from a disposable email provider using the Veille API.

Overview

Disposable email providers let users create temporary inboxes that get deleted after minutes or days. This playbook shows how to query the Veille domain validation endpoint and use the result to block or flag signups at the point of registration.

Prerequisites

  • A Veille API key — get one at app.veille.io
  • Install dependencies for your language:
pip install requests

No extra dependencies — uses the native fetch API (Node 18+).

curl extension enabled (on by default).

No extra dependencies — uses net/http (Go 1.18+).

No extra dependencies — uses java.net.http (Java 11+).

No extra dependencies — uses System.Net.Http (.NET 6+).

# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"

Steps

Check a domain

Call GET /v1/domain with the domain parameter. The response returns whether it is disposable, free, or a custom domain.

import requests

API_KEY  = "YOUR_API_KEY"
response = requests.get(
    "https://api.veille.io/v1/domain",
    headers={"x-api-key": API_KEY},
    params={"domain": "mailinator.com"},
)
result = response.json()
print(result)
const API_KEY = "YOUR_API_KEY";

const params   = new URLSearchParams({ domain: "mailinator.com" });
const response = await fetch(`https://api.veille.io/v1/domain?${params}`, {
  headers: { "x-api-key": API_KEY },
});
const result = await response.json();
console.log(result);
<?php
$apiKey = "YOUR_API_KEY";
$params = http_build_query(["domain" => "mailinator.com"]);

$ch = curl_init("https://api.veille.io/v1/domain?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($result);
package main

import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)

func main() {
    req, _ := http.NewRequest("GET", "https://api.veille.io/v1/domain?domain=mailinator.com", nil)
    req.Header.Set("x-api-key", "YOUR_API_KEY")

    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)

    var result map[string]any
    json.Unmarshal(body, &result)
    fmt.Println(result)
}
import java.net.URI;
import java.net.http.*;

var client  = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.veille.io/v1/domain?domain=mailinator.com"))
    .header("x-api-key", "YOUR_API_KEY")
    .GET().build();

var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
using System.Net.Http;

using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY");

var body = await client.GetStringAsync("https://api.veille.io/v1/domain?domain=mailinator.com");
Console.WriteLine(body);
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let result = reqwest::Client::new()
        .get("https://api.veille.io/v1/domain")
        .header("x-api-key", "YOUR_API_KEY")
        .query(&[("domain", "mailinator.com")])
        .send().await?.json::<serde_json::Value>().await?;

    println!("{:#?}", result);
    Ok(())
}

Read the response fields

The response includes is_disposable, is_free, is_custom, domain, and provider (when identified).

domain        = result.get("domain")
is_disposable = result.get("is_disposable")
is_free       = result.get("is_free")
provider      = result.get("provider", "unknown")

if is_disposable:
    print(f"❌ {domain} is a DISPOSABLE email provider ({provider}). Block this signup.")
elif is_free:
    print(f"⚠️  {domain} is a free email provider. Consider extra verification.")
else:
    print(f"✅ {domain} looks like a custom / business domain. Allow.")
const { domain, is_disposable, is_free, provider } = result;

if (is_disposable) {
  console.log(`❌ ${domain} is DISPOSABLE (${provider ?? "unknown"}). Block this signup.`);
} else if (is_free) {
  console.log(`⚠️  ${domain} is a free provider. Consider extra verification.`);
} else {
  console.log(`✅ ${domain} looks like a business domain. Allow.`);
}
$domain      = $result["domain"] ?? "";
$isDisposable = $result["is_disposable"] ?? false;
$isFree       = $result["is_free"] ?? false;
$provider     = $result["provider"] ?? "unknown";

if ($isDisposable) {
    echo "❌ {$domain} is DISPOSABLE ({$provider}). Block this signup.\n";
} elseif ($isFree) {
    echo "⚠️  {$domain} is free. Consider extra verification.\n";
} else {
    echo "✅ {$domain} looks like a business domain. Allow.\n";
}
domain        := result["domain"].(string)
isDisposable  := result["is_disposable"].(bool)
isFree        := result["is_free"].(bool)
provider, _   := result["provider"].(string)

switch {
case isDisposable:
    fmt.Printf("❌ %s is DISPOSABLE (%s). Block this signup.\n", domain, provider)
case isFree:
    fmt.Printf("⚠️  %s is a free provider.\n", domain)
default:
    fmt.Printf("✅ %s is a business domain. Allow.\n", domain)
}
import org.json.*;

var r            = new JSONObject(response.body());
var domain       = r.getString("domain");
var isDisposable = r.getBoolean("is_disposable");
var isFree       = r.getBoolean("is_free");
var provider     = r.optString("provider", "unknown");

if (isDisposable)      System.out.printf("❌ %s is DISPOSABLE (%s). Block.%n", domain, provider);
else if (isFree)       System.out.printf("⚠️  %s is free. Extra verification.%n", domain);
else                   System.out.printf("✅ %s is a business domain. Allow.%n", domain);
using System.Text.Json;

var r            = JsonDocument.Parse(body).RootElement;
var domain       = r.GetProperty("domain").GetString();
var isDisposable = r.GetProperty("is_disposable").GetBoolean();
var isFree       = r.GetProperty("is_free").GetBoolean();
var provider     = r.TryGetProperty("provider", out var p) ? p.GetString() : "unknown";

if (isDisposable)
    Console.WriteLine($"❌ {domain} is DISPOSABLE ({provider}). Block.");
else if (isFree)
    Console.WriteLine($"⚠️  {domain} is free. Extra verification.");
else
    Console.WriteLine($"✅ {domain} is a business domain. Allow.");
let domain       = result["domain"].as_str().unwrap_or("");
let is_disposable = result["is_disposable"].as_bool().unwrap_or(false);
let is_free       = result["is_free"].as_bool().unwrap_or(false);
let provider      = result["provider"].as_str().unwrap_or("unknown");

if is_disposable {
    println!("❌ {} is DISPOSABLE ({}). Block.", domain, provider);
} else if is_free {
    println!("⚠️  {} is free. Extra verification.", domain);
} else {
    println!("✅ {} is a business domain. Allow.", domain);
}

Integrate into a signup handler

Add the domain check to your registration endpoint and return a validation error before the user record is created.

import requests

API_KEY = "YOUR_API_KEY"

def is_disposable_email(email: str) -> bool:
    domain = email.split("@")[-1].lower()
    r = requests.get("https://api.veille.io/v1/domain",
        headers={"x-api-key": API_KEY}, params={"domain": domain})
    return r.json().get("is_disposable", False)

def register_user(email: str, password: str) -> dict:
    if is_disposable_email(email):
        return {"success": False, "error": "Disposable email addresses are not allowed."}
    # ... create the user record in your database
    return {"success": True, "message": f"Welcome, {email}!"}

print(register_user("[email protected]", "secret"))
print(register_user("[email protected]",    "secret"))
const API_KEY = "YOUR_API_KEY";

async function isDisposableEmail(email: string): Promise<boolean> {
  const domain = email.split("@").at(-1)!.toLowerCase();
  const params = new URLSearchParams({ domain });
  const res    = await fetch(`https://api.veille.io/v1/domain?${params}`, {
    headers: { "x-api-key": API_KEY },
  });
  return (await res.json()).is_disposable ?? false;
}

async function registerUser(email: string, password: string) {
  if (await isDisposableEmail(email)) {
    return { success: false, error: "Disposable email addresses are not allowed." };
  }
  // ... create user record
  return { success: true, message: `Welcome, ${email}!` };
}

console.log(await registerUser("[email protected]", "secret"));
console.log(await registerUser("[email protected]",    "secret"));
<?php
function isDisposableEmail(string $apiKey, string $email): bool {
    $domain = strtolower(explode("@", $email)[1]);
    $params = http_build_query(["domain" => $domain]);
    $ch = curl_init("https://api.veille.io/v1/domain?{$params}");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
    $result = json_decode(curl_exec($ch), true);
    curl_close($ch);
    return $result["is_disposable"] ?? false;
}

function registerUser(string $apiKey, string $email, string $password): array {
    if (isDisposableEmail($apiKey, $email)) {
        return ["success" => false, "error" => "Disposable email addresses are not allowed."];
    }
    // ... create user record
    return ["success" => true, "message" => "Welcome, {$email}!"];
}

print_r(registerUser("YOUR_API_KEY", "[email protected]", "secret"));
print_r(registerUser("YOUR_API_KEY", "[email protected]",    "secret"));
package main

import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "strings"
)

func isDisposable(apiKey, email string) bool {
    domain := strings.ToLower(strings.SplitN(email, "@", 2)[1])
    req, _ := http.NewRequest("GET", "https://api.veille.io/v1/domain?domain="+domain, nil)
    req.Header.Set("x-api-key", apiKey)
    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)
    var result map[string]any
    json.Unmarshal(body, &result)
    v, _ := result["is_disposable"].(bool)
    return v
}

func registerUser(apiKey, email, password string) string {
    if isDisposable(apiKey, email) {
        return "❌ Disposable email not allowed."
    }
    return "✅ Welcome, " + email + "!"
}

func main() {
    apiKey := "YOUR_API_KEY"
    fmt.Println(registerUser(apiKey, "[email protected]", "secret"))
    fmt.Println(registerUser(apiKey, "[email protected]",    "secret"))
}
import java.net.URI;
import java.net.http.*;
import org.json.*;

public class Main {
    static HttpClient client = HttpClient.newHttpClient();
    static String API_KEY   = "YOUR_API_KEY";

    static boolean isDisposable(String email) throws Exception {
        var domain  = email.substring(email.indexOf('@') + 1).toLowerCase();
        var request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.veille.io/v1/domain?domain=" + domain))
            .header("x-api-key", API_KEY).GET().build();
        var resp    = client.send(request, HttpResponse.BodyHandlers.ofString());
        return new JSONObject(resp.body()).optBoolean("is_disposable", false);
    }

    static String registerUser(String email, String password) throws Exception {
        if (isDisposable(email)) return "❌ Disposable email not allowed.";
        return "✅ Welcome, " + email + "!";
    }

    public static void main(String[] args) throws Exception {
        System.out.println(registerUser("[email protected]", "secret"));
        System.out.println(registerUser("[email protected]",    "secret"));
    }
}
using System.Net.Http;
using System.Text.Json;

var apiKey = "YOUR_API_KEY";
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", apiKey);

async Task<bool> IsDisposable(string email)
{
    var domain = email.Split('@').Last().ToLower();
    var body   = await client.GetStringAsync($"https://api.veille.io/v1/domain?domain={domain}");
    return JsonDocument.Parse(body).RootElement.GetProperty("is_disposable").GetBoolean();
}

async Task<string> RegisterUser(string email, string password) =>
    await IsDisposable(email)
        ? "❌ Disposable email not allowed."
        : $"✅ Welcome, {email}!";

Console.WriteLine(await RegisterUser("[email protected]", "secret"));
Console.WriteLine(await RegisterUser("[email protected]",    "secret"));
use reqwest::Client;
use serde_json::Value;

async fn is_disposable(client: &Client, api_key: &str, email: &str) -> bool {
    let domain = email.split('@').last().unwrap_or("").to_lowercase();
    let result = client.get("https://api.veille.io/v1/domain")
        .header("x-api-key", api_key)
        .query(&[("domain", domain.as_str())])
        .send().await.unwrap().json::<Value>().await.unwrap();
    result["is_disposable"].as_bool().unwrap_or(false)
}

#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let client  = Client::new();
    let api_key = "YOUR_API_KEY";

    for email in ["[email protected]", "[email protected]"] {
        if is_disposable(&client, api_key, email).await {
            println!("❌ {} — disposable. Block.", email);
        } else {
            println!("✅ {} — looks valid. Allow.", email);
        }
    }
    Ok(())
}

Cache results for frequently checked domains (e.g. in Redis with a 24-hour TTL) to avoid redundant API calls. The list of disposable providers rarely changes within a single day.

On this page