Skip to content

You can ask for any DNS record over HTTP and get JSON back. No dig, no resolver library, nothing to parse. This page goes from the first request to a small function you can drop into a project, using the DNS Lookup API.

Your first lookup

You need an API key in the X-API-Key header. It's free, sign up here.

curl -G "https://apixies.io/api/v1/dns-lookup" \
     -H "X-API-Key: YOUR_API_KEY" \
     --data-urlencode "domain=github.com" \
     --data-urlencode "type=A"
{
  "status": "success",
  "http_code": 200,
  "code": "SUCCESS",
  "message": "DNS lookup successful",
  "data": {
    "domain": "github.com",
    "records": [
      { "type": "A", "host": "github.com", "ttl": 50, "ip": "140.82.121.3" }
    ],
    "record_count": 1,
    "types_queried": ["A"],
    "timed_out": { "A": false },
    "partial": false,
    "queried_at": "2026-09-19T00:19:36+00:00"
  }
}

Two parameters, that's all there is.

Parameter Required What it does
domain yes The name to look up. A full URL works too, the host is pulled out of it.
type no One of A, AAAA, CNAME, MX, NS, TXT, SOA, SRV, CAA, PTR. Upper or lower case.

Leave the type out

Without type you don't get A records only. You get the six common types in one call: A, AAAA, MX, TXT, NS and CNAME. They're looked up at the same time, so it isn't slower than asking for one.

curl -G "https://apixies.io/api/v1/dns-lookup" \
     -H "X-API-Key: YOUR_API_KEY" \
     --data-urlencode "domain=github.com"

I got ten records back: one A, one MX (github-com.mail.protection.outlook.com, so their mail runs on Microsoft) and eight NS records split between NS1 and AWS.

That's one request against your daily limit, not six. If you want the overall picture of a domain, this is the cheap way to get it.

Look at partial when you do this. If one of the six lookups didn't come back in time, you still get the rest, partial is true, and timed_out tells you which type is missing:

"timed_out": { "A": false, "AAAA": false, "MX": false, "TXT": true, "NS": false, "CNAME": false },
"partial": true

An empty list for a type and a timed out type aren't the same thing. Check before you decide a domain has no TXT records.

What a record looks like

Every record has type, host and ttl. The rest depends on the type, and it's worth knowing before you write code against it, because the value isn't always in the same field.

Type Extra fields
A ip
AAAA ipv6
CNAME, NS, PTR target
MX priority, target
TXT txt
SOA mname, rname, serial, refresh, retry, expire, minimum_ttl
SRV priority, weight, port, target
CAA flags, tag, value

Mail servers for gmail.com:

"records": [
  { "type": "MX", "host": "gmail.com", "ttl": 2648, "priority": 5, "target": "gmail-smtp-in.l.google.com" },
  { "type": "MX", "host": "gmail.com", "ttl": 2648, "priority": 10, "target": "alt1.gmail-smtp-in.l.google.com" },
  { "type": "MX", "host": "gmail.com", "ttl": 2648, "priority": 20, "target": "alt2.gmail-smtp-in.l.google.com" },
  { "type": "MX", "host": "gmail.com", "ttl": 2648, "priority": 30, "target": "alt3.gmail-smtp-in.l.google.com" },
  { "type": "MX", "host": "gmail.com", "ttl": 2648, "priority": 40, "target": "alt4.gmail-smtp-in.l.google.com" }
]

The ttl is what's left on the answer in the resolver's cache, in seconds. So it counts down between calls, and a small number doesn't mean the domain owner set a short TTL.

Things that'll trip you up

Only MX records are sorted. They come back by priority, lowest first, the order mail is tried in. Everything else is in whatever order the resolver gave, and DNS doesn't promise one. Don't build on records[0] for A or NS records.

An A lookup can return a CNAME. www.github.com is an alias, so asking for its A record gives you both steps:

"records": [
  { "type": "CNAME", "host": "www.github.com", "ttl": 1025, "target": "github.com" },
  { "type": "A", "host": "github.com", "ttl": 32, "ip": "140.82.121.4" }
]

If your code does records[0].ip, it breaks here. Filter by type first.

A domain that doesn't exist is still a 200. I looked up this-domain-does-not-exist-zz81.com and got "status": "success" with "record_count": 0. No records is an answer, not an error. What tells the two cases apart is exists. It's false when the resolver says the name isn't there at all, and true for a real domain that just has no records of that type.

Some records live on underscore names. DMARC is a TXT record on _dmarc.gmail.com, a DKIM key sits on selector._domainkey.example.com, and SRV names look like _imaps._tcp.gmail.com. Look those names up directly. If it's mail setup you're after, the email authentication endpoint finds and reads SPF, DKIM and DMARC in one call.

A 504 means try again. The code is UPSTREAM_TIMEOUT: the resolver didn't answer in time. Retry once before you give up on the domain.

In code

One small function per language. Each returns the list of records and throws on anything that isn't a success.

JavaScript

async function dnsLookup(domain, type) {
  const params = new URLSearchParams(type ? { domain, type } : { domain });
  const res = await fetch(`https://apixies.io/api/v1/dns-lookup?${params}`, {
    headers: { "X-API-Key": process.env.APIXIES_API_KEY },
  });
  const body = await res.json();

  if (body.status !== "success") {
    throw new Error(`${body.code}: ${body.message}`);
  }
  return body.data.records;
}

const mx = await dnsLookup("gmail.com", "MX");
console.log(mx[0].target); // gmail-smtp-in.l.google.com

Python

import os
import requests

def dns_lookup(domain, record_type=None):
    params = {"domain": domain}
    if record_type:
        params["type"] = record_type

    res = requests.get(
        "https://apixies.io/api/v1/dns-lookup",
        params=params,
        headers={"X-API-Key": os.environ["APIXIES_API_KEY"]},
        timeout=15,
    )
    body = res.json()
    if body["status"] != "success":
        raise RuntimeError(f"{body['code']}: {body['message']}")
    return body["data"]["records"]

for record in dns_lookup("gmail.com", "TXT"):
    print(record["txt"])

PHP

function dnsLookup(string $domain, ?string $type = null): array
{
    $query = http_build_query(array_filter(['domain' => $domain, 'type' => $type]));
    $context = stream_context_create(['http' => [
        'header' => 'X-API-Key: ' . getenv('APIXIES_API_KEY'),
        'ignore_errors' => true,
    ]]);

    $body = json_decode(file_get_contents("https://apixies.io/api/v1/dns-lookup?$query", false, $context), true);

    if (($body['status'] ?? '') !== 'success') {
        throw new RuntimeException(($body['code'] ?? 'ERROR') . ': ' . ($body['message'] ?? 'no response'));
    }

    return $body['data']['records'];
}

$ips = array_column(
    array_filter(dnsLookup('github.com', 'A'), fn ($r) => $r['type'] === 'A'),
    'ip'
);
print_r($ips);

A real use: did the customer point their domain at you?

If you host things on customer domains (status pages, shops, docs), you need to know when their DNS is ready. That's one CNAME lookup. GitHub's status page is a customer of Statuspage, and it shows:

curl -G "https://apixies.io/api/v1/dns-lookup" \
     -H "X-API-Key: YOUR_API_KEY" \
     --data-urlencode "domain=www.githubstatus.com" \
     --data-urlencode "type=CNAME"
"records": [
  { "type": "CNAME", "host": "www.githubstatus.com", "ttl": 870, "target": "kctbh9vrtdwd.stspg-customer.com" }
]

Your version of that check:

async function pointsAtUs(customerDomain) {
  const records = await dnsLookup(customerDomain, "CNAME");
  return records.some((r) => r.target.endsWith(".your-platform.example"));
}

Run it when the customer clicks "verify", not on a timer for every customer. The free tier is 75 requests a day. That's plenty for checks a person triggers, and not much for polling.

One thing this can't tell you is whether a change has reached the rest of the world yet. The lookup goes through one resolver. For that there's the DNS propagation endpoint, which asks several public resolvers and compares.

Next steps

Try the DNS Lookup API

Free tier is for development & small projects. 75 requests/day with a registered account.

cookies

We use analytics cookies to see how the site gets used. Nothing loads until you accept. Privacy policy