resources background

Tutorial

How to Get DNS Records with an API (cURL Tutorial)

Written By Qasim, WhoisFreaks Team Published: September 14, 2026, Last Updated: September 14, 2026

Getting DNS records with an API returns a domain's full zone in one call: the A record pointing at its host, the MX records handling its mail, the NS records controlling it, and the TXT records proving ownership. This tutorial covers the WhoisFreaks DNS API end to end, using cURL: how to run a live lookup, read every field in the response, narrow to a single record type, resolve an IP, pull dated history, and handle the failures that break scripts in production.

You'll need an API key. If you don't have one yet, follow sign up and get your API key first

What a DNS Lookup Actually Returns

Every domain publishes a set of records in its DNS zone, and each record type answers a different question:

Record Answers Typical use
A Which IPv4 address does this domain resolve to? Finding the hosting server
AAAA Which IPv6 address does it resolve to? Checking IPv6 readiness
NS Which nameservers are authoritative for it? Identifying the DNS provider
MX Which mail servers accept its email? Identifying the email provider
TXT Arbitrary text: SPF, DKIM, DMARC, site verification Email authentication, ownership proof
CNAME Which name is this one an alias for? Following alias chains
SOA Who administers the zone, and how is it cached? Zone health, serial tracking
SPF Which servers may send mail as this domain? Anti-spoofing checks

A single API call returns all of them, so you rarely need more than one request per domain. This table is a working reference rather than an explanation; for how each record type is structured and why it exists, the complete DNS guide covers them individually.

Step 1: Get Your API Key

Sign in to the WhoisFreaks dashboard and open API Keys. Copy your primary key - it's the only credential the DNS API needs, passed as the apiKey query parameter.

WhoisFreaks dashboard API Keys page showing the primary key with a copy button

Step 2: Run Your First DNS Lookup

The live endpoint takes a domain name and a record type. Use type=all to get everything at once:

curl -L --max-time 3600 "https://api.whoisfreaks.com/v2.0/dns/live?apiKey=API_KEY&domainName=whoisfreaks.com&type=all"

Replace API_KEY with your key and whoisfreaks.com with the domain you're checking. That's the whole request: no headers, no request body, no authentication dance.

The parameters you'll actually use:

Parameter Required Value
apiKey Yes Your API key from the dashboard
domainName Yes* The domain to look up, without http:// or a trailing dot
ipAddress Yes* An IP address, for a PTR (reverse) record lookup
type Yes all, or a comma-separated list: A, AAAA, NS, MX, CNAME, SOA, TXT, SPF
format No JSON or XML. Defaults to JSON.
Supply domainName, ipAddress, or both; at least one is required.

type is not limited to one value. type=mx,ns returns just those two record sets, which is often the right middle ground between one type and the whole zone.

A live DNS lookup costs 1 credit per successful domain query; see the credit usage documentation.

Step 3: Read the Response

The response is JSON with a summary block followed by the records themselves:

{
  "queryTime": "2026-08-26 09:53:04",
  "domainName": "whoisfreaks.com",
  "dnsTypes": {
    "A": 1,
    "NS": 2,
    "SOA": 6,
    "MX": 15,
    "TXT": 16,
    "AAAA": 28,
    "SPF": 99
  },
  "dnsRecords": [
    {
      "name": "whoisfreaks.com",
      "type": 1,
      "dnsType": "A",
      "ttl": 300,
      "rawText": "whoisfreaks.com.\t300\tIN\tA\t188.114.96.0",
      "rRsetType": 1,
      "address": "188.114.96.0"
    },
    {
      "name": "whoisfreaks.com",
      "type": 2,
      "dnsType": "NS",
      "ttl": 21600,
      "rawText": "whoisfreaks.com.\t21600\tIN\tNS\talbert.ns.cloudflare.com.",
      "rRsetType": 2,
      "singleName": "albert.ns.cloudflare.com."
    },
    {
      "name": "whoisfreaks.com",
      "type": 6,
      "dnsType": "SOA",
      "ttl": 1800,
      "rawText": "whoisfreaks.com.\t1800\tIN\tSOA\talbert.ns.cloudflare.com. dns.cloudflare.com. 2412711383 10000 2400 604800 1800",
      "rRsetType": 6,
      "admin": "dns.cloudflare.com.",
      "host": "albert.ns.cloudflare.com.",
      "expire": 604800,
      "minimum": 1800,
      "refresh": 10000,
      "retry": 2400,
      "serial": 2412711383
    },
    {
      "name": "whoisfreaks.com",
      "type": 15,
      "dnsType": "MX",
      "ttl": 300,
      "rawText": "whoisfreaks.com.\t300\tIN\tMX\t0 whoisfreaks-com.mail.protection.outlook.com.",
      "rRsetType": 15,
      "target": "whoisfreaks-com.mail.protection.outlook.com.",
      "priority": 0
    },
    {
      "name": "whoisfreaks.com",
      "type": 16,
      "dnsType": "TXT",
      "ttl": 300,
      "rawText": "whoisfreaks.com.\t300\tIN\tTXT\t\"google-site-verification=s0lTTBfSePwOO7LWlSND2ulZUfz7h-BE15H2llWem2A\"",
      "rRsetType": 16,
      "strings": [
        "google-site-verification=s0lTTBfSePwOO7LWlSND2ulZUfz7h-BE15H2llWem2A"
      ]
    },
    {
      "name": "whoisfreaks.com",
      "type": 28,
      "dnsType": "AAAA",
      "ttl": 300,
      "rawText": "whoisfreaks.com.\t300\tIN\tAAAA\t2a06:98c1:3121:0:0:0:0:0",
      "rRsetType": 28,
      "address": "2a06:98c1:3121:0:0:0:0:0"
    },
    {
      "name": "whoisfreaks.com",
      "type": 99,
      "dnsType": "SPF",
      "ttl": 300,
      "rawText": "whoisfreaks.com.\t300\tIN\tSPF\t\"v=spf1 include:spf.protection.outlook.com include:_spf.whoisfreaks_com._d.easydmarc.pro -all\"",
      "rRsetType": 99,
      "strings": [
        "v=spf1 include:spf.protection.outlook.com include:_spf.whoisfreaks_com._d.easydmarc.pro -all"
      ]
    }
  ]
}

Here's what each top-level field tells you:

Field Meaning
queryTime When the records were resolved, in UTC. DNS changes, so this timestamp matters.
domainName The domain that was queried, echoed back.
ipAddress Present when you queried by IP. The address the PTR lookup was run against.
dnsTypes A map of every record type present, keyed by type name, with its IANA numeric type code as the value. MX: 15 means MX records are present, not that there are fifteen of them. A quick way to see what a domain publishes without walking the array.
dnsRecords The records themselves, one object per record.

There is no status or domainRegistered field on this endpoint. A failed lookup is signaled by the HTTP status code and an error body, a domain that does not exist returns 404 with the message Entered Domain does not exist, not a 200 with a flag set to false.

Fields Inside Each Record

Every record object shares name, dnsType, ttl and rawText. The remaining fields depend on the type, and this is the part that trips people up:

Record typeField holding the answer
A, AAAAaddress holds the IP
NS, CNAME, PTRsingleName holds the target hostname, with a trailing dot
MXtarget is the mail server, priority is its preference value. Lower wins.
TXT, SPFstrings is an array of the text chunks, already unquoted
SOAhost, admin, serial, refresh, retry, expire, minimum

rawText is always present and always the record exactly as DNS returned it. If you're logging for later comparison or diffing a zone over time, store rawText; it's stable and complete.

ttl is the number of seconds resolvers may cache the record. Flag a very low TTL on a domain you're investigating. It lets an operator move infrastructure quickly, which is common in both CDNs and fast-flux abuse.

DNS Live cURL Request
Terminal showing a DNS API JSON response with the dnsTypes summary and A, NS and MX records

Step 4: Look Up a Single Record Type

Fetching everything is convenient but wasteful when you only care about one type. Swap type=all for the type you want:

curl -L "https://api.whoisfreaks.com/v2.0/dns/live?apiKey=API_KEY&domainName=whoisfreaks.com&type=mx"

The response has the same shape. dnsTypes lists only the type you asked for, and dnsRecords contains only those records.

Common single-type lookups:

  • type=a where the site is hosted
  • type=ns who runs the DNS
  • type=mx who handles the mail
  • type=txt SPF, DKIM and DMARC policies

Step 5: Look Up a PTR Record for an IP

The same endpoint accepts an ipAddress parameter, which runs a PTR (reverse) lookup, asking what hostname an IP address claims as its name. Pass it on its own, or together with a domain:

curl -L "https://api.whoisfreaks.com/v2.0/dns/live?apiKey=API_KEY&ipAddress=8.8.8.8&type=all"
curl -L "https://api.whoisfreaks.com/v2.0/dns/live?apiKey=API_KEY&domainName=whoisfreaks.com&ipAddress=8.8.8.8&type=all"

Passing both simply returns both sets of records in one response; the domain's records and the IP's PTR record, side by side in the same dnsRecords array. It does not change how the domain is resolved.

The PTR answer is in singleName. name holds the in-addr.arpa form of the query, which is what trips people up the first time:

{
  "queryTime": "2026-08-26 09:54:51",
  "ipAddress": "8.8.8.8",
  "dnsTypes": {
    "PTR": 12
  },
  "dnsRecords": [
    {
      "name": "8.8.8.8.in-addr.arpa",
      "type": 12,
      "dnsType": "PTR",
      "ttl": 15535,
      "rawText": "8.8.8.8.in-addr.arpa.\t15535\tIN\tPTR\tdns.google.",
      "rRsetType": 12,
      "singleName": "dns.google."
    }
  ]
}

PTR is not one of the values the documented type enum accepts, even though PTR records come back. Use type=all, as the documentation's own sample does.

Step 6: Check What the Records Used to Be

A live lookup is a snapshot. To see how a domain's DNS has changed, use the historical endpoint. This section covers the request shape only; for reading and interpreting a domain's DNS timeline, see how to check DNS history for any domain.

curl -L "https://api.whoisfreaks.com/v2.0/dns/historical?apiKey=API_KEY&domainName=whoisfreaks.com&type=all&page=1"

The response is paginated and groups records by observation date:

{
  "totalRecords": 71,
  "totalPages": 1,
  "currentPage": 1,
  "historicalDnsRecords": [
    {
      "queryTime": "2024-04-01",
      "domainName": "whoisfreaks.com.",
      "dnsTypes": {
        "A": 1
      },
      "dnsRecords": [
        {
          "name": "whoisfreaks.com",
          "type": 1,
          "dnsType": "A",
          "ttl": 3600,
          "rawText": "whoisfreaks.com.\t3600\tIN\tA\t139.144.20.35",
          "rRsetType": 1,
          "address": "139.144.20.35"
        }
      ]
    },
    {
      "queryTime": "2024-05-07",
      "domainName": "whoisfreaks.com.",
      "dnsTypes": {
        "A": 1
      },
      "dnsRecords": [
        {
          "name": "whoisfreaks.com",
          "type": 1,
          "dnsType": "A",
          "ttl": 3600,
          "rawText": "whoisfreaks.com.\t3600\tIN\tA\t4.157.25.229",
          "rRsetType": 1,
          "address": "4.157.25.229"
        }
      ]
    },
    {
      "queryTime": "2025-02-18",
      "domainName": "whoisfreaks.com.",
      "dnsTypes": {
        "SPF": 99
      },
      "dnsRecords": [
        {
          "name": "whoisfreaks.com",
          "type": 99,
          "dnsType": "SPF",
          "ttl": 3600,
          "rawText": "whoisfreaks.com.\t3600\tIN\tSPF\t\"v=spf1 include:spf.protection.outlook.com -all\"",
          "rRsetType": 99,
          "strings": [
            "v=spf1 include:spf.protection.outlook.com -all"
          ]
        },
        {
          "name": "whoisfreaks.com",
          "type": 99,
          "dnsType": "SPF",
          "ttl": 3600,
          "rawText": "whoisfreaks.com.\t3600\tIN\tSPF\t\"v=spf1 mx include:spf.mtasv.net -all.\"",
          "rRsetType": 99,
          "strings": [
            "v=spf1 mx include:spf.mtasv.net -all."
          ]
        }
      ]
    }
  ]
}

Each entry is dated, so you can trace a domain moving between hosts, changing mail providers, or adding an SPF record. Read totalPages and walk page=1..n to collect the full history. A historical lookup is charged per page, not per domain, so a domain with a long record set costs more than one call.

Step 7: Handle the Cases That Aren't a Clean Answer

Real lookups fail in predictable ways. Handle these three before you put a script into production:

  • Domain does not exist: This is an HTTP error, not a 200 with a flag. You get 404 with "message": "Entered Domain does not exist: asdsawewfds.com". There is no domainRegistered field on this endpoint to test, so branch on the status code.
  • Registered but no records of that type: Perfectly normal; a domain with no mail service has no MX records. dnsTypes won't list the type and no records of it appear in dnsRecords. This is not an error, and it returns 200.
  • Rate limits and credits: A live DNS lookup costs 1 credit per successful domain query and a historical lookup 2 credits per page; the free 500 credits allow 10 live requests a minute. See the credit usage and rate limiting docs, and back off on 429 rather than retrying immediately.

The -L flag in these examples follows redirects, and --max-time 3600 caps how long cURL will wait. Keep both in scripted use.

Summary

Step Action
1 Copy your API key from the dashboard
2 GET /v2.0/dns/live with domainName and type=all
3 Read dnsTypes for a fast overview, dnsRecords for the detail
4 Narrow to one type with type=a, mx, ns, txt and so on
5 Query by ipAddress for a PTR lookup when you're starting from an IP
6 Use /v2.0/dns/historical to see how the records changed
7 Branch on the HTTP status, expect missing types, respect rate limits

One request gets you a domain's entire DNS footprint, and the same endpoint scales from a one-off check to a scripted sweep. To run the same lookup across a list of domains in a single call, see how to get bulk WHOIS and DNS records. For live, historical, reverse, and bulk endpoints in one place, along with per-plan rates and the full parameter reference, see the DNS API.

Frequently Asked Questions

What is a DNS lookup?

A DNS lookup asks the Domain Name System what records a domain publishes. The most familiar answer is the A record, which maps the domain to an IPv4 address, but the same query also returns nameservers, mail servers, and the text records used for email authentication. The WhoisFreaks DNS API runs the lookup and returns every record as structured JSON.

How do I do a DNS lookup on a domain?

Send a GET request to https://api.whoisfreaks.com/v2.0/dns/live with your apiKey, the domainName, and type=all. No headers or request body are needed, so a single cURL command is enough. The response contains a dnsTypes summary and a dnsRecords array holding every record.

Can I look up just one record type?

Yes. Replace type=all with the specific type (A, AAAA, NS, MX, CNAME, SOA, TXT or SPF) or with a comma-separated list such as type=mx,ns.

What's the difference between a live and a historical DNS lookup?

A live lookup resolves the domain right now and reflects whatever is currently published. A historical lookup returns dated snapshots of what the records used to be, which is how you spot a domain changing hosts, switching mail providers, or adding an SPF record. They're separate endpoints: /v2.0/dns/live and /v2.0/dns/historical.

Why does my lookup return no records?

Check the HTTP status code first, because this endpoint has no status or domainRegistered field to test. A domain that does not exist comes back as 404 with the message "Entered Domain does not exist". A 200 with nothing for a given type means the opposite - the domain resolves fine and simply doesn't publish that record, which is legitimate for a domain with no email service and no MX records.

Does the API return the raw DNS record?

Yes. Every record includes a rawText field containing the record exactly as DNS returned it, alongside parsed fields like address for A records or target and priority for MX records. Use the parsed fields for logic and rawText for logging and comparison.

How do I look up DNS records for an IP address?

Use the ipAddress parameter instead of domainName on the same live endpoint with type=all. That runs a PTR lookup, which asks what hostname the IP claims - the answer is in singleName, while name holds the in-addr.arpa form of the query. You can pass domainName and ipAddress together, which returns both sets of records in the same response rather than changing how either is resolved.

How many credits does a DNS lookup cost?

A live DNS lookup costs 1 credit per successful domain query, and a historical DNS lookup costs 2 credits per page of 100 records. Bulk DNS is charged 1 credit per successful query in the batch, and reverse DNS 5 credits per page. The credit usage documentation carries the full table, and your remaining balance is shown in the dashboard. New accounts start with 500 free credits, which is enough to work through this tutorial and test properly.