domain-check-lib 1.0.1

A fast, robust library for checking domain availability using RDAP and WHOIS protocols
Documentation

domain-check-lib

Rust library for checking domain availability using RDAP and WHOIS protocols. The same engine that powers the domain-check CLI.

Crates.io Documentation Downloads License: MIT OR Apache-2.0

Why use the library?

  • Domain registrar dashboards — check availability in real time as users type
  • SaaS platforms — let customers claim a custom domain during onboarding
  • Brand monitoring — scan TLDs for trademark squatting on a schedule
  • CI/CD pipelines — validate domain ownership before deploy
  • Bulk research tools — check thousands of domains with streaming results

At a Glance

  • 1,200+ TLDs via IANA bootstrap, 32 hardcoded as offline fallback
  • Dual protocol — RDAP-first with automatic WHOIS fallback via IANA server discovery
  • Up to 100 concurrent checks with configurable concurrency
  • Streaming support — process results as they arrive via futures_util::Stream
  • Rich metadata — registrar, creation/expiration dates, status codes, nameservers
  • 11 built-in TLD presets — startup, tech, creative, finance, etc.
  • Domain expansion — expand base names across TLD lists automatically
  • Comprehensive error types — timeout, network, parse, bootstrap errors with recovery hints
  • Pure async Rust — built on tokio + reqwest, no OpenSSL dependency

Want a ready-to-use CLI instead? See domain-check.

Quick Start

[dependencies]
domain-check-lib = "1.0.1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
use domain_check_lib::DomainChecker;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let checker = DomainChecker::new();
    let result = checker.check_domain("example.com").await?;

    match result.available {
        Some(true) => println!("{} is AVAILABLE", result.domain),
        Some(false) => println!("{} is TAKEN", result.domain),
        None => println!("{} status is UNKNOWN", result.domain),
    }

    Ok(())
}

Usage Examples

Bulk Domain Checking

use domain_check_lib::{DomainChecker, CheckConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = CheckConfig::default()
        .with_concurrency(20)
        .with_detailed_info(true);

    let checker = DomainChecker::with_config(config);
    let domains = vec![
        "example.com".to_string(),
        "google.org".to_string(),
        "github.io".to_string(),
    ];

    let results = checker.check_domains(&domains).await?;

    for result in results {
        match result.available {
            Some(true) => println!("{} is available", result.domain),
            Some(false) => println!("{} is taken", result.domain),
            None => println!("{} status unknown", result.domain),
        }
    }

    Ok(())
}

Streaming Results

use domain_check_lib::DomainChecker;
use futures_util::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let checker = DomainChecker::new();
    let domains = vec![
        "example.com".to_string(),
        "startup.org".to_string(),
        "mybrand.net".to_string(),
    ];

    let mut stream = checker.check_domains_stream(&domains);

    while let Some(result) = stream.next().await {
        match result {
            Ok(r) => println!("{}: {:?}", r.domain, r.available),
            Err(e) => eprintln!("Error: {}", e),
        }
    }

    Ok(())
}

Custom Configuration

use domain_check_lib::{DomainChecker, CheckConfig};
use std::time::Duration;

let config = CheckConfig::default()
    .with_concurrency(50)                          // Max 50 concurrent checks
    .with_timeout(Duration::from_secs(10))         // 10 second timeout
    .with_whois_fallback(true)                     // Enable WHOIS fallback
    .with_bootstrap(true)                          // Use IANA bootstrap
    .with_detailed_info(true);                     // Extract full domain info

let checker = DomainChecker::with_config(config);

TLD Management & Domain Expansion

use domain_check_lib::{
    DomainChecker, get_all_known_tlds, get_preset_tlds,
    initialize_bootstrap, expand_domain_inputs,
};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let checker = DomainChecker::new();

    // Pre-warm the IANA bootstrap cache for full TLD coverage
    initialize_bootstrap().await?;

    // Get all known TLDs (1,200+ after bootstrap, 32 hardcoded offline)
    let all_tlds = get_all_known_tlds();
    println!("Loaded {} TLDs", all_tlds.len());

    // Or use curated presets (11 built-in: startup, popular, tech, creative, etc.)
    let startup_tlds = get_preset_tlds("startup");

    // Expand base names with TLDs
    let domains = expand_domain_inputs(
        &["myapp".to_string(), "mystartup".to_string()],
        &startup_tlds,
    );
    // → myapp.com, myapp.io, myapp.ai, ..., mystartup.com, ...

    let results = checker.check_domains(&domains).await?;
    let available: Vec<_> = results.iter()
        .filter(|r| r.available == Some(true))
        .collect();

    println!("Found {} available domains", available.len());
    Ok(())
}

Bootstrap & WHOIS Discovery

use domain_check_lib::{initialize_bootstrap, get_whois_server};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Load 1,200+ TLDs from the IANA RDAP bootstrap registry
    initialize_bootstrap().await?;

    // Discover authoritative WHOIS servers for any TLD via IANA referral
    if let Some(server) = get_whois_server("com").await {
        println!(".com WHOIS server: {}", server); // → whois.verisign-grs.com
    }

    Ok(())
}

Error Handling

use domain_check_lib::DomainCheckError;

match checker.check_domain("invalid-domain").await {
    Ok(result) => println!("Success: {:?}", result),
    Err(DomainCheckError::InvalidDomain { domain, reason }) => {
        eprintln!("Invalid domain '{}': {}", domain, reason);
    }
    Err(DomainCheckError::NetworkError { message, .. }) => {
        eprintln!("Network error: {}", message);
    }
    Err(DomainCheckError::Timeout { operation, duration }) => {
        eprintln!("Timeout after {:?}: {}", duration, operation);
    }
    Err(e) => eprintln!("Other error: {}", e),
}

Data Structures

DomainResult

pub struct DomainResult {
    pub domain: String,                    // Domain that was checked
    pub available: Option<bool>,           // true = available, false = taken, None = unknown
    pub info: Option<DomainInfo>,          // Detailed registration info
    pub check_duration: Option<Duration>,  // How long the check took
    pub method_used: CheckMethod,          // RDAP, WHOIS, or Bootstrap
    pub error_message: Option<String>,     // Error details (if applicable)
}

DomainInfo

pub struct DomainInfo {
    pub registrar: Option<String>,
    pub creation_date: Option<String>,
    pub expiration_date: Option<String>,
    pub status: Vec<String>,
    pub updated_date: Option<String>,
    pub nameservers: Vec<String>,
}

Protocol Support

Protocol Role Details
RDAP Primary Structured JSON responses, 1,180+ TLDs via IANA bootstrap, rich data
WHOIS Fallback Targeted queries via IANA server discovery, smart parsing, rate limiting
Bootstrap Discovery Bulk IANA registry fetch (enabled by default), 24h cache, zero maintenance

Related

License

Licensed under either of Apache License, Version 2.0 or MIT License at your option.

Contributing

Contributions welcome. See CONTRIBUTING.md for setup and workflow.