Référence développeur

Comparaison des résolveurs DNS

API DomScan : Compare les réponses DNS de deux fournisseurs publics de résolution récursive interrogés indépendamment via DoH : Cloudflare et Google.

Comparaison des résolveurs DNS

Compare les réponses DNS de deux fournisseurs publics de résolution récursive interrogés indépendamment via DoH : Cloudflare et Google. Tous deux utilisent l’anycast mondial. Les résultats sont des observations de ces fournisseurs depuis le chemin de la requête, et non des sondes géographiques, des requêtes auprès des serveurs faisant autorité ou une preuve de propagation mondiale.

GET /v1/dns/propagation

Paramètres de requête

ParamètreTypeDescription
domain requis string Domaine à interroger auprès de chaque résolveur configuré (par exemple, "example.com")
type optionnel string Type d’enregistrement à comparer : A, AAAA, CNAME, MX, TXT, NS, SOA (A par défaut)
expected optionnel string Valeur d’enregistrement attendue facultative utilisée comme base de comparaison (par exemple, une nouvelle adresse IP)

Champs de réponse

ChampTypeDescription
propagation_percentagenumberSans expected : taille du plus grand groupe de réponses identiques divisée par le nombre total de résolveurs configurés. Avec expected : correspondances exactes avec la valeur attendue divisées par le nombre total de résolveurs configurés (0-100)
fully_propagatedbooleanVrai lorsque tous les résolveurs configurés convergent ou, si expected est fourni, lorsqu’ils correspondent tous à cette valeur
consistentbooleanVrai uniquement lorsque chaque résolveur configuré répond correctement et renvoie le même ensemble canonique de réponses
unique_valuesarrayValeurs d’enregistrement uniques renvoyées par les résolveurs ayant répondu correctement
resultsarrayFournisseur, enregistrements, TTL observé, temps de réponse depuis le chemin de requête et erreurs pour chaque résolveur

Exemple de requête

# Compare A records across configured resolvers
curl -H "X-API-Key: your-api-key" "https://domscan.net/v1/dns/propagation?domain=example.com&type=A"

# Compare MX answers against an expected value
curl -H "X-API-Key: your-api-key" "https://domscan.net/v1/dns/propagation?domain=example.com&type=MX&expected=mail.example.com"
const domscanFetch = (url, options = {}) =>
  fetch(url, {
    ...options,
    headers: { ...options.headers, "X-API-Key": "your-api-key" },
  });

const url = new URL("https://domscan.net/v1/dns/propagation");
url.searchParams.set("domain", "example.com");
url.searchParams.set("type", "A");
url.searchParams.set("expected", "203.0.113.42");

const response = await domscanFetch(url);
const data = await response.json();

// Legacy response field names are retained for API compatibility.
console.log(`Resolver convergence: ${data.propagation_percentage}%`);
console.log(`All configured resolvers match: ${data.fully_propagated}`);

// Check which servers are still showing old values
data.results
  .filter(r => !r.success || !r.records.includes(data.expected_value))
  .forEach(r => console.log(`${r.server.name}: ${r.records}`));
import requests

domscan = requests.Session()
domscan.headers.update({"X-API-Key": "your-api-key"})

response = domscan.get(
    "https://domscan.net/v1/dns/propagation",
    params={"domain": "example.com", "type": "A"}
)
data = response.json()

# Legacy response field names are retained for API compatibility.
print(f"Resolver convergence: {data['propagation_percentage']}%")
print(f"All configured resolvers match: {data['fully_propagated']}")

# Show servers with different values
for result in data['results']:
    print(f"{result['server']['name']}: {result['records']}")
package main

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

func domscanGet(url string) (*http.Response, error) {
  request, err := http.NewRequest(http.MethodGet, url, nil)
  if err != nil {
    return nil, err
  }
  request.Header.Set("X-API-Key", os.Getenv("DOMSCAN_API_KEY"))
  return http.DefaultClient.Do(request)
}

func main() {
  resp, _ := domscanGet("https://domscan.net/v1/dns/propagation?domain=example.com&type=A")
  defer resp.Body.Close()

  var data map[string]interface{}
  json.NewDecoder(resp.Body).Decode(&data)

  // Legacy response field names are retained for API compatibility.
  fmt.Printf("Resolver convergence: %.0f%%\n", data["propagation_percentage"])
  fmt.Printf("All configured resolvers match: %v\n", data["fully_propagated"])
}
require 'net/http'
require 'json'

uri = URI("https://domscan.net/v1/dns/propagation?domain=example.com&type=A")
request = Net::HTTP::Get.new(uri)
request["X-API-Key"] = ENV.fetch("DOMSCAN_API_KEY")
response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") { |http| http.request(request) }
data = JSON.parse(response.body)

# Legacy response field names are retained for API compatibility.
puts "Resolver convergence: #{data['propagation_percentage']}%"
puts "All configured resolvers match: #{data['fully_propagated']}"

Exemple de réponse

{
  "domain": "example.com",
  "record_type": "A",
  "measurement_scope": "configured_recursive_resolvers",
  "percentage_basis": "resolver_convergence",
  "propagation_percentage": 100,
  "fully_propagated": true,
  "consistent": true,
  "unique_values": ["93.184.216.34"],
  "results": [
    {
      "server": {
        "name": "Cloudflare 1.1.1.1",
        "ip": "1.1.1.1",
        "provider": "Cloudflare",
        "location": "Global anycast",
        "country": "GLOBAL",
        "scope": "global-anycast",
        "doh_endpoint": "https://cloudflare-dns.com/dns-query"
      },
      "success": true,
      "records": ["93.184.216.34"],
      "ttl": 86400,
      "response_time_ms": 12
    },
    {
      "server": {
        "name": "Google Public DNS",
        "ip": "8.8.8.8",
        "provider": "Google",
        "location": "Global anycast",
        "country": "GLOBAL",
        "scope": "global-anycast",
        "doh_endpoint": "https://dns.google/resolve"
      },
      "success": true,
      "records": ["93.184.216.34"],
      "ttl": 86400,
      "response_time_ms": 15
    }
  ],
  "summary": {
    "total_servers": 2,
    "successful": 2,
    "failed": 0
  }
}
GET /v1/dns/servers

Répertorie les deux fournisseurs publics de résolution DNS récursive via DoH utilisés pour la comparaison : Cloudflare et Google. Tous deux utilisent l’anycast mondial. Ce ne sont ni des sondes géographiques ni des serveurs de noms faisant autorité.

Exemple de réponse

{
  "measurement_scope": "configured_recursive_resolvers",
  "geographic_vantage": false,
  "servers": [
    {
      "name": "Cloudflare 1.1.1.1",
      "ip": "1.1.1.1",
      "provider": "Cloudflare",
      "location": "Global anycast",
      "country": "GLOBAL",
      "scope": "global-anycast",
      "doh_endpoint": "https://cloudflare-dns.com/dns-query"
    },
    {
      "name": "Google Public DNS",
      "ip": "8.8.8.8",
      "provider": "Google",
      "location": "Global anycast",
      "country": "GLOBAL",
      "scope": "global-anycast",
      "doh_endpoint": "https://dns.google/resolve"
    }
  ],
  "total": 2
}

Champs de réponse

Champ Type
measurement_scope string
geographic_vantage boolean
servers[] object[]
servers[] object
servers[].name string
servers[].location string
servers[].country string
servers[].ip string
servers[].provider string
servers[].scope string
servers[].doh_endpoint string
servers[].documentation_url string
servers[].format string
total integer
meta object
meta.served_by string
POST /v1/dns/propagation/bulk Crédits: 2/item

Paramètres de requête

Paramètre Type requis
dry_run string
Valeurs autorisées true
optionnel

Paramètres du corps

Paramètre Type requis
domains string[] requis
type string
Valeurs autorisées AAAAACNAMEMXTXTNSSOA
Défaut A
optionnel
expected string optionnel

Champs de réponse

Champ Type
results[] unknown[]
meta object
meta.total integer
meta.succeeded integer
meta.failed integer
meta.max_items integer
meta.credits_per_item integer
meta.duration_ms integer

Exemple de requête

curl -X POST "https://domscan.net/v1/dns/propagation/bulk" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $DOMSCAN_API_KEY" \
  -d '{
  "domains": [
    "example.com",
    "cloudflare.com"
  ],
  "type": "A"
}'
const response = await fetch("https://domscan.net/v1/dns/propagation/bulk", {
  method: "POST",
  headers: { "X-API-Key": process.env.DOMSCAN_API_KEY, "Content-Type": "application/json" },
  body: JSON.stringify({
    "domains": [
      "example.com",
      "cloudflare.com"
    ],
    "type": "A"
  }),
});
const data = await response.json();
console.log(data);
import os
import requests

response = requests.post(
    "https://domscan.net/v1/dns/propagation/bulk",
    headers={"X-API-Key": os.environ["DOMSCAN_API_KEY"]},
    json={
        "domains": [
            "example.com",
            "cloudflare.com",
        ],
        "type": "A",
    },
)
print(response.json())
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
)

func main() {
	body := strings.NewReader(`{
	  "domains": [
	    "example.com",
	    "cloudflare.com"
	  ],
	  "type": "A"
	}`)
	req, err := http.NewRequest(http.MethodPost, "https://domscan.net/v1/dns/propagation/bulk", body)
	if err != nil {
		panic(err)
	}
	req.Header.Set("X-API-Key", os.Getenv("DOMSCAN_API_KEY"))
	req.Header.Set("Content-Type", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	data, err := io.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}
	fmt.Println(resp.Status, string(data))
}
require "json"
require "net/http"

uri = URI("https://domscan.net/v1/dns/propagation/bulk")
request = Net::HTTP::Post.new(uri)
request["X-API-Key"] = ENV.fetch("DOMSCAN_API_KEY")
request["Content-Type"] = "application/json"
request.body = JSON.generate({
  "domains" => [
    "example.com",
    "cloudflare.com"
  ],
  "type" => "A"
})

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end
puts JSON.pretty_generate(JSON.parse(response.body))

Exemple de réponse

{
  "results": [
    {
      "input": "example",
      "data": {}
    }
  ],
  "meta": {
    "total": 1,
    "succeeded": 1,
    "failed": 1,
    "max_items": 10,
    "credits_per_item": 1,
    "duration_ms": 1
  }
}

Codes de statut HTTP

  • 200 OK
  • 400 Requête incorrecte
  • 401 Non autorisé
  • 402 Paiement requis
  • 409 HTTP 409
  • 422 HTTP 422
  • 429 Trop de requêtes