Version: v1
Description: Obtiene la informacion de servidores WHOIS o RDAP para cualquier dominio, con soporte para gTLD y ccTLD.
La API provee dos endpoints principales:
/whois - Consulta directa de dominios (sin cache)/whoiscache - Consulta de dominios con cache (máximo 30 días)URL Base: https://api.latinapi.com/api/whois
/whoisMétodos: GET, POST
/whoiscacheMétodos: GET, POST
Para autenticar las solicitudes es necesario enviar en encabezado X-API-Key con tu clave API válida en cada petición.
GET /whois?domain=google.com
GET /whoiscache?domain=ejemplo.orgPOST /whois
Content-Type: application/json
{
"domain": "google.com"
}
POST /whois
Content-Type: application/x-www-form-urlencoded
domain=google.com
{
"status": "success",
"type": "rdap",
"cached": false,
"data": {
"creation_date": "1997-09-15T04:00:00Z",
"updated_date": "2026-02-27T04:43:53Z",
"expiration_date": "2028-09-14T04:00:00Z",
"registrar_name": "MarkMonitor Inc.",
"status": [
"client delete prohibited",
"client transfer prohibited"
],
"dns_servers": [
"NS1.GOOGLE.COM",
"NS2.GOOGLE.COM"
]
}
}{
"status": "available",
"type": "rdap",
"cached": false,
"message": "Domain is available for registration"
}{
"error": true,
"message": "Invalid or missing domain name",
"http_code": 400,
"error_code": "INVALID_DOMAIN"
}# Consulta Directa
curl "https://api.latinapi.com/api/whois?domain=google.com"
# Consulta con Cache
curl "https://api.latinapi.com/api/whoiscache?domain=google.com"
curl -X POST "https://api.latinapi.com/api/whois" \
-H "Content-Type: application/json" \
-d '{"domain":"google.com"}'curl -X POST "https://api.latinapi.com/api/whois" \
-d "domain=google.com"curl -s "https://api.latinapi.com/api/whois?domain=google.com" | jq '.'# Obtener solo el nombre del registrador
curl -s "https://api.latinapi.com/api/whois?domain=google.com" | jq -r '.data.registrar_name'
# Obtener la fecha de vencimiento
curl -s "https://api.latinapi.com/api/whois?domain=google.com" | jq -r '.data.expiration_date'
# Consultar si el dominio está disponible
curl -s "https://api.latinapi.com/api/whois?domain=noexiste12345.com" | jq -r '.status'
#!/bin/bash
domains=("google.com" "github.com" "stackoverflow.com")
for domain in "${domains[@]}"; do
echo "Consultando $domain..."
curl -s "https://api.latinapi.com/api/whoiscache?domain=$domain" | jq '{domain: "'$domain'", status: .status, registrar: .data.registrar_name}'
echo
done
// Consulta directa
async function lookupDomain(domain) {
try {
const response = await fetch(`https://api.latinapi.com/api/whois?domain=${domain}`);
const data = await response.json();
if (data.error) {
throw new Error(data.message);
}
return data;
} catch (error) {
console.error('Consulta de dominio con error:', error.message);
throw error;
}
}
// Uso
lookupDomain('google.com')
.then(result => {
console.log('Estado del dominio:', result.status);
if (result.status === 'success') {
console.log('Registrador:', result.data.registrar_name);
console.log('Expira:', result.data.expiration_date);
}
})
.catch(error => console.error(error));
const axios = require('axios');
class DomainLookupClient {
constructor(baseURL = 'https://api.latinapi.com/api') {
this.client = axios.create({
baseURL,
timeout: 30000,
headers: {
'Content-Type': 'application/json'
}
});
}
async lookup(domain, useCache = false) {
const endpoint = useCache ? '/whoiscache' : '/whois';
try {
const response = await this.client.post(endpoint, { domain });
return response.data;
} catch (error) {
if (error.response) {
throw new Error(error.response.data.message || 'API Error');
}
throw error;
}
}
async batchLookup(domains, useCache = true) {
const promises = domains.map(domain =>
this.lookup(domain, useCache).catch(error => ({
domain,
error: error.message
}))
);
return Promise.all(promises);
}
}
// Uso
const client = new DomainLookupClient();
// Consulta de un solo dominio
client.lookup('google.com', true)
.then(result => console.log(result))
.catch(error => console.error(error));
// Consultas por lotes
const domains = ['google.com', 'github.com', 'nonexistent12345.com'];
client.batchLookup(domains)
.then(results => {
results.forEach(result => {
if (result.error) {
console.log(`${result.domain}: Error - ${result.error}`);
} else {
console.log(`${result.domain}: ${result.status}`);
}
});
});
import React, { useState, useEffect } from 'react';
const DomainLookup = () => {
const [domain, setDomain] = useState('');
const [result, setResult] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const lookupDomain = async (domainName) => {
setLoading(true);
setError(null);
try {
const response = await fetch(`https://api.latinapi.com/api/whoiscache?domain=${domainName}`);
const data = await response.json();
if (data.error) {
throw new Error(data.message);
}
setResult(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
const handleSubmit = (e) => {
e.preventDefault();
if (domain.trim()) {
lookupDomain(domain.trim());
}
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
type="text"
value={domain}
onChange={(e) => setDomain(e.target.value)}
placeholder="Ingresa el dominio"
disabled={loading}
/>
<button type="submit" disabled={loading}>
{loading ? 'Consultando...' : 'Consultar'}
</button>
</form>
{error && <div className="error">Error: {error}</div>}
{result && (
<div className="result">
<h3>Dominio: {domain}</h3>
<p>Estado: {result.status}</p>
<p>Tipo: {result.type}</p>
{result.cached && <p>En Cache: Si</p>}
{result.status === 'success' && result.data && (
<div>
<p>Registrador: {result.data.registrar_name}</p>
<p>Creado: {result.data.creation_date}</p>
<p>Expira: {result.data.expiration_date}</p>
<p>Servidores DNS: {result.data.dns_servers.join(', ')}</p>
</div>
)}
{result.status === 'available' && (
<p className="available">El dominio se encuentra disponibel para registro!</p>
)}
</div>
)}
</div>
);
};
export default DomainLookup;
import requests
import json
from typing import Dict, List, Optional
from datetime import datetime
class DomainLookupClient:
def __init__(self, base_url: str = "https://api.latinapi.com/api"):
self.base_url = base_url.rstrip('/')
self.session = requests.Session()
self.session.headers.update({
'Content-Type': 'application/json',
'User-Agent': 'Domain Lookup Python Client/1.0'
})
def lookup(self, domain: str, use_cache: bool = False) -> Dict:
"""
Información de consultas
Args:
domain: Dominio a consultar
use_cache: Usar resultados en cache o no
Respuesta:
Diccionario conteniendo la información del dominio
"""
endpoint = '/whoiscache' if use_cache else '/whois'
url = f"{self.base_url}{endpoint}"
try:
response = self.session.post(url, json={'domain': domain}, timeout=30)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
raise Exception(f"Falla en la peticion a la API: {str(e)}")
def lookup_get(self, domain: str, use_cache: bool = False) -> Dict:
"""Consulta usando el método GET"""
endpoint = '/whoiscache' if use_cache else '/whois'
url = f"{self.base_url}{endpoint}?domain={domain}"
try:
response = self.session.get(url, timeout=30)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
raise Exception(f"API request failed: {str(e)}")
def batch_lookup(self, domains: List[str], use_cache: bool = True) -> List[Dict]:
"""Consulta múltiples dominios"""
results = []
for domain in domains:
try:
result = self.lookup(domain, use_cache)
result['domain'] = domain
results.append(result)
except Exception as e:
results.append({
'domain': domain,
'error': True,
'message': str(e)
})
return results
def is_available(self, domain: str) -> bool:
"""Consultar si un dominio está disponible"""
try:
result = self.lookup(domain, use_cache=True)
return result.get('status') == 'available'
except:
return False
def get_expiration_date(self, domain: str) -> Optional[datetime]:
"""Obtener la fecha de expiración"""
try:
result = self.lookup(domain, use_cache=True)
if result.get('status') == 'success' and result.get('data'):
exp_date = result['data'].get('expiration_date')
if exp_date:
return datetime.fromisoformat(exp_date.replace('Z', '+00:00'))
except:
pass
return None
# Ejempĺos de uso
if __name__ == "__main__":
client = DomainLookupClient()
# Consulta de un dominio
try:
result = client.lookup('google.com', use_cache=True)
print(f"Dominio: google.com")
print(f"Estado: {result['status']}")
if result['status'] == 'success':
data = result['data']
print(f"Registrador: {data.get('registrar_name', 'N/A')}")
print(f"Creado: {data.get('creation_date', 'N/A')}")
print(f"Expira: {data.get('expiration_date', 'N/A')}")
print(f"Servidores DNS: {', '.join(data.get('dns_servers', []))}")
except Exception as e:
print(f"Error: {e}")
print("\n" + "="*50 + "\n")
# Consulta por lotes
domains = ['google.com', 'github.com', 'nonexistent12345.com']
results = client.batch_lookup(domains)
for result in results:
domain = result.get('domain', 'Unknown')
if result.get('error'):
print(f"{domain}: Error - {result.get('message', 'Error desconocido')}")
else:
status = result.get('status', 'Desconocido')
print(f"{domain}: {status}")
if status == 'success' and result.get('data'):
registrar = result['data'].get('registrar_name', 'N/A')
print(f" Registrador: {registrar}")
print("\n" + "="*50 + "\n")
# Consultar disponibilidad de dominio
test_domains = ['google.com', 'nonexistent12345.com']
for domain in test_domains:
available = client.is_available(domain)
print(f"{domain}: {'Disponible' if available else 'No Disponible'}")
print("\n" + "="*50 + "\n")
# Obtener fechas de expiración
for domain in ['google.com', 'github.com']:
exp_date = client.get_expiration_date(domain)
if exp_date:
print(f"{domain} expira: {exp_date.strftime('%Y-%m-%d')}")
else:
print(f"{domain}: No se pudo obtener la fecha de vencimiento o expiracion")
import aiohttp
import asyncio
import json
from typing import Dict, List
class AsyncDomainLookupClient:
def __init__(self, base_url: str = "https://api.latinapi.com/api"):
self.base_url = base_url.rstrip('/')
async def lookup(self, session: aiohttp.ClientSession, domain: str, use_cache: bool = False) -> Dict:
endpoint = '/whoiscache' if use_cache else '/whois'
url = f"{self.base_url}{endpoint}"
async with session.post(url, json={'domain': domain}) as response:
if response.status == 200:
return await response.json()
else:
error_data = await response.json()
raise Exception(error_data.get('message', f'HTTP {response.status}'))
async def batch_lookup(self, domains: List[str], use_cache: bool = True) -> List[Dict]:
async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=30)) as session:
tasks = []
for domain in domains:
task = self.lookup_with_error_handling(session, domain, use_cache)
tasks.append(task)
return await asyncio.gather(*tasks)
async def lookup_with_error_handling(self, session: aiohttp.ClientSession, domain: str, use_cache: bool) -> Dict:
try:
result = await self.lookup(session, domain, use_cache)
result['domain'] = domain
return result
except Exception as e:
return {
'domain': domain,
'error': True,
'message': str(e)
}
# Uso
async def main():
client = AsyncDomainLookupClient()
domains = ['google.com', 'github.com', 'stackoverflow.com', 'nonexistent12345.com']
results = await client.batch_lookup(domains)
for result in results:
domain = result.get('domain')
if result.get('error'):
print(f"{domain}: Error - {result.get('message')}")
else:
print(f"{domain}: {result.get('status')}")
# Ejecutar el ejemplo asíncrono
# asyncio.run(main())
<?php
class DomainLookupClient {
private $baseUrl;
private $timeout;
public function __construct($baseUrl = 'https://api.latinapi.com/api', $timeout = 30) {
$this->baseUrl = rtrim($baseUrl, '/');
$this->timeout = $timeout;
}
/**
* Información de consulta de dominio
*/
public function lookup($domain, $useCache = false) {
$endpoint = $useCache ? '/whoiscache' : '/whois';
$url = $this->baseUrl . $endpoint;
$data = json_encode(['domain' => $domain]);
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $data,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $this->timeout,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Content-Length: ' . strlen($data)
]
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
throw new Exception("cURL Error: $error");
}
$result = json_decode($response, true);
if ($httpCode !== 200) {
$message = $result['message'] ?? "HTTP Error $httpCode";
throw new Exception($message);
}
return $result;
}
/**
* Consulta usando el método GET
*/
public function lookupGet($domain, $useCache = false) {
$endpoint = $useCache ? '/whoiscache' : '/whois';
$url = $this->baseUrl . $endpoint . '?domain=' . urlencode($domain);
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $this->timeout,
CURLOPT_HTTPHEADER => [
'Accept: application/json'
]
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new Exception("HTTP Error $httpCode");
}
return json_decode($response, true);
}
/**
* Consulta por lotes de múltiples dominios
*/
public function batchLookup($domains, $useCache = true) {
$results = [];
foreach ($domains as $domain) {
try {
$result = $this->lookup($domain, $useCache);
$result['domain'] = $domain;
$results[] = $result;
} catch (Exception $e) {
$results[] = [
'domain' => $domain,
'error' => true,
'message' => $e->getMessage()
];
}
}
return $results;
}
/**
* Consultar si el dominio está disponible
*/
public function isAvailable($domain) {
try {
$result = $this->lookup($domain, true);
return $result['status'] === 'available';
} catch (Exception $e) {
return false;
}
}
/**
* Obtener la fecha de vencimiento o expiración
*/
public function getExpirationDate($domain) {
try {
$result = $this->lookup($domain, true);
if ($result['status'] === 'success' && isset($result['data']['expiration_date'])) {
return new DateTime($result['data']['expiration_date']);
}
} catch (Exception $e) {
// Ignore errors
}
return null;
}
}
// Ejemplos de uso
try {
$client = new DomainLookupClient();
// Consulta de un solo dominio
echo "=== Consulta de un solo dominio ===\n";
$result = $client->lookup('google.com', true);
echo "Dominio: google.com\n";
echo "Estado: " . $result['status'] . "\n";
echo "Tipo: " . $result['type'] . "\n";
echo "Cache: " . ($result['cached'] ? 'Si' : 'No') . "\n";
if ($result['status'] === 'success') {
$data = $result['data'];
echo "Registrador: " . ($data['registrar_name'] ?? 'N/A') . "\n";
echo "Creado: " . ($data['creation_date'] ?? 'N/A') . "\n";
echo "Expira: " . ($data['expiration_date'] ?? 'N/A') . "\n";
echo "Servidores DNS: " . implode(', ', $data['dns_servers'] ?? []) . "\n";
}
echo "\n=== Consulta por lotes ===\n";
$domains = ['google.com', 'github.com', 'nonexistent12345.com'];
$results = $client->batchLookup($domains);
foreach ($results as $result) {
$domain = $result['domain'];
if (isset($result['error'])) {
echo "$domain: Error - " . $result['message'] . "\n";
} else {
echo "$domain: " . $result['status'] . "\n";
}
}
echo "\n=== Consulta de disponibilidad ===\n";
$testDomains = ['google.com', 'nonexistent12345.com'];
foreach ($testDomains as $domain) {
$available = $client->isAvailable($domain);
echo "$domain: " . ($available ? 'Disponible' : 'No Disponible') . "\n";
}
echo "\n=== Fechas de Expiración o Vencimiento ===\n";
foreach (['google.com', 'github.com'] as $domain) {
$expDate = $client->getExpirationDate($domain);
if ($expDate) {
echo "$domain expira: " . $expDate->format('Y-m-d') . "\n";
} else {
echo "$domain: No se pudo obtener la fecha de vencimiento\n";
}
}
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
?>
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
public class DomainLookupClient {
private final String baseUrl;
private final HttpClient httpClient;
private final ObjectMapper objectMapper;
public DomainLookupClient(String baseUrl) {
this.baseUrl = baseUrl.replaceAll("/$", "");
this.httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
this.objectMapper = new ObjectMapper();
}
public DomainLookupClient() {
this("https://api.latinapi.com/api");
}
/**
* Información de consulta de dominio
*/
public DomainResult lookup(String domain, boolean useCache) throws Exception {
String endpoint = useCache ? "/whoiscache" : "/whois";
String url = baseUrl + endpoint;
// Crear carga JSON
String jsonPayload = objectMapper.writeValueAsString(
new DomainRequest(domain)
);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonPayload))
.timeout(Duration.ofSeconds(30))
.build();
HttpResponse<String> response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) {
JsonNode errorNode = objectMapper.readTree(response.body());
throw new Exception("API Error: " + errorNode.get("message").asText());
}
return objectMapper.readValue(response.body(), DomainResult.class);
}
/**
* Consulta usando GET
*/
public DomainResult lookupGet(String domain, boolean useCache) throws Exception {
String endpoint = useCache ? "/whoiscache" : "/whois";
String url = baseUrl + endpoint + "?domain=" + domain;
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Accept", "application/json")
.GET()
.timeout(Duration.ofSeconds(30))
.build();
HttpResponse<String> response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) {
throw new Exception("HTTP Error: " + response.statusCode());
}
return objectMapper.readValue(response.body(), DomainResult.class);
}
/**
* Consulta asíncrona por lotes
*/
public List<DomainResult> batchLookup(List<String> domains, boolean useCache) {
List<CompletableFuture<DomainResult>> futures = new ArrayList<>();
for (String domain : domains) {
CompletableFuture<DomainResult> future = CompletableFuture.supplyAsync(() -> {
try {
DomainResult result = lookup(domain, useCache);
result.setDomain(domain);
return result;
} catch (Exception e) {
DomainResult errorResult = new DomainResult();
errorResult.setDomain(domain);
errorResult.setError(true);
errorResult.setMessage(e.getMessage());
return errorResult;
}
});
futures.add(future);
}
return futures.stream()
.map(CompletableFuture::join)
.collect(java.util.stream.Collectors.toList());
}
/**
* Consulta disponibilidad de dominio
*/
public boolean isAvailable(String domain) {
try {
DomainResult result = lookup(domain, true);
return "available".equals(result.getStatus());
} catch (Exception e) {
return false;
}
}
// Data classes
public static class DomainRequest {
private String domain;
public DomainRequest(String domain) {
this.domain = domain;
}
public String getDomain() { return domain; }
public void setDomain(String domain) { this.domain = domain; }
}
public static class DomainResult {
private String domain;
private String status;
private String type;
private boolean cached;
private boolean error;
private String message;
private DomainData data;
// Getters and setters
public String getDomain() { return domain; }
public void setDomain(String domain) { this.domain = domain; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public boolean isCached() { return cached; }
public void setCached(boolean cached) { this.cached = cached; }
public boolean isError() { return error; }
public void setError(boolean error) { this.error = error; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public DomainData getData() { return data; }
public void setData(DomainData data) { this.data = data; }
}
public static class DomainData {
private String creation_date;
private String updated_date;
private String expiration_date;
private String registrar_name;
private List<String> status;
private List<String> dns_servers;
// Getters and setters
public String getCreation_date() { return creation_date; }
public void setCreation_date(String creation_date) { this.creation_date = creation_date; }
public String getUpdated_date() { return updated_date; }
public void setUpdated_date(String updated_date) { this.updated_date = updated_date; }
public String getExpiration_date() { return expiration_date; }
public void setExpiration_date(String expiration_date) { this.expiration_date = expiration_date; }
public String getRegistrar_name() { return registrar_name; }
public void setRegistrar_name(String registrar_name) { this.registrar_name = registrar_name; }
public List<String> getStatus() { return status; }
public void setStatus(List<String> status) { this.status = status; }
public List<String> getDns_servers() { return dns_servers; }
public void setDns_servers(List<String> dns_servers) { this.dns_servers = dns_servers; }
}
// Ejemplos de uso
public static void main(String[] args) {
DomainLookupClient client = new DomainLookupClient();
try {
// Consulta de un dominio
System.out.println("=== Consulta de un dominio ===");
DomainResult result = client.lookup("google.com", true);
System.out.println("Dominio: google.com");
System.out.println("Estado: " + result.getStatus());
System.out.println("Tipo: " + result.getType());
System.out.println("Cache: " + result.isCached());
if ("success".equals(result.getStatus()) && result.getData() != null) {
DomainData data = result.getData();
System.out.println("Registrador: " + data.getRegistrar_name());
System.out.println("Creado: " + data.getCreation_date());
System.out.println("Expira: " + data.getExpiration_date());
System.out.println("Servidores DNS: " + String.join(", ", data.getDns_servers()));
}
// Consulta por lotes
System.out.println("\n=== Consulta por lotes ===");
List<String> domains = List.of("google.com", "github.com", "nonexistent12345.com");
List<DomainResult> results = client.batchLookup(domains, true);
for (DomainResult r : results) {
if (r.isError()) {
System.out.println(r.getDomain() + ": Error - " + r.getMessage());
} else {
System.out.println(r.getDomain() + ": " + r.getStatus());
}
}
// Consulta de disponibilidad
System.out.println("\n=== Consulta de disponibilidad ===");
List<String> testDomains = List.of("google.com", "nonexistent12345.com");
for (String domain : testDomains) {
boolean available = client.isAvailable(domain);
System.out.println(domain + ": " + (available ? "Disponible" : "No Disponible"));
}
} catch (Exception e) {
System.err.println("Error: " + e.getMessage());
}
}
}
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"sync"
"time"
)
type DomainLookupClient struct {
BaseURL string
HTTPClient *http.Client
}
type DomainRequest struct {
Domain string `json:"domain"`
}
type DomainData struct {
CreationDate string `json:"creation_date"`
UpdatedDate string `json:"updated_date"`
ExpirationDate string `json:"expiration_date"`
RegistrarName string `json:"registrar_name"`
Status []string `json:"status"`
DNSServers []string `json:"dns_servers"`
}
type DomainResult struct {
Domain string `json:"domain,omitempty"`
Status string `json:"status"`
Type string `json:"type"`
Cached bool `json:"cached"`
Message string `json:"message,omitempty"`
Data *DomainData `json:"data,omitempty"`
Error bool `json:"error,omitempty"`
}
func NewDomainLookupClient(baseURL string) *DomainLookupClient {
if baseURL == "" {
baseURL = "https://api.latinapi.com/api"
}
return &DomainLookupClient{
BaseURL: strings.TrimRight(baseURL, "/"),
HTTPClient: &http.Client{
Timeout: 30 * time.Second,
},
}
}
func (c *DomainLookupClient) Lookup(domain string, useCache bool) (*DomainResult, error) {
endpoint := "/whois"
if useCache {
endpoint = "/whoiscache"
}
url := c.BaseURL + endpoint
reqBody := DomainRequest{Domain: domain}
jsonData, err := json.Marshal(reqBody)
if err != nil {
return nil, fmt.Errorf("failed to marshal request: %w", err)
}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
resp, err := c.HTTPClient.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
var result DomainResult
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("API error: %s", result.Message)
}
return &result, nil
}
func (c *DomainLookupClient) LookupGet(domain string, useCache bool) (*DomainResult, error) {
endpoint := "/whois"
if useCache {
endpoint = "/whoiscache"
}
url := fmt.Sprintf("%s%s/%s", c.BaseURL, endpoint, url.PathEscape(domain))
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Accept", "application/json")
resp, err := c.HTTPClient.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
var result DomainResult
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("HTTP error %d: %s", resp.StatusCode, result.Message)
}
return &result, nil
}
func (c *DomainLookupClient) BatchLookup(domains []string, useCache bool) []*DomainResult {
var wg sync.WaitGroup
results := make([]*DomainResult, len(domains))
for i, domain := range domains {
wg.Add(1)
go func(index int, d string) {
defer wg.Done()
result, err := c.Lookup(d, useCache)
if err != nil {
result = &DomainResult{
Domain: d,
Error: true,
Message: err.Error(),
}
} else {
result.Domain = d
}
results[index] = result
}(i, domain)
}
wg.Wait()
return results
}
func (c *DomainLookupClient) IsAvailable(domain string) bool {
result, err := c.Lookup(domain, true)
if err != nil {
return false
}
return result.Status == "available"
}
func (c *DomainLookupClient) GetExpirationDate(domain string) (time.Time, error) {
result, err := c.Lookup(domain, true)
if err != nil {
return time.Time{}, err
}
if result.Status == "success" && result.Data != nil && result.Data.ExpirationDate != "" {
return time.Parse(time.RFC3339, result.Data.ExpirationDate)
}
return time.Time{}, fmt.Errorf("no expiration date available")
}
func main() {
client := NewDomainLookupClient("https://api.latinapi.com/api")
// Consulta de un dominio
fmt.Println("=== Consulta de un dominio ===")
result, err := client.Lookup("google.com", true)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Dominio: google.com\n")
fmt.Printf("Estado: %s\n", result.Status)
fmt.Printf("Tipo: %s\n", result.Type)
fmt.Printf("Cache: %t\n", result.Cached)
if result.Status == "success" && result.Data != nil {
fmt.Printf("Registrador: %s\n", result.Data.RegistrarName)
fmt.Printf("Creado: %s\n", result.Data.CreationDate)
fmt.Printf("Expira: %s\n", result.Data.ExpirationDate)
fmt.Printf("Servidores DNS: %s\n", strings.Join(result.Data.DNSServers, ", "))
}
// Consulta por lotes
fmt.Println("\n=== Consulta por lotes ===")
domains := []string{"google.com", "github.com", "nonexistent12345.com"}
results := client.BatchLookup(domains, true)
for _, r := range results {
if r.Error {
fmt.Printf("%s: Error - %s\n", r.Domain, r.Message)
} else {
fmt.Printf("%s: %s\n", r.Domain, r.Status)
}
}
// Consulta de disponibilidad
fmt.Println("\n=== Consulta de disponibilidad ===")
testDomains := []string{"google.com", "nonexistent12345.com"}
for _, domain := range testDomains {
available := client.IsAvailable(domain)
status := "No Disponible"
if available {
status = "Disponible"
}
fmt.Printf("%s: %s\n", domain, status)
}
// Fechas de vencimiento o expiracion
fmt.Println("\n=== Fechas de vencimiento o expiracion ===")
for _, domain := range []string{"google.com", "github.com"} {
expDate, err := client.GetExpirationDate(domain)
if err != nil {
fmt.Printf("%s: No se pudo obtener la fecha de vencimiento - %v\n", domain, err)
} else {
fmt.Printf("%s expira: %s\n", domain, expDate.Format("2006-01-02"))
}
}
}
| Código HTTP | Código de Error | Descripción |
|---|---|---|
| 400 | INVALID_DOMAIN |
El formato del dominio es inválido |
| 404 | ENDPOINT_NOT_FOUND |
Endpoint de API inválido |
| 429 | RATE_LIMIT_EXCEEDED |
Demasiadas peticiones |
| 500 | DB_CONNECTION_ERROR |
Error en conexión a la base de datos |
| 500 | INTERNAL_ERROR |
Error interno |
{
"error": true,
"message": "Mensaje de error en lenguaje humano",
"http_code": 400,
"error_code": "CODIGO_LEGIBLE_POR_SISTEMAS"
}try {
const result = await fetch('/whois?domain=dominio-invalido');
const data = await result.json();
if (data.error) {
console.error(`Error ${data.error_code}: ${data.message}`);
}
} catch (error) {
console.error('Error de red:', error.message);
}try:
result = client.lookup('dominio-invalido')
except requests.exceptions.HTTPError as e:
if e.response.status_code == 400:
error_data = e.response.json()
print(f"Error de validación: {error_data['message']}")
elif e.response.status_code == 429:
print("Máximo de peticiones alcanzado. Por favor espera antes de reintentar.")
except requests.exceptions.RequestException as e:
print(f"Error de red: {e}")Esta API implementa límite de peticiones para evitar abusos
# Usa /whoiscache para mejorar el desempeño y disminuir el consumo de créditos
curl "https://api.latinapi.com/api/whoiscache?domain=google.com"def safe_domain_lookup(client, domain):
try:
return client.lookup(domain, use_cache=True)
except requests.exceptions.Timeout:
return {"error": True, "message": "Tiempo de espera agotado"}
except requests.exceptions.ConnectionError:
return {"error": True, "message": "Error de conexión"}
except Exception as e:
return {"error": True, "message": str(e)}function isValidDomain(domain) {
const domainRegex = /^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?\.[a-zA-Z]{2,}$/;
return domainRegex.test(domain) && domain.length <= 255;
}
async function lookupDomainSafe(domain) {
if (!isValidDomain(domain)) {
throw new Error('Formato de dominio inválido');
}
return await lookupDomain(domain);
}
client := &http.Client{
Timeout: 30 * time.Second,
}
// Para operaciones por lotes incrementa el tiempo de espera
batchClient := &http.Client{
Timeout: 60 * time.Second,
}