Skip to content
DomDetailer
Developer guide · Python

Fetch domain metrics in Python and export CSV

Use Python’s standard library for a bounded list of domain lookups, with an environment variable key, HTTP checks and results saved after each request.

Updated · DomDetailer

Prepare a small paid batch

Use a private working directory with a text file named domains.txt, one bare ASCII or punycode hostname per line. Start with one domain you intend to check. Save your API key in the DOMDETAILER_API_KEY environment variable; the example never embeds it in source or puts it in the query string.

Verify credentials first with the free balance endpoint. Running a real domain request can consume a credit. These examples are sequential, deduplicate the inputs and refuse more than 20 unique hostnames per run.

Use Python 3. The example needs only standard-library modules, so it does not require a third-party HTTP package. Save the code below as check_domains.py and run python check_domains.py in the directory containing the input file.

A sequential Python example

The request uses urllib.request.Request with an apikey header and encoded app and domain parameters. The client timeout is 60 seconds.

import csv
import json
import math
import os
import re
import sys
from pathlib import Path
from urllib.error import HTTPError, URLError
from urllib.parse import urlencode
from urllib.request import HTTPRedirectHandler, Request, build_opener

class NoRedirect(HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        return None

opener = build_opener(NoRedirect)

key = os.environ.get("DOMDETAILER_API_KEY", "").strip()
if not key:
    raise SystemExit("Set DOMDETAILER_API_KEY before running.")

hosts = list(dict.fromkeys(
    line.strip().lower()
    for line in Path("domains.txt").read_text().splitlines()
    if line.strip()
))
host_pattern = re.compile(
    r"(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+"
    r"[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?"
)
if not hosts or len(hosts) > 20 or any(
    not host_pattern.fullmatch(host) for host in hosts
):
    raise SystemExit("Use 1-20 unique bare ASCII/punycode hostnames.")

fields = ["mozDA", "majesticTF", "majesticCF", "mozSpam"]
# Exclusive creation avoids accidentally repeating a completed batch.
with open("metrics.csv", "x", newline="", encoding="utf-8") as output:
    writer = csv.writer(output)
    writer.writerow(["domain", "status", *fields])
    output.flush()
    for host in hosts:
        print(f"Attempting {host}", file=sys.stderr)
        url = "https://domdetailer.com/api2/checkDomain.php?" + urlencode({
            "app": "PythonResearch", "domain": host
        })
        request = Request(url, headers={"apikey": key})
        try:
            with opener.open(request, timeout=60) as response:
                data = json.load(response)
            if not isinstance(data, dict) or any(
                isinstance(data.get(field), bool)
                or not isinstance(data.get(field), (int, float))
                or not math.isfinite(data.get(field))
                for field in fields
            ):
                raise ValueError("Unexpected metric response")
        except HTTPError as error:
            writer.writerow([host, f"HTTP {error.code}", "", "", "", ""])
            output.flush()
            print("Stopped; review the HTTP error before retrying.", file=sys.stderr)
            break
        except (URLError, TimeoutError, ValueError, OSError):
            writer.writerow([host, "UNCERTAIN - review account", "", "", "", ""])
            output.flush()
            print("Stopped; verify usage before retrying.", file=sys.stderr)
            break
        writer.writerow([host, "OK", *(data[field] for field in fields)])
        output.flush()

Read the CSV and handle uncertain rows

The CSV keeps status separate from metric values. OK rows contain DA, TF, CF and raw mozSpam. Read the documented raw Spam Score scale; do not attach a percent sign to that field.

The script stops at the first HTTP, transport or response error and does not automatically retry. Correct input and authentication problems; respect the returned guidance for rate limits. A timeout or malformed response can leave the server outcome uncertain. Check usage and preserve the output before repeating a request.

The output file is created exclusively, so rerunning with the same filename will stop before making requests. To continue, prepare an explicit list of unprocessed domains and choose a new output filename. Investigate the last attempted domain if the process was interrupted.

Scale only after checking a sample

A successful domain request costs one credit for the combined response. Twenty successful unique checks consume 20 credits. If you expand the batch limit, budget the unique inputs and retain a checkpoint or durable job record for larger work.

1,000 unique successful requests consume 1,000 credits. That is about $0.79–$1.36 in consumed credit value at the current pack rates. Credits are bought in packs: the entry pack is $34 for 25,000 credits. This is not a $0.79 checkout option. Compare the current DomDetailer credit packs.

Use the expired-domain vetting workflow to decide which candidates need a separate backlink request. You can compute a TF/CF ratio from the two returned fields while preserving the inputs.

Use the response and CSV within the service terms. API access does not grant redistribution rights. Keep keys out of shared code, terminal logs and public files.

Questions

Does the Python script need a paid request for every domain?

Each successful unique domain lookup consumes one credit. The example deduplicates its input list and makes no automatic retries.

Can I safely rerun the same file after a timeout?

Check the saved status and account usage first. A timeout does not prove the server did no work. The example refuses to overwrite its existing output file.

Sources and further reading

Continue your research

Start at 25,000 lookups for $34.

One balance covers the browser tools, the desktop app and the API. Nothing renews, nothing expires, and you can stop using it for six months without losing anything.