DNS Lookup API
DNS records for a domain (A, AAAA, MX, TXT, NS, CNAME, SOA, CAA) with their TTLs.
A DNS query needs the network, so an agent cannot answer it on its own. It is what you run in a loop over a list of domains.
01Request
HTTP request
POST /v1/dns-lookup
content-type: application/json
{
"domain": "example.com",
"types": [
"a",
"mx"
]
}02Call it from code
JavaScript
// dns-lookup: free, no payment needed
// Save as client.mjs (ES module, Node 18+) and run: node client.mjs
const body = {
"domain": "example.com",
"types": [
"a",
"mx"
]
}
const res = await fetch('https://grist.tools/v1/dns-lookup', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(body),
})
console.log(res.status, await res.json())Python
# dns-lookup: free, no payment needed
# Install: pip install requests
import requests
payload = {
"domain": "example.com",
"types": [
"a",
"mx"
]
}
res = requests.post("https://grist.tools/v1/dns-lookup", json=payload)
print(res.status_code, res.json())curl
# dns-lookup: free, no payment needed
curl -X POST 'https://grist.tools/v1/dns-lookup' -H 'content-type: application/json' -d '{"domain":"example.com","types":["a","mx"]}'03Response
HTTP response
200 OK
{
"domain": "example.com",
"types_requested": [
"a",
"mx"
],
"records": [
{
"type": "a",
"values": [
{
"address": "93.184.215.14",
"ttl": 3600
}
],
"truncated": false,
"resolution_error": null
},
{
"type": "mx",
"values": [
{
"exchange": "mail.example.com",
"priority": 10
}
],
"truncated": false,
"resolution_error": null
}
],
"queried_at": "2026-08-26T18:00:00.000Z"
}This example is illustrative: it shows the exact shape the handler returns, but it was not produced by a reproducible call, because the real answer depends on live network data that changes over time.
04Input schema
The same schema the handler validates against, published from the same source. Also served, free, at/v1/dns-lookup/schema.
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"domain": {
"description": "Fully qualified domain name to resolve, up to 253 characters; it is trimmed, lowercased and stripped of trailing dots, and IP literals, single-label names and private suffixes such as .local or .internal are refused.",
"type": "string",
"minLength": 1,
"maxLength": 253
},
"types": {
"description": "Record types to query, from a, aaaa, mx, txt, ns, cname, soa and caa in either case; duplicates collapse to one query per type, answers come back in that fixed order, and the default is all eight.",
"minItems": 1,
"maxItems": 16,
"type": "array",
"items": {
"type": "string",
"enum": [
"a",
"aaaa",
"mx",
"txt",
"ns",
"cname",
"soa",
"caa",
"A",
"AAAA",
"MX",
"TXT",
"NS",
"CNAME",
"SOA",
"CAA"
]
}
}
},
"required": [
"domain"
],
"additionalProperties": false
}05Errors
| Code | HTTP | When |
|---|---|---|
| invalid_input | 400 | The body does not match the schema on this page. Nothing is charged. |
| unreachable_target | 424 | The destination you named did not resolve or refused the connection. |
| upstream_timeout | 424 | The destination you named did not answer before the declared timeout for this endpoint expired. |
| rate_limited | 429 | Too many calls from the same payer, or too many calls towards the same destination domain. Nothing is charged. |
| concurrency_limit, registry_at_capacity, upstream_unavailable | 503 | We could not serve the call right now. Retryable: wait the number of seconds in the retry-after header, then repeat the call. concurrency_limit and registry_at_capacity mean we were at capacity: the call did not run. You can send the same payment again if its validBefore is still far enough away to cover another call. If it is not, the retry gets a 402 authorization_validity_too_short: sign a new payment. upstream_unavailable means our internal service failed or did not answer in time: the call may already have run and the payment may have been settled. A retry with the same payment can get a 402 whose error field says why. authorization_validity_too_short: too little time is left before validBefore to cover another call. This check comes before the replay check, so it does not tell whether the first payment was settled. replay_consumed (the payment was used and may have been settled) or replay_exhausted (too many attempts): sign a new payment. replay_in_flight: the first call is still running, so wait and do not pay again. invalid_exact_evm_nonce_already_used: the transfer is already on-chain, so this payment cannot move again. Sign a new payment. An authorization can be settled only once, since its nonce is single-use. To see whether it was settled, read the EIP-3009 authorizationState of its nonce on-chain. |
| misconfigured | 503 | The service is not configured to serve calls. The call did not run and nothing is charged. It needs a fix on our side, so there is no retry delay to wait for. |
06Questions
- What does a call to dns-lookup cost?
- Nothing. It is a free endpoint: call it without a payment header.
- How long can a call take?
- The handler has 5 s to answer.
- What happens if the call fails?
- It answers a typed JSON error. The errors table in section 05 lists each code, its HTTP status and when it is raised.
- Where is the input schema?
- In section 04 on this page, and served free at /v1/dns-lookup/schema. It is the schema the handler validates against, also published in /openapi.json.