DoH JSON API

Query ResolveDB names over HTTPS using a Google-style JSON DNS response.

Endpoint

GET https://doh.resolvedb.io/resolve

The equivalent parameter-selected route is https://doh.resolvedb.io/dns-query?name=....

ResolveDB is authoritative-only. These endpoints resolve ResolveDB qnames, not arbitrary Internet domains.

Request

curl --get https://doh.resolvedb.io/resolve \
  --data-urlencode "name=get.newyork.weather.public.v1.resolvedb.net" \
  --data-urlencode "type=TXT"
ParameterRequiredDefaultBehavior
nameYes-ResolveDB qname, maximum 253 characters
typeNoADNS type name or number; use TXT for UQRP data
cdNofalseCopied to the DNS checking-disabled flag
edns_client_subnetNo-Echoed in the JSON response; not used for routing
doNo-Accepted for compatibility but currently ignored
random_paddingNo-Accepted and ignored

Response

{
  "Status": 0,
  "TC": false,
  "RD": true,
  "RA": false,
  "AD": false,
  "CD": false,
  "Question": [
    { "name": "get.newyork.weather.public.v1.resolvedb.net", "type": 16 }
  ],
  "Answer": [
    {
      "name": "get.newyork.weather.public.v1.resolvedb.net",
      "type": 16,
      "TTL": 300,
      "data": "\"v=rdb1;s=ok;t=data;...\""
    }
  ]
}

Status is the DNS RCODE. ResolveDB normally represents an unknown name as NOERROR with no answers (NODATA), not NXDOMAIN. RA is false because the service is authoritative, not recursive.

All JSON responses use Cache-Control: no-store. Use DNS wire transport when you need HTTP caching based on the DNS TTL.

Parsing TXT Data

Answer[].data uses DNS presentation format. A TXT record can contain multiple quoted character strings. Unquote and concatenate them before parsing the UQRP envelope. Then decode d according to e.

This example reads the operator-managed, public-read Hooli demo record. Hooli namespaces are read-only examples and cannot be claimed by customers.

function decodeTxtPresentation(value) {
  const strings = value.match(/"(?:\\.|[^"\\])*"/g) ?? [];
  return strings.map((part) => JSON.parse(part)).join('');
}

function parseUqrpJson(txt) {
  const dataMarker = ';d=';
  const markerIndex = txt.indexOf(dataMarker);
  if (markerIndex === -1) throw new Error('Missing UQRP data field');

  const metadata = Object.fromEntries(
    txt.slice(0, markerIndex).split(';').map((field) => field.split('=', 2)),
  );
  let payload = txt.slice(markerIndex + dataMarker.length);
  if (metadata.e === 'b64') {
    payload = atob(payload);
  }
  return JSON.parse(payload);
}

const name = 'get.dark-mode.flags.hooli.v1.resolvedb.net';
const response = await fetch(
  `https://doh.resolvedb.io/resolve?name=${encodeURIComponent(name)}&type=TXT`,
);
const dns = await response.json();
if (dns.Status !== 0 || !dns.Answer?.length) {
  throw new Error(dns.Comment || 'No answer');
}

const txt = decodeTxtPresentation(dns.Answer[0].data);
console.log(parseUqrpJson(txt));
// { enabled: true, variant: "default" }

Private hosted records use the same response format but require an auth-rdbq... label. Treat the qname as a bearer credential and use HTTPS.

Status Codes

DNS statusMeaning
0NOERROR: answer or NODATA
1FORMERR: malformed query
2SERVFAIL: service or storage failure
5REFUSED: authorization or namespace denial

Validation errors return HTTP 400 with a static JSON error. Valid DNS queries, including DNS-level errors, return HTTP 200. The JSON endpoint does not emit plan-based X-RateLimit-* headers.

CORS

The endpoint permits browser requests from any origin.

Next Steps