Prepare a server-side Node.js job
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 a current supported Node.js runtime with global fetch and AbortSignal.timeout. Save the example as check-domains.mjs, then run node check-domains.mjs. This is a Node.js script; do not paste the API key into browser code.
A Node.js fetch example with a request budget
The code validates bare hostnames, creates a fresh CSV file and makes one request at a time. It refuses unexpected redirects and sets a 60-second abort signal for each attempt.
import { readFileSync, openSync, writeSync, closeSync } from 'node:fs';
const key = process.env.DOMDETAILER_API_KEY?.trim();
if (!key) throw new Error('Set DOMDETAILER_API_KEY before running.');
const hosts = [...new Set(readFileSync('domains.txt', 'utf8')
.split(/\r?\n/).map(line => line.trim().toLowerCase()).filter(Boolean))];
const hostPattern = /^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
if (!hosts.length || hosts.length > 20 || hosts.some(host => !hostPattern.test(host))) {
throw new Error('Use 1-20 unique bare ASCII/punycode hostnames.');
}
const fields = ['mozDA', 'majesticTF', 'majesticCF', 'mozSpam'];
const csv = values => values.map(value =>
'"' + String(value).replaceAll('"', '""') + '"').join(',') + '\n';
// Exclusive creation prevents overwriting a previous run.
const output = openSync('metrics.csv', 'wx');
try {
writeSync(output, csv(['domain', 'status', ...fields]));
for (const host of hosts) {
console.error(`Attempting ${host}`);
let label = 'UNCERTAIN - review account';
let data;
try {
const url = new URL('https://domdetailer.com/api2/checkDomain.php');
url.search = new URLSearchParams({ app: 'NodeResearch', domain: host });
const response = await fetch(url, {
headers: { apikey: key },
redirect: 'error',
signal: AbortSignal.timeout(60_000),
});
if (!response.ok) {
label = `HTTP ${response.status}`;
} else {
data = await response.json();
if (!data || fields.some(field => !Number.isFinite(data[field]))) {
throw new Error('Unexpected metric response');
}
label = 'OK';
}
} catch {
// A timeout or malformed response does not prove no credit was used.
}
writeSync(output, csv([host, label,
...fields.map(field => label === 'OK' ? data[field] : '')]));
if (label !== 'OK') {
console.error('Stopped; review the result and account before retrying.');
break;
}
}
} finally {
closeSync(output);
}Keep response errors out of score columns
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.
The CSV quotes its values and escapes embedded quotes. Domain inputs are restricted to hostname characters, and the returned score fields must be finite JSON numbers before being written as metrics.
Scale with checkpoints and account limits
Twenty successful unique lookups consume 20 credits. Larger queues need an explicit budget, durable completion tracking and account concurrency controls. Increasing parallelism is not a substitute for handling uncertain outcomes.
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.
Inspect the API compatibility notes when adding extension coverage or other response shapes to a job. The Sheets guide applies the same deliberate-start principle to spreadsheet work.
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 use fetch from a public browser page with my key?
Keep the account key on your server. This Node.js example uses an environment variable and is intended for private processing.
Does aborting fetch cancel a charged server request?
A client abort does not establish the server outcome. Check saved results and account usage before repeating an uncertain request.