ChartQuery

Detectar dominios desechables

Bloquea en tiempo real los registros con emails temporales verificando si un dominio pertenece a un proveedor de correo desechable usando la API de Veille.

Descripción general

Los proveedores de email desechables permiten a los usuarios crear bandejas de entrada temporales que se eliminan al cabo de minutos o días. Esta guía muestra cómo consultar el endpoint de validación de dominio de Veille y usar el resultado para bloquear o marcar registros en el momento del alta.

Prerrequisitos

  • Una clave de API de Veille — consigue una en app.veille.io
  • Instala las dependencias para tu lenguaje:
pip install requests

Sin dependencias adicionales — usa la API nativa fetch (Node 18+).

Extensión curl habilitada (activa por defecto).

Sin dependencias adicionales — usa net/http (Go 1.18+).

Sin dependencias adicionales — usa java.net.http (Java 11+).

Sin dependencias adicionales — usa System.Net.Http (.NET 6+).

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

Pasos

Verifica un dominio

Llama a GET /v1/domain con el parámetro domain. La respuesta indica si es desechable, gratuito o un dominio personalizado.

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(())
}

Lee los campos de la respuesta

La respuesta incluye is_disposable, is_free, is_custom, domain y provider (cuando se identifica).

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

if is_disposable:
    print(f"❌ {domain} es un proveedor de email DESECHABLE ({provider}). Bloquea este registro.")
elif is_free:
    print(f"⚠️  {domain} es un proveedor gratuito. Considera verificación adicional.")
else:
    print(f"✅ {domain} parece un dominio personalizado / empresarial. Permitir.")
const { domain, is_disposable, is_free, provider } = result;

if (is_disposable) {
  console.log(`❌ ${domain} es DESECHABLE (${provider ?? "desconocido"}). Bloquea este registro.`);
} else if (is_free) {
  console.log(`⚠️  ${domain} es un proveedor gratuito. Considera verificación adicional.`);
} else {
  console.log(`✅ ${domain} parece un dominio empresarial. Permitir.`);
}
$domain       = $result["domain"] ?? "";
$isDisposable = $result["is_disposable"] ?? false;
$isFree       = $result["is_free"] ?? false;
$provider     = $result["provider"] ?? "desconocido";

if ($isDisposable) {
    echo "❌ {$domain} es DESECHABLE ({$provider}). Bloquea este registro.\n";
} elseif ($isFree) {
    echo "⚠️  {$domain} es gratuito. Considera verificación adicional.\n";
} else {
    echo "✅ {$domain} parece un dominio empresarial. Permitir.\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 es DESECHABLE (%s). Bloquea este registro.\n", domain, provider)
case isFree:
    fmt.Printf("⚠️  %s es un proveedor gratuito.\n", domain)
default:
    fmt.Printf("✅ %s es un dominio empresarial. Permitir.\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", "desconocido");

if (isDisposable)      System.out.printf("❌ %s es DESECHABLE (%s). Bloquear.%n", domain, provider);
else if (isFree)       System.out.printf("⚠️  %s es gratuito. Verificación adicional.%n", domain);
else                   System.out.printf("✅ %s es un dominio empresarial. Permitir.%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() : "desconocido";

if (isDisposable)
    Console.WriteLine($"❌ {domain} es DESECHABLE ({provider}). Bloquear.");
else if (isFree)
    Console.WriteLine($"⚠️  {domain} es gratuito. Verificación adicional.");
else
    Console.WriteLine($"✅ {domain} es un dominio empresarial. Permitir.");
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("desconocido");

if is_disposable {
    println!("❌ {} es DESECHABLE ({}). Bloquear.", domain, provider);
} else if is_free {
    println!("⚠️  {} es gratuito. Verificación adicional.", domain);
} else {
    println!("✅ {} es un dominio empresarial. Permitir.", domain);
}

Integra en un handler de registro

Añade la verificación de dominio a tu endpoint de registro y devuelve un error de validación antes de crear el registro de usuario.

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": "No se permiten direcciones de email desechables."}
    # ... crear el registro de usuario en la base de datos
    return {"success": True, "message": f"¡Bienvenido, {email}!"}

print(register_user("[email protected]", "secreto"))
print(register_user("[email protected]",    "secreto"))
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: "No se permiten direcciones de email desechables." };
  }
  // ... crear registro de usuario
  return { success: true, message: `¡Bienvenido, ${email}!` };
}

console.log(await registerUser("[email protected]", "secreto"));
console.log(await registerUser("[email protected]",    "secreto"));
<?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" => "No se permiten direcciones de email desechables."];
    }
    // ... crear registro de usuario
    return ["success" => true, "message" => "¡Bienvenido, {$email}!"];
}

print_r(registerUser("YOUR_API_KEY", "[email protected]", "secreto"));
print_r(registerUser("YOUR_API_KEY", "[email protected]",    "secreto"));
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 "❌ Email desechable no permitido."
    }
    return "✅ ¡Bienvenido, " + email + "!"
}

func main() {
    apiKey := "YOUR_API_KEY"
    fmt.Println(registerUser(apiKey, "[email protected]", "secreto"))
    fmt.Println(registerUser(apiKey, "[email protected]",    "secreto"))
}
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 "❌ Email desechable no permitido.";
        return "✅ ¡Bienvenido, " + email + "!";
    }

    public static void main(String[] args) throws Exception {
        System.out.println(registerUser("[email protected]", "secreto"));
        System.out.println(registerUser("[email protected]",    "secreto"));
    }
}
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)
        ? "❌ Email desechable no permitido."
        : $"✅ ¡Bienvenido, {email}!";

Console.WriteLine(await RegisterUser("[email protected]", "secreto"));
Console.WriteLine(await RegisterUser("[email protected]",    "secreto"));
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!("❌ {} — desechable. Bloquear.", email);
        } else {
            println!("✅ {} — parece válido. Permitir.", email);
        }
    }
    Ok(())
}

Almacena en caché los resultados de dominios consultados frecuentemente (p. ej. en Redis con TTL de 24 horas) para evitar llamadas redundantes a la API. La lista de proveedores desechables raramente cambia en un mismo día.

En esta página