Developer Reference
DNS Resolver Comparison
Explore DNS Resolver Comparison API documentation, request parameters, response fields, code examples, and error handling for DomScan integrations.
DNS Resolver Comparison
Compare DNS record answers from two independently queried public recursive DoH providers: Cloudflare and Google. Both are global-anycast services, so results are provider observations from the Worker request path, not geographic probes, authoritative nameserver checks, or proof of worldwide propagation.
GET
/v1/dns/propagation
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| domain required | string | Domain to query through each configured resolver (e.g., "example.com") |
| type optional | string | Record type to compare: A, AAAA, CNAME, MX, TXT, NS, SOA (default: A) |
| expected optional | string | Optional expected record value to use as the comparison basis (e.g., a new IP address) |
Response Fields
| Field | Type | Description |
|---|---|---|
propagation_percentage | number | Without expected: largest identical-answer cohort divided by all configured resolvers. With expected: exact expected-value matches divided by all configured resolvers (0-100) |
fully_propagated | boolean | True when all configured resolvers converge, or when all match expected if it is supplied |
consistent | boolean | True only when every configured resolver succeeds and returns the same canonical answer set |
unique_values | array | Unique record values returned by successful resolvers |
results | array | Per-resolver provider, records, observed TTL, request-path response time, and errors |
Example Request
# 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']}"
Example Response
{
"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
List the two public recursive DoH providers used for comparison: Cloudflare and Google. Both are global-anycast resolvers, not geographic probes or authoritative nameservers.
Example Response
{
"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
}
Response Fields
| Field | 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 |
POST
/v1/dns/propagation/bulk
Body Parameters
| Parameter | Type | required |
|---|---|---|
| domains | string[] | required |
| type | string | optional |
| expected | string | optional |
Response Fields
| Field | 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 |
Example Request
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"
}'
Example Response
{
"results": [
null
],
"meta": {
"total": 1,
"succeeded": 1,
"failed": 1,
"max_items": 10,
"credits_per_item": 1,
"duration_ms": 1
}
}