Building a Website Monitoring Dashboard with ToolCenter API

C
Christian Mesa
Feb 28, 2026
5 min read

Keeping tabs on your websites shouldn't require expensive SaaS tools. With ToolCenter's suite of APIs, you can build a fully custom monitoring dashboard that tracks uptime, SSL health, DNS records, and page speed — all from a single integration.

In this guide, we'll build a monitoring dashboard step by step, combining multiple ToolCenter endpoints into one cohesive system.

Why Build Your Own Monitoring?

Commercial monitoring tools like Pingdom, UptimeRobot, or Datadog are great — but they come with limitations:

  • Pricing tiers restrict the number of monitors or check frequency
  • Limited customization of alerts and dashboards
  • Vendor lock-in makes migration painful
  • Overkill for small teams managing 5-20 sites

With ToolCenter, you pay per API call and own every piece of the stack.

The Architecture

Our dashboard will use four ToolCenter APIs:

  • Status Check — HTTP response codes and uptime
  • SSL Checker — Certificate expiry and validity
  • DNS Lookup — Record verification
  • Page Speed — Performance metrics

Each API call returns structured JSON, making it trivial to store results and render dashboards.

Step 1: Set Up the Status Check

The Status Check API is the backbone of any monitoring system. It tells you whether your site is responding and how fast.

const ToolCenter = require('toolcenter');

const client = new ToolCenter('your_api_key');

async function checkStatus(url) {
  const result = await client.status.check({ url });
  return {
    url,
    statusCode: result.status_code,
    responseTime: result.response_time_ms,
    isUp: result.status_code >= 200 && result.status_code < 400,
    checkedAt: new Date().toISOString()
  };
}

// Monitor multiple sites
const sites = [
  'https://example.com',
  'https://api.example.com',
  'https://docs.example.com'
];

const results = await Promise.all(sites.map(checkStatus));
console.log(results);

The response includes the HTTP status code and response time in milliseconds — everything you need for uptime tracking.

Python Alternative

from toolcenter import ToolCenter
from datetime import datetime

client = ToolCenter("your_api_key")

def check_status(url):
    result = client.status.check(url=url)
    return {
        "url": url,
        "status_code": result["status_code"],
        "response_time": result["response_time_ms"],
        "is_up": 200 <= result["status_code"] < 400,
        "checked_at": datetime.utcnow().isoformat()
    }

sites = ["https://example.com", "https://api.example.com"]
results = [check_status(site) for site in sites]

Step 2: Monitor SSL Certificates

Expired SSL certificates break trust and kill SEO rankings. The SSL Checker API gives you full certificate details:

async function checkSSL(domain) {
  const ssl = await client.ssl.check({ url: domain });
  
  const expiryDate = new Date(ssl.valid_to);
  const daysUntilExpiry = Math.floor(
    (expiryDate - new Date()) / (1000 * 60 * 60 * 24)
  );

  return {
    domain,
    issuer: ssl.issuer,
    validFrom: ssl.valid_from,
    validTo: ssl.valid_to,
    daysUntilExpiry,
    isExpiringSoon: daysUntilExpiry < 30,
    isValid: ssl.is_valid
  };
}

const sslStatus = await checkSSL('https://example.com');

if (sslStatus.isExpiringSoon) {
  console.warn(`SSL for ${sslStatus.domain} expires in ${sslStatus.daysUntilExpiry} days!`);
}

Pro tip: Set your alert threshold to 30 days. Most certificate authorities like Let's Encrypt renew at 30 days, so if you're inside that window and it hasn't renewed, something is wrong.

Step 3: Verify DNS Records

DNS misconfigurations are silent killers. A changed nameserver or missing MX record can go unnoticed for days. The DNS Lookup API helps you verify records match your expected configuration:

async function checkDNS(domain, expectedRecords) {
  const dns = await client.dns.lookup({ domain, type: 'A' });
  
  const currentIPs = dns.records.map(r => r.value);
  const mismatches = expectedRecords.filter(ip => !currentIPs.includes(ip));

  return {
    domain,
    records: currentIPs,
    expected: expectedRecords,
    hasIssues: mismatches.length > 0,
    mismatches
  };
}

const dnsCheck = await checkDNS('example.com', ['93.184.216.34']);

if (dnsCheck.hasIssues) {
  console.error(`DNS mismatch for ${dnsCheck.domain}!`);
  console.error(`Missing: ${dnsCheck.mismatches.join(', ')}`);
}

You can extend this to check MX, CNAME, TXT (SPF/DKIM), and NS records for complete DNS monitoring.

Step 4: Track Page Speed

Slow pages lose visitors. The Page Speed API gives you performance metrics you can track over time:

async function checkSpeed(url) {
  const speed = await client.pageSpeed.analyze({ url });
  
  return {
    url,
    loadTime: speed.load_time_ms,
    ttfb: speed.ttfb_ms,
    pageSize: speed.page_size_bytes,
    requests: speed.total_requests,
    score: speed.performance_score,
    isSlowLoading: speed.load_time_ms > 3000
  };
}

const speedResult = await checkSpeed('https://example.com');

if (speedResult.isSlowLoading) {
  console.warn(`${speedResult.url} is loading slowly: ${speedResult.loadTime}ms`);
}

Track these metrics daily to catch performance regressions before users do.

Step 5: Putting It All Together

Here's a complete monitoring script that combines all four checks:

const ToolCenter = require('toolcenter');
const fs = require('fs');

const client = new ToolCenter(process.env.TOOLCENTER_API_KEY);

const SITES = [
  {
    url: 'https://example.com',
    expectedDNS: ['93.184.216.34'],
    label: 'Main Site'
  },
  {
    url: 'https://api.example.com',
    expectedDNS: ['93.184.216.34'],
    label: 'API'
  }
];

async function runFullCheck(site) {
  const [status, ssl, dns, speed] = await Promise.all([
    client.status.check({ url: site.url }),
    client.ssl.check({ url: site.url }),
    client.dns.lookup({ domain: new URL(site.url).hostname, type: 'A' }),
    client.pageSpeed.analyze({ url: site.url })
  ]);

  const sslExpiry = Math.floor(
    (new Date(ssl.valid_to) - new Date()) / 86400000
  );

  return {
    label: site.label,
    url: site.url,
    timestamp: new Date().toISOString(),
    uptime: {
      isUp: status.status_code >= 200 && status.status_code < 400,
      statusCode: status.status_code,
      responseTime: status.response_time_ms
    },
    ssl: {
      isValid: ssl.is_valid,
      daysUntilExpiry: sslExpiry,
      needsAttention: sslExpiry < 30
    },
    dns: {
      records: dns.records.map(r => r.value),
      matchesExpected: site.expectedDNS.every(
        ip => dns.records.some(r => r.value === ip)
      )
    },
    performance: {
      loadTime: speed.load_time_ms,
      score: speed.performance_score,
      isHealthy: speed.load_time_ms < 3000
    }
  };
}

async function monitor() {
  const results = await Promise.all(SITES.map(runFullCheck));
  
  results.forEach(r => {
    console.log(`${r.label} (${r.url})`);
    console.log(`  Status: ${r.uptime.isUp ? 'UP' : 'DOWN'} ${r.uptime.statusCode} (${r.uptime.responseTime}ms)`);
    console.log(`  SSL: ${r.ssl.daysUntilExpiry} days remaining`);
    console.log(`  DNS: ${r.dns.records.join(', ')}`);
    console.log(`  Speed: ${r.performance.loadTime}ms (score: ${r.performance.score})`);
  });

  // Store history
  const historyFile = './monitoring-history.json';
  let history = fs.existsSync(historyFile) 
    ? JSON.parse(fs.readFileSync(historyFile, 'utf8')) 
    : [];
  history.push(...results);
  fs.writeFileSync(historyFile, JSON.stringify(history, null, 2));

  // Alert on issues
  const alerts = results.filter(r => 
    !r.uptime.isUp || r.ssl.needsAttention || !r.dns.matchesExpected || !r.performance.isHealthy
  );

  if (alerts.length > 0) {
    alerts.forEach(a => {
      if (!a.uptime.isUp) console.log(`ALERT: ${a.label} is DOWN`);
      if (a.ssl.needsAttention) console.log(`ALERT: ${a.label} SSL expires in ${a.ssl.daysUntilExpiry} days`);
      if (!a.dns.matchesExpected) console.log(`ALERT: ${a.label} DNS mismatch`);
      if (!a.performance.isHealthy) console.log(`ALERT: ${a.label} slow: ${a.performance.loadTime}ms`);
    });
  }
}

monitor().catch(console.error);

Scheduling with Cron

Save the script as monitor.js and schedule it:

# Run every 5 minutes
*/5 * * * * cd /path/to/project && node monitor.js >> /var/log/site-monitor.log 2>&1

For a more sophisticated setup, use PM2:

pm2 start monitor.js --cron-restart="*/5 * * * *" --no-autorestart

Adding Slack Alerts

Connect monitoring to real notification channels:

async function sendSlackAlert(message) {
  await fetch(process.env.SLACK_WEBHOOK_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ text: message })
  });
}

if (alerts.length > 0) {
  const message = alerts.map(a => {
    const issues = [];
    if (!a.uptime.isUp) issues.push(`DOWN (${a.uptime.statusCode})`);
    if (a.ssl.needsAttention) issues.push(`SSL expires in ${a.ssl.daysUntilExpiry}d`);
    if (!a.dns.matchesExpected) issues.push('DNS mismatch');
    if (!a.performance.isHealthy) issues.push(`Slow (${a.performance.loadTime}ms)`);
    return `${a.label}: ${issues.join(', ')}`;
  }).join('\n');

  await sendSlackAlert(`Monitoring Alert\n${message}`);
}

Cost Estimation

Each full site check uses 4 API calls:

  • 5-minute intervals: 288 checks/day x 4 calls = 1,152 calls/site/day
  • 15-minute intervals: 96 checks/day x 4 calls = 384 calls/site/day
  • Hourly intervals: 24 checks/day x 4 calls = 96 calls/site/day

For most small teams monitoring 5-10 sites, hourly checks are the sweet spot between visibility and cost.

What's Next

This foundation gives you full visibility into your web infrastructure. From here you could:

  • Build a web UI with charts using Chart.js or Recharts
  • Add incident tracking with automatic status page updates
  • Monitor API endpoints with custom request bodies
  • Track historical trends to predict issues before they happen
  • Set up escalation policies (email, Slack, SMS)

The ToolCenter API gives you the building blocks. What you build with them is up to you.


Ready to start monitoring? Get your free API key and try the Status Check API — it takes less than a minute to make your first call.

Share this article

CM

Christian Mesa

Founder & Developer at ToolCenter

Full-stack developer from the Canary Islands, Spain. Building developer tools and APIs that simplify web development. Passionate about clean code, performance, and making complex things simple.

Try ToolCenter APIs Free

100 API calls/month free. No credit card required.

Related Posts