Join our FREE personalized newsletter for news, trends, and insights that matter to everyone in America

Newsletter
New

How To Build A Domain Intelligence Tool In Javascript (dns + Geolocation + Screenshots)

Card image cap

Before you sign a contract with a vendor, onboard a new partner, or click a suspicious link, you probably want to know: who is behind this domain?

Tools like DomainTools and SecurityTrails charge $99+/month for this. But you can build your own domain intelligence tool in under 120 lines of JavaScript using free APIs — and it runs from the command line in seconds.

In this tutorial, we'll build a CLI that takes any domain and returns:

  • DNS records (A, MX, NS, TXT, CNAME)
  • WHOIS data (registrar, creation date, expiration)
  • Server geolocation (country, city, ISP, hosting provider)
  • A visual screenshot of the live site
  • A risk assessment based on domain age, hosting, and DNS configuration

The result is a clean report you can use for vendor vetting, phishing analysis, or sales prospecting.

What We're Building

$ node domain-intel.js stripe.com  
  
╔══════════════════════════════════════════════╗  
║  DOMAIN INTELLIGENCE REPORT: stripe.com     ║  
╚══════════════════════════════════════════════╝  
  
DNS Records  
  A:     185.166.143.0, 185.166.142.0  
  MX:    aspmx.l.google.com (pri 1)  
  NS:    ns-cloud-d1.googledomains.com  
  TXT:   v=spf1 include:_spf.google.com ...  
  
Server Location  
  IP:       185.166.143.0  
  Location: San Francisco, US  
  ISP:      Fastly, Inc.  
  Hosting:  Fastly CDN  
  
WHOIS  
  Registrar:  MarkMonitor Inc.  
  Created:    1995-09-12  
  Expires:    2031-09-11  
  Age:        30.5 years  
  
Risk Score: 2/100 (Very Low)  
  ✅ Domain age > 5 years  
  ✅ Professional registrar  
  ✅ SPF record configured  
  ✅ MX records present  
  ✅ CDN-hosted  
  
Screenshot saved: stripe.com-screenshot.png  

Prerequisites

  • Node.js 18+ (for native fetch)
  • A free API key from Frostbyte API Gateway — 200 free credits, no credit card

Step 1: Get Your Free API Key

curl -X POST https://api.frostbyte.systems/api/keys/create  

You'll get back something like:

{  
  "key": "gw_abc123...",  
  "credits": 200,  
  "rateLimit": "60/min"  
}  

Save that key. Each domain lookup uses 3 credits (one per API call), so you get ~66 full reports for free.

Step 2: Build the Domain Intel Tool

Create a file called domain-intel.js:

import { writeFileSync } from 'fs';  
  
const API_KEY = process.env.FROSTBYTE_KEY || 'your-key-here';  
const BASE = 'https://api.frostbyte.systems/v1';  
  
async function api(path, options = {}) {  
  const res = await fetch(`${BASE}${path}`, {  
    ...options,  
    headers: {  
      'x-api-key': API_KEY,  
      'Content-Type': 'application/json',  
      ...options.headers,  
    },  
  });  
  if (!res.ok) throw new Error(`API error: ${res.status}`);  
  return res.json();  
}  
  
// ── Step A: DNS + WHOIS ──────────────────────────────  
async function getDomainInfo(domain) {  
  const data = await api(`/agent-dns/resolve/${domain}`);  
  return data;  
}  
  
async function getWhois(domain) {  
  const data = await api(`/agent-dns/whois/${domain}`);  
  return data;  
}  
  
// ── Step B: Geolocate the server IP ──────────────────  
async function geolocateIP(ip) {  
  const data = await api(`/agent-geo/geo/${ip}`);  
  return data;  
}  
  
// ── Step C: Screenshot the live site ─────────────────  
async function screenshotDomain(domain) {  
  const data = await api('/agent-screenshot/screenshot', {  
    method: 'POST',  
    body: JSON.stringify({  
      url: `https://${domain}`,  
      viewport: 'desktop',  
      fullPage: false,  
      format: 'png',  
    }),  
  });  
  return data;  
}  
  
// ── Risk scoring logic ───────────────────────────────  
function calculateRisk(dns, whois, geo) {  
  let score = 50; // Start neutral  
  const flags = [];  
  
  // Domain age  
  if (whois?.createdDate) {  
    const ageYears =  
      (Date.now() - new Date(whois.createdDate).getTime()) /  
      (365.25 * 24 * 60 * 60 * 1000);  
    if (ageYears > 5) {  
      score -= 20;  
      flags.push('✅ Domain age > 5 years');  
    } else if (ageYears < 0.5) {  
      score += 25;  
      flags.push('⚠️  Domain registered < 6 months ago');  
    } else if (ageYears < 1) {  
      score += 10;  
      flags.push('⚠️  Domain registered < 1 year ago');  
    }  
  } else {  
    score += 10;  
    flags.push('⚠️  WHOIS data unavailable');  
  }  
  
  // Registrar reputation  
  const trustedRegistrars = [  
    'markmonitor', 'cloudflare', 'godaddy', 'namecheap',  
    'google', 'gandi', 'hover', 'name.com',  
  ];  
  if (whois?.registrar) {  
    const reg = whois.registrar.toLowerCase();  
    if (trustedRegistrars.some((t) => reg.includes(t))) {  
      score -= 10;  
      flags.push('✅ Professional registrar');  
    }  
  }  
  
  // SPF record  
  const hasSPF = dns?.TXT?.some((r) =>  
    (r.value || r).toString().includes('v=spf1')  
  );  
  if (hasSPF) {  
    score -= 5;  
    flags.push('✅ SPF record configured');  
  } else {  
    score += 5;  
    flags.push('⚠️  No SPF record');  
  }  
  
  // MX records  
  if (dns?.MX?.length > 0) {  
    score -= 5;  
    flags.push('✅ MX records present');  
  }  
  
  // DMARC  
  // (Would need _dmarc.domain TXT lookup — bonus feature)  
  
  // Hosting  
  if (geo?.isp) {  
    const cdn = ['cloudflare', 'fastly', 'akamai', 'amazon', 'google'];  
    if (cdn.some((c) => geo.isp.toLowerCase().includes(c))) {  
      score -= 10;  
      flags.push('✅ CDN-hosted');  
    }  
  }  
  
  return {  
    score: Math.max(0, Math.min(100, score)),  
    flags,  
  };  
}  
  
// ── Main: Assemble the report ────────────────────────  
async function investigate(domain) {  
  console.log(`\n${'' + ''.repeat(46) + ''}`);  
  console.log(`║  DOMAIN INTELLIGENCE REPORT: ${domain.padEnd(16)} ║`);  
  console.log(`${'' + ''.repeat(46) + ''}\n`);  
  
  // Run DNS and WHOIS in parallel  
  console.log('Fetching DNS records...');  
  const [dns, whois] = await Promise.all([  
    getDomainInfo(domain).catch((e) => {  
      console.error('  DNS lookup failed:', e.message);  
      return null;  
    }),  
    getWhois(domain).catch((e) => {  
      console.error('  WHOIS lookup failed:', e.message);  
      return null;  
    }),  
  ]);  
  
  // Print DNS  
  if (dns) {  
    console.log('\nDNS Records');  
    if (dns.A) console.log(`  A:     ${dns.A.join(', ')}`);  
    if (dns.AAAA) console.log(`  AAAA:  ${dns.AAAA.join(', ')}`);  
    if (dns.MX)  
      console.log(  
        `  MX:    ${dns.MX.map((r) => `${r.exchange || r} (pri ${r.priority || '?'})`).join(', ')}`  
      );  
    if (dns.NS) console.log(`  NS:    ${dns.NS.join(', ')}`);  
    if (dns.CNAME) console.log(`  CNAME: ${dns.CNAME.join(', ')}`);  
    if (dns.TXT)  
      console.log(  
        `  TXT:   ${dns.TXT.map((t) => (t.length > 60 ? t.slice(0, 60) + '...' : t)).join('\n         ')}`  
      );  
  }  
  
  // Print WHOIS  
  if (whois) {  
    console.log('\nWHOIS');  
    if (whois.registrar) console.log(`  Registrar: ${whois.registrar}`);  
    if (whois.createdDate) console.log(`  Created:   ${whois.createdDate}`);  
    if (whois.expiresDate) console.log(`  Expires:   ${whois.expiresDate}`);  
    if (whois.createdDate) {  
      const years = (  
        (Date.now() - new Date(whois.createdDate).getTime()) /  
        (365.25 * 24 * 60 * 60 * 1000)  
      ).toFixed(1);  
      console.log(`  Age:       ${years} years`);  
    }  
  }  
  
  // Geolocate the primary A record  
  const primaryIP = dns?.A?.[0];  
  let geo = null;  
  if (primaryIP) {  
    console.log('\nGeolocating server...');  
    geo = await geolocateIP(primaryIP).catch((e) => {  
      console.error('  Geolocation failed:', e.message);  
      return null;  
    });  
    if (geo) {  
      console.log('\nServer Location');  
      console.log(`  IP:       ${primaryIP}`);  
      if (geo.city) console.log(`  Location: ${geo.city}, ${geo.country}`);  
      if (geo.isp) console.log(`  ISP:      ${geo.isp}`);  
      if (geo.org) console.log(`  Org:      ${geo.org}`);  
    }  
  }  
  
  // Risk score  
  const { score, flags } = calculateRisk(dns, whois, geo);  
  const level =  
    score < 20  
      ? 'Very Low'  
      : score < 40  
        ? 'Low'  
        : score < 60  
          ? 'Medium'  
          : score < 80  
            ? 'High'  
            : 'Critical';  
  console.log(`\nRisk Score: ${score}/100 (${level})`);  
  flags.forEach((f) => console.log(`  ${f}`));  
  
  // Screenshot  
  console.log('\nCapturing screenshot...');  
  try {  
    const shot = await screenshotDomain(domain);  
    if (shot?.image) {  
      // image is base64 — decode and save  
      const buffer = Buffer.from(shot.image, 'base64');  
      const filename = `${domain.replace(/\./g, '-')}-screenshot.png`;  
      writeFileSync(filename, buffer);  
      console.log(`Screenshot saved: ${filename}`);  
    } else if (shot?.url) {  
      console.log(`Screenshot URL: ${shot.url}`);  
    }  
  } catch (e) {  
    console.log(`  Screenshot failed: ${e.message}`);  
  }  
  
  console.log('\n' + ''.repeat(48));  
  console.log('Powered by Frostbyte API — https://api-catalog-three.vercel.app');  
  
  return { dns, whois, geo, risk: { score, level, flags } };  
}  
  
// ── CLI entry ────────────────────────────────────────  
const domain = process.argv[2];  
if (!domain) {  
  console.error('Usage: node domain-intel.js <domain>');  
  console.error('Example: node domain-intel.js stripe.com');  
  process.exit(1);  
}  
  
investigate(domain.replace(/^https?:\/\//, '').replace(/\/.*$/, ''));  

Step 3: Run It

export FROSTBYTE_KEY="gw_your_key_here"  
node domain-intel.js github.com  

How It Works

The tool chains three API calls together:

1. DNS + WHOIS (/v1/agent-dns)

The DNS API returns all record types for a domain in a single call. We also pull WHOIS data separately to get registrar info, creation date, and expiration — critical signals for assessing domain legitimacy.

// Get all DNS records  
const dns = await api(`/agent-dns/resolve/example.com`);  
// Returns: { A: [...], MX: [...], NS: [...], TXT: [...] }  
  
// Get WHOIS / RDAP data  
const whois = await api(`/agent-dns/whois/example.com`);  
// Returns: { registrar, createdDate, expiresDate, nameServers, ... }  

2. IP Geolocation (/v1/agent-geo)

Once we have the A record IPs, we geolocate them to find out where the server is physically hosted. This reveals the hosting provider, country, and ISP — useful for detecting if a "US company" is actually hosted in an unexpected jurisdiction.

const geo = await api(`/agent-geo/geo/185.166.143.0`);  
// Returns: { country: "US", city: "San Francisco", isp: "Fastly", ... }  

3. Visual Screenshot (/v1/agent-screenshot)

A screenshot of the live site gives you instant visual confirmation. Does it look like a real business? Is it a parking page? A phishing clone? One glance tells you what DNS records can't.

const shot = await api('/agent-screenshot/screenshot', {  
  method: 'POST',  
  body: JSON.stringify({ url: 'https://example.com', viewport: 'desktop' }),  
});  
// Returns: { image: "base64...", width: 1280, height: 800 }  

The Risk Scoring Algorithm

The most useful part of this tool is the automated risk assessment. Here's how the scoring works:

Signal Effect on Score
Domain age > 5 years -20 (safer)
Domain age < 6 months +25 (riskier)
Trusted registrar (MarkMonitor, Cloudflare, etc.) -10
SPF record present -5
MX records configured -5
Hosted on CDN (Cloudflare, Fastly, etc.) -10
WHOIS data unavailable +10
No SPF record +5

The score starts at 50 and adjusts based on these signals. A domain like stripe.com (30+ years old, MarkMonitor, SPF, CDN) lands around 2/100. A freshly registered domain with no email records might score 80+.

Real-World Use Cases

Vendor Security Assessment

Before connecting a third-party service to your infrastructure, run this tool to verify they're legitimate:

node domain-intel.js new-saas-vendor.com  

Red flags to watch for:

  • Domain registered less than a year ago
  • No SPF/DMARC records (poor email security hygiene)
  • Hosted on a residential ISP instead of a cloud provider
  • WHOIS privacy on a supposedly enterprise company

Phishing Detection

Got a suspicious email with a link? Check the domain before clicking:

node domain-intel.js amaz0n-secure-login.com  

Phishing domains almost always score high because they're newly registered, lack email infrastructure, and often use cheap registrars.

Sales Prospecting

Before reaching out to a company, understand their technical setup:

node domain-intel.js target-company.io  

You can see what email provider they use (MX records), what CDN they're on, and how established they are. This is the same data sales intelligence platforms charge hundreds per month for.

Extending the Tool

Here are some ideas to make it more powerful:

Add DNS Propagation Check

The DNS API also supports propagation analysis across 8 resolvers:

const prop = await api(`/agent-dns/propagation/${domain}`);  
// Shows if DNS records are consistent across Google, Cloudflare, Quad9, etc.  

Add Web Scraping for Page Content

Use the scraper API to pull the actual page content and analyze it:

const page = await api(  
  `/agent-scraper/scrape?url=https://${domain}&format=markdown`  
);  
// Extract title, meta description, heading structure, outbound links  

Batch Processing

Investigate multiple domains at once:

const domains = ['example1.com', 'example2.com', 'example3.com'];  
const results = await Promise.all(domains.map(investigate));  

Export as JSON

Add a --json flag for piping into other tools:

if (process.argv.includes('--json')) {  
  console.log(JSON.stringify(result, null, 2));  
  process.exit(0);  
}  

API Costs

Each full domain report uses:

API Call Credits
DNS Lookup 1
WHOIS Lookup 1
IP Geolocation 1
Screenshot 1
Total per domain 4

With 200 free credits, you get 50 full domain reports without paying anything. If you need more, credits are $1 per 500 at api-catalog-three.vercel.app/topup.

Wrapping Up

In about 120 lines of JavaScript, you've built a domain intelligence tool that combines DNS analysis, server geolocation, visual screenshots, and automated risk scoring. It's the kind of tool that security teams, sales teams, and developers all find useful — and it runs locally with no dependencies beyond Node.js.

The full source code uses three Frostbyte APIs through a single API key:

  • DNS Lookup — records, WHOIS, propagation
  • IP Geolocation — server location, ISP, hosting provider
  • Screenshots — visual confirmation of live sites

Get your free API key at api-catalog-three.vercel.app and start investigating domains in seconds.

Have questions or want to see more API tutorials? Drop a comment below or check out the full API docs.