Skip to content
DomDetailer
Developer guide · PHP

Fetch domain metrics with PHP cURL and JSON

Call the API from server-side PHP, keep credentials in an environment variable and validate HTTP and JSON before exporting metric values.

Updated · DomDetailer

Prepare PHP and the input file

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.

Enable PHP cURL and use a current PHP runtime with JSON support. Save the example as check_domains.php and run it from the command line with php check_domains.php. Keep the script and its input/output outside a public web directory.

A PHP cURL batch example

cURL receives an API key header, connect and total timeouts, and a fixed HTTPS endpoint. Redirect following stays disabled. JSON_THROW_ON_ERROR makes malformed JSON an explicit error rather than an empty metric row.

<?php
declare(strict_types=1);

$key = trim((string)getenv('DOMDETAILER_API_KEY'));
if ($key === '' || !extension_loaded('curl')) {
    exit("Set DOMDETAILER_API_KEY and enable PHP cURL.\n");
}
$lines = file('domains.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($lines === false) exit("Cannot read domains.txt.\n");
$hosts = array_values(array_unique(array_filter(array_map(
    static fn($line) => strtolower(trim($line)), $lines
))));
$pattern = '/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+'
    . '[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/D';
if (!$hosts || count($hosts) > 20) exit("Use 1-20 unique domains.\n");
foreach ($hosts as $host) {
    if (!preg_match($pattern, $host)) exit("Use bare ASCII/punycode hosts.\n");
}
$output = fopen('metrics.csv', 'x');
if ($output === false) exit("Choose a new output file and unprocessed inputs.\n");
$fields = ['mozDA', 'majesticTF', 'majesticCF', 'mozSpam'];
fputcsv($output, ['domain', 'status', ...$fields], ',', '"', '');
fflush($output);
foreach ($hosts as $host) {
    fwrite(STDERR, "Attempting $host\n");
    $url = 'https://domdetailer.com/api2/checkDomain.php?' . http_build_query([
        'app' => 'PHPResearch', 'domain' => $host,
    ], '', '&', PHP_QUERY_RFC3986);
    $curl = curl_init($url);
    curl_setopt_array($curl, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_TIMEOUT => 60,
        CURLOPT_HTTPHEADER => ['apikey: ' . $key],
        CURLOPT_FOLLOWLOCATION => false,
    ]);
    $raw = curl_exec($curl);
    $status = (int)curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
    $transportError = curl_errno($curl) !== 0;
    curl_close($curl);
    $label = $transportError ? 'UNCERTAIN - review account' : "HTTP $status";
    $data = null;
    if (!$transportError && $status >= 200 && $status < 300) {
        try {
            $data = json_decode((string)$raw, true, 512, JSON_THROW_ON_ERROR);
            if (!is_array($data)) throw new RuntimeException('Unexpected JSON');
            foreach ($fields as $field) {
                if (!isset($data[$field]) || !is_numeric($data[$field])) {
                    throw new RuntimeException('Unexpected metric field');
                }
            }
            $label = 'OK';
        } catch (Throwable $error) {
            $label = 'UNCERTAIN - review response and account';
        }
    }
    $row = [$host, $label];
    foreach ($fields as $field) $row[] = $label === 'OK' ? $data[$field] : '';
    fputcsv($output, $row, ',', '"', '');
    fflush($output);
    if ($label !== 'OK') {
        fwrite(STDERR, "Stopped; review the result before retrying.\n");
        break;
    }
}
fclose($output);

Separate HTTP errors from valid metrics

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.

When using the same request inside a web application, keep calls on your server. Return only the fields needed by the browser and authenticate the user before starting paid work. Do not expose this batch script as an anonymous endpoint.

Budget server-side jobs deliberately

Each successful domain request costs one credit. The example processes at most 20 unique domains and saves each result. For recurring jobs, add durable completion records so schedule restarts do not repeat already completed 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.

Individual anchors and source URLs use the backlink data API. Exact-name registry presence uses extension coverage; neither is included in a domain metrics response.

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

Can I put this PHP example on a public URL?

Keep it as a private command-line job. A web application needs user authorization, a bounded job queue and clear credit controls before it starts requests.

Why does the example disable redirect following?

The request targets the fixed HTTPS API address. Treat an unexpected redirect as a condition to investigate instead of forwarding the credential to another destination.

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.