Nova Uptime
Guidesapiemail-healthdeveloper

Email Health Monitoring Through Nova Uptime's Public API: Developer Integration Guide

Build custom email health monitoring into your platform using Nova Uptime's REST API. Complete guide with code examples, rate limits, and production patterns.

SN
Sumit Nova Uptime
February 28, 2026 · 8 min read
Share:

Why Use Nova Uptime's API for Email Health Monitoring?#

If you manage customer domains (SaaS platform, hosting provider, agency), you need to programmatically check email health across all of them.

Three Approaches:

  1. Manual: Check each domain in Nova Uptime's dashboard. Not scalable.
  2. WHOIS Queries: Write custom DKIM/SPF/DMARC parsing. Complex, unreliable.
  3. Nova Uptime Public API: REST API that handles all complexity. Scalable, reliable, maintained.

This guide covers the API approach.

API Overview#

Base URL: https://api.novauptime.com/api/v1

Authentication: X-API-Key header

Rate Limits: 50 requests/hour for free tier, 1,000/hour for paid

Response Format:

{
  "success": true,
  "data": { ... },
  "message": "Optional message"
}

Step 1: Generate Your API Key#

  1. Log into go.novauptime.com
  2. Settings → API Keys
  3. Click "Generate New Key"
  4. Copy the 20-character key (e.g., abc123def456ghi789jk)
  5. Store securely (don't commit to git!)

Environment Variable:

export NOVAUPTIME_API_KEY="abc123def456ghi789jk"

Step 2: Check Email Health for a Domain#

Endpoint: GET /domains/{domain}/email-health

Example Request:

curl -H "X-API-Key: abc123def456ghi789jk" \
  https://api.novauptime.com/api/v1/domains/example.com/email-health

Response:

{
  "success": true,
  "data": {
    "domain": "example.com",
    "score": 92,
    "grade": "A",
    "timestamp": "2026-02-20T10:30:00Z",
    "records": {
      "mx": {
        "status": "configured",
        "value": "mail.example.com"
      },
      "spf": {
        "status": "configured",
        "value": "v=spf1 include:sendgrid.net -all",
        "lookups": 4
      },
      "dkim": {
        "status": "configured",
        "selectors": ["s1", "s2"],
        "configured_count": 2
      },
      "dmarc": {
        "status": "configured",
        "policy": "reject"
      },
      "blacklist": {
        "status": "clean",
        "checked_against": 4,
        "listed_on": 0
      }
    },
    "recommendations": [
      {
        "type": "warning",
        "message": "SPF record has 4 lookups (limit is 10). Consider consolidating includes.",
        "action": "Use SPF flattening service or consolidate email providers"
      }
    ]
  }
}

Real-World Use Cases#

Use Case 1: Agency Dashboard#

You're an agency managing 100+ client websites. You want to show each client their email health in your dashboard.

Implementation:

// Express.js route to fetch email health
app.get("/client/:clientId/email-health", async (req, res) => {
  const clientId = req.params.clientId;

  // Get client's domain from database
  const client = await Client.findById(clientId);
  const domain = client.primaryDomain;

  // Fetch email health from Nova Uptime
  const response = await fetch(
    `https://api.novauptime.com/api/v1/domains/${domain}/email-health`,
    {
      headers: { "X-API-Key": process.env.NOVAUPTIME_API_KEY }
    }
  );

  const emailHealth = await response.json();

  // Return to frontend
  res.json(emailHealth.data);
});

Use Case 2: Automated Email Health Scoring#

Grade all customer domains and alert on degradation.

Implementation:

// Cron job: Check all domains daily
async function dailyEmailHealthAudit() {
  const domains = await Domain.find();

  for (const domain of domains) {
    // Fetch current score
    const current = await fetchEmailHealth(domain.name);

    // Compare to previous day
    const previous = await EmailHealthHistory.findLatest(domain.name);

    if (current.data.score < previous.score - 5) {
      // Score dropped >5 points, alert
      await sendSlackAlert({
        domain: domain.name,
        oldScore: previous.score,
        newScore: current.data.score,
        change: current.data.score - previous.score
      });
    }

    // Store history
    await EmailHealthHistory.create({
      domain: domain.name,
      score: current.data.score,
      timestamp: new Date()
    });
  }
}

Use Case 3: Bulk Domain Audit#

You acquired a competitor. 50 new customer domains. You want email health status for all.

Implementation:

// Fetch email health for 50 domains
async function auditAcquiredDomains(acquiredDomains) {
  const results = [];

  // Fetch with concurrency limit (5 at a time)
  for (const domain of acquiredDomains) {
    const health = await fetchEmailHealth(domain);
    results.push({
      domain,
      score: health.data.score,
      grade: health.data.grade,
      issues: health.data.recommendations
    });
  }

  // Export to CSV
  const csv = convertToCSV(results);
  fs.writeFileSync("email-health-audit.csv", csv);

  // Summary: 40 domains healthy, 10 need fixes
  console.log(`Healthy: ${results.filter(r => r.score > 80).length}`);
  console.log(`Need fixes: ${results.filter(r => r.score < 80).length}`);
}

API Patterns for Production#

Pattern 1: Caching with Expiration#

Don't call the API every request. Cache results locally.

const redis = require("redis");
const client = redis.createClient();

async function getEmailHealthCached(domain, maxAge = 3600) {
  // Try cache first
  const cached = await client.get(`email-health:${domain}`);
  if (cached) {
    return JSON.parse(cached);
  }

  // Cache miss, fetch from API
  const health = await fetchEmailHealth(domain);

  // Store in cache for 1 hour
  await client.setex(
    `email-health:${domain}`,
    maxAge,
    JSON.stringify(health.data)
  );

  return health.data;
}

Pattern 2: Rate Limit Handling#

Nova Uptime API has rate limits. Handle gracefully.

async function fetchWithRetry(domain, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(
        `https://api.novauptime.com/api/v1/domains/${domain}/email-health`,
        {
          headers: { "X-API-Key": process.env.NOVAUPTIME_API_KEY },
          timeout: 10000
        }
      );

      if (response.status === 429) {
        // Rate limited, wait before retry
        const retryAfter = response.headers.get("Retry-After") || (2 ** i);
        console.log(`Rate limited. Retrying in ${retryAfter}s`);
        await new Promise(r => setTimeout(r, retryAfter * 1000));
        continue;
      }

      if (!response.ok) throw new Error(`HTTP ${response.status}`);

      return await response.json();
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      console.log(`Attempt ${i + 1} failed, retrying...`);
      await new Promise(r => setTimeout(r, (2 ** i) * 1000));
    }
  }
}

Pattern 3: Batch Processing#

When checking 100+ domains, batch requests efficiently.

async function batchEmailHealthCheck(domains) {
  const batchSize = 10; // 10 concurrent requests
  const results = [];

  for (let i = 0; i < domains.length; i += batchSize) {
    const batch = domains.slice(i, i + batchSize);

    // Process batch concurrently
    const batchResults = await Promise.all(
      batch.map(domain => fetchEmailHealth(domain))
    );

    results.push(...batchResults);

    // Log progress
    console.log(`Processed ${Math.min(i + batchSize, domains.length)}/${domains.length}`);
  }

  return results;
}

Pattern 4: Storing Results Locally#

Store email health results in your own database for historical tracking.

// Database model
const EmailHealthSchema = {
  domain: String,
  score: Number,
  grade: String,
  records: {
    mx: Object,
    spf: Object,
    dkim: Object,
    dmarc: Object,
    blacklist: Object
  },
  timestamp: Date,
  createdAt: Date
};

async function storeEmailHealth(domain) {
  const health = await fetchEmailHealth(domain);

  // Store in DB
  const record = new EmailHealthLog({
    domain,
    score: health.data.score,
    grade: health.data.grade,
    records: health.data.records,
    timestamp: new Date(health.data.timestamp),
    createdAt: new Date()
  });

  await record.save();

  return record;
}

Pattern 5: Alerting on Changes#

Track changes and alert team when something breaks.

async function monitorEmailHealth(domain) {
  const current = await getEmailHealthCached(domain);
  const previous = await EmailHealthLog.findLatest(domain);

  if (!previous) {
    // First check, just store it
    await storeEmailHealth(domain);
    return;
  }

  // Detect changes
  const scoreChange = current.score - previous.score;

  if (scoreChange < -10) {
    // Major degradation
    await alertSlack({
      channel: "#email-alerts",
      message: `
        ${domain}: Email health degraded
        Previous: ${previous.score} (${previous.grade})
        Current: ${current.score} (${current.grade})
        Change: ${scoreChange < 0 ? "" : "+"}${scoreChange}

        Issues: ${current.recommendations.map(r => r.message).join("\n")}
      `
    });
  }
}

Error Handling#

Common Error Scenarios:

async function robustEmailHealthCheck(domain) {
  try {
    const health = await fetchWithRetry(domain);
    return health.data;
  } catch (error) {
    if (error.code === "ENOTFOUND") {
      // Domain doesn't exist
      console.error(`Domain ${domain} not found`);
      return null;
    } else if (error.statusCode === 401) {
      // Invalid API key
      console.error("Invalid Nova Uptime API key");
      return null;
    } else if (error.statusCode === 429) {
      // Rate limited (even after retries)
      console.error("Rate limit exceeded");
      return null;
    } else {
      // Unknown error
      console.error(`Error checking ${domain}: ${error.message}`);
      return null;
    }
  }
}

Frontend Integration#

React Component Example#

import { useState, useEffect } from 'react';

function EmailHealthCard({ domain }) {
  const [health, setHealth] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchHealth() {
      try {
        const response = await fetch(`/api/email-health/${domain}`);
        const data = await response.json();
        setHealth(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    }

    fetchHealth();
  }, [domain]);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <div className="email-health-card">
      <h3>{domain}</h3>
      <div className={`score score-${health.grade}`}>
        {health.score}/100 ({health.grade})
      </div>

      <div className="records">
        {Object.entries(health.records).map(([key, value]) => (
          <div key={key} className={`record ${value.status}`}>
            <strong>{key.toUpperCase()}</strong>: {value.status}
          </div>
        ))}
      </div>

      {health.recommendations.length > 0 && (
        <div className="recommendations">
          <h4>Recommendations:</h4>
          <ul>
            {health.recommendations.map((rec, i) => (
              <li key={i} className={rec.type}>
                {rec.message}
              </li>
            ))}
          </ul>
        </div>
      )}
    </div>
  );
}

API Documentation Reference#

Base URL: https://api.novauptime.com/api/v1

Endpoints:

GET /domains/{domain}/email-health
  → Check email health for domain
  → Rate limit: 50/hour (free), 1000/hour (paid)
  → Response: Email health score, grade, records, recommendations

GET /domains/{domain}/incidents
  → Get last 20 downtime incidents
  → Optional param: limit, offset

GET /domains/{domain}/history
  → Get check history (configurable hours, max 720h)
  → Optional params: hours (default 168), limit (default 500)

GET /domains
  → List user's domains (paginated, max 50/page)
  → Optional params: page, limit

Best Practices#

  1. Cache aggressively: Email health doesn't change frequently. Cache for 1-24 hours.
  2. Batch requests: Check 10 domains concurrently, not 1 at a time.
  3. Handle errors gracefully: Network issues happen. Retry with exponential backoff.
  4. Monitor the API: Track your API usage. If approaching rate limit, cache longer.
  5. Secure your API key: Never commit to git. Use environment variables.
  6. Test rate limits: Simulate high volume before deploying to production.

Summary: API Integration Checklist#

  • ✅ Generate and secure API key
  • ✅ Implement basic email health check
  • ✅ Add caching layer (Redis or in-memory)
  • ✅ Implement rate limit handling
  • ✅ Add error handling for common failures
  • ✅ Test with your actual domain
  • ✅ Build frontend component to display results
  • ✅ Set up alerting for score changes
  • ✅ Document API key management in team wiki
  • ✅ Monitor API usage and quota

Get Started Today#

The Nova Uptime public API is available on the free tier. Generate your first API key in Settings and start integrating email health checks into your platform.

For detailed API reference, visit the Nova Uptime API documentation.

Monitor Your Website Before It Goes Down

Get uptime monitoring, SSL tracking, domain expiry alerts, and email health checks. Free plan — no credit card required.

Start Monitoring Free

Related Articles