Nova Uptime
GuidesAPIintegrationdeveloper

How to Integrate Uptime Monitoring Into Your App With an API

A developer guide to integrating website uptime monitoring using Nova Uptime's REST API. Includes authentication, endpoints, code examples, and best practices.

SN
Sumit Nova Uptime
February 4, 2026 · 12 min read
Share:

When you manage more than a handful of websites, clicking through a dashboard to add domains, check status, and pull reports stops scaling. You need an API. An uptime monitoring API lets you automate domain management, integrate monitoring data into your own tools, and build workflows that respond to incidents programmatically.

This guide walks through integrating Nova Uptime's REST API into your application, from authentication and core endpoints to practical code examples and integration patterns.

Why Integrate Monitoring via API#

Automation#

Adding a new domain to monitoring should not require logging into a dashboard. If your platform provisions websites (for clients, tenants, or internal teams), the monitoring setup should be part of the provisioning script. When a domain is decommissioned, monitoring should be removed automatically.

Custom Dashboards#

You may already have an internal dashboard or client portal. Instead of asking users to check a separate monitoring tool, pull uptime data into the interface they already use. Show uptime percentage, recent incidents, and current status directly in your application.

CI/CD Integration#

After deploying a new version of your application, you want to know immediately if the deployment broke something. An API integration can check monitoring status after deployment, pull recent incidents, and flag any post-deploy issues in your CI/CD pipeline.

Multi-Tenant SaaS#

If you run a SaaS platform where each customer has their own subdomain or custom domain, you need monitoring at scale. The API lets you programmatically add monitoring for each new customer, remove it when they churn, and pull per-customer uptime metrics for their account dashboard.

Nova Uptime API Overview#

Base URL#

All API requests go to:

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

Authentication#

Every request must include an API key in the X-API-Key header. You can generate an API key from the Nova Uptime dashboard under Settings.

X-API-Key: your_api_key_here

API keys are 20-character alphanumeric strings, unique per user. Each key has the same permissions as the user account it belongs to.

Rate Limits#

The API enforces rate limits to prevent abuse. Standard limits allow a generous number of requests per minute for normal operations. If you exceed the rate limit, you receive a 429 Too Many Requests response. Implement exponential backoff in your integration to handle this gracefully.

Response Format#

All responses follow a consistent JSON structure:

{
  "success": true,
  "message": "Domains retrieved successfully",
  "data": {
    // Response payload
  }
}

Error responses include a descriptive message:

{
  "success": false,
  "message": "Domain not found"
}

HTTP status codes follow standard conventions: 200 for success, 201 for created resources, 400 for validation errors, 401 for authentication failures, 404 for not found, and 429 for rate limiting.

Key Endpoints#

List Monitored Domains#

Retrieve all domains in your account with their current status, uptime data, and configuration.

Request:

curl -X GET "https://api.novauptime.com/api/v1/domains" \
  -H "X-API-Key: your_api_key_here"

Response (abbreviated):

{
  "success": true,
  "data": {
    "domains": [
      {
        "id": "clx1abc...",
        "url": "https://example.com",
        "status": "up",
        "lastCheckedAt": "2026-02-04T10:30:00Z",
        "responseTime": 245,
        "uptimePercentage": 99.97,
        "sslValid": true,
        "sslExpiresAt": "2026-08-15T00:00:00Z",
        "checkInterval": 300,
        "emailHealthGrade": "A"
      }
    ],
    "pagination": {
      "page": 1,
      "totalPages": 3,
      "total": 28
    }
  }
}

The endpoint supports pagination with ?page=1 query parameters. The maximum page size is 50 domains.

Get Domain Details#

Retrieve detailed information about a specific domain, including its full configuration.

Request:

curl -X GET "https://api.novauptime.com/api/v1/domains/clx1abc123" \
  -H "X-API-Key: your_api_key_here"

Add a New Domain#

Add a domain to monitoring. The system normalizes the URL (adds https:// if missing), validates it, and begins monitoring immediately.

Request:

curl -X POST "https://api.novauptime.com/api/v1/domains" \
  -H "X-API-Key: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://newsite.example.com",
    "checkInterval": 300
  }'

Response:

{
  "success": true,
  "message": "Domain added successfully",
  "data": {
    "id": "clx2def...",
    "url": "https://newsite.example.com",
    "status": "unknown",
    "checkInterval": 300
  }
}

The domain's status will be unknown initially. The first health check runs within seconds of adding the domain, after which the status updates to up, down, or degraded.

Note: There is a limit of 10 domains via the API per account (plan limits apply). The check interval must be at or above your plan's minimum allowed interval.

Delete a Domain#

Remove a domain from monitoring. This is a soft delete; the domain is deactivated but its historical data is preserved.

Request:

curl -X DELETE "https://api.novauptime.com/api/v1/domains/clx2def456" \
  -H "X-API-Key: your_api_key_here"

Get Domain Incidents#

Retrieve the most recent incidents (downtime events) for a specific domain.

Request:

curl -X GET "https://api.novauptime.com/api/v1/domains/clx1abc123/incidents" \
  -H "X-API-Key: your_api_key_here"

Response (abbreviated):

{
  "success": true,
  "data": [
    {
      "id": "inc_001",
      "startedAt": "2026-02-03T14:22:00Z",
      "resolvedAt": "2026-02-03T14:25:30Z",
      "durationSeconds": 210,
      "httpStatus": 503,
      "screenshotUrl": "https://api.novauptime.com/screenshots/inc_001.jpg"
    }
  ]
}

The endpoint returns the 20 most recent incidents.

Get Check History#

Retrieve historical health check results for a domain. Useful for building response time charts or calculating custom uptime metrics.

Request:

curl -X GET "https://api.novauptime.com/api/v1/domains/clx1abc123/history?hours=24" \
  -H "X-API-Key: your_api_key_here"

The hours parameter controls how far back to look, with a maximum of 720 hours (30 days) and a maximum of 500 records per response.

Run Email Health Check#

Perform an email health check on any domain. This checks MX records, SPF, DKIM, DMARC, and blacklist status.

Request:

curl -X POST "https://api.novauptime.com/api/v1/email-health" \
  -H "X-API-Key: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "example.com"
  }'

Add "fresh": true to bypass the cache and force a fresh lookup:

curl -X POST "https://api.novauptime.com/api/v1/email-health" \
  -H "X-API-Key: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "example.com",
    "fresh": true
  }'

The response includes scores for each category (MX, SPF, DKIM, DMARC, blacklist), an overall score, a letter grade, and actionable recommendations.

Code Examples in JavaScript / Node.js#

Setting Up an API Client#

Create a reusable API client to handle authentication, error handling, and response parsing:

const API_BASE = 'https://api.novauptime.com/api/v1';
const API_KEY = process.env.Nova Uptime_API_KEY;

async function apiRequest(method, path, body = null) {
  const options = {
    method,
    headers: {
      'X-API-Key': API_KEY,
      'Content-Type': 'application/json',
    },
  };

  if (body) {
    options.body = JSON.stringify(body);
  }

  const response = await fetch(`${API_BASE}${path}`, options);
  const json = await response.json();

  if (!json.success) {
    throw new Error(json.message || `API error: ${response.status}`);
  }

  return json.data;
}

Adding a Domain#

async function addDomainToMonitoring(url, checkInterval = 300) {
  const data = await apiRequest('POST', '/domains', {
    url,
    checkInterval,
  });

  console.log(`Domain added: ${data.id}`);
  return data;
}

// Usage
await addDomainToMonitoring('https://mysite.example.com', 60);

Listing All Domains With Pagination#

async function getAllDomains() {
  const allDomains = [];
  let page = 1;
  let totalPages = 1;

  while (page <= totalPages) {
    const data = await apiRequest('GET', `/domains?page=${page}`);
    allDomains.push(...data.domains);
    totalPages = data.pagination.totalPages;
    page++;
  }

  return allDomains;
}

Checking for Recent Incidents#

async function getRecentIncidents(domainId) {
  const incidents = await apiRequest(
    'GET',
    `/domains/${domainId}/incidents`
  );
  return incidents;
}

// Check if any domain has had incidents in the last hour
async function checkAllDomainsForRecentIssues() {
  const domains = await getAllDomains();
  const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000);

  for (const domain of domains) {
    const incidents = await getRecentIncidents(domain.id);
    const recentIncidents = incidents.filter(
      (inc) => new Date(inc.startedAt) > oneHourAgo
    );

    if (recentIncidents.length > 0) {
      console.log(
        `${domain.url}: ${recentIncidents.length} incident(s) in the last hour`
      );
    }
  }
}

Running an Email Health Check#

async function checkEmailHealth(domain) {
  const result = await apiRequest('POST', '/email-health', {
    domain,
    fresh: true,
  });

  console.log(`${domain}: Grade ${result.grade} (${result.score}/100)`);

  if (result.recommendations) {
    const critical = result.recommendations.filter(
      (r) => r.severity === 'critical'
    );
    if (critical.length > 0) {
      console.log(`  Critical issues: ${critical.length}`);
      critical.forEach((r) => console.log(`  - ${r.message}`));
    }
  }

  return result;
}

Common Integration Patterns#

Auto-Add Domains on Deploy#

If your deployment pipeline creates or configures websites, add monitoring as a post-deploy step:

// In your deployment script or CI/CD pipeline
async function postDeployMonitoring(deployedUrl) {
  try {
    // Check if domain is already monitored
    const domains = await getAllDomains();
    const existing = domains.find((d) => d.url === deployedUrl);

    if (!existing) {
      await addDomainToMonitoring(deployedUrl, 300);
      console.log(`Monitoring added for ${deployedUrl}`);
    } else {
      console.log(`${deployedUrl} already monitored (ID: ${existing.id})`);
    }
  } catch (error) {
    // Monitoring failure should not block deployment
    console.error(`Failed to set up monitoring: ${error.message}`);
  }
}

This pattern is particularly useful for platforms that provision customer subdomains. Each new customer gets monitoring automatically without manual intervention.

Post-Deploy Health Verification#

After deploying, check whether the site is responding correctly:

async function verifyPostDeploy(domainId, waitMinutes = 3) {
  // Wait for a few check cycles to complete
  console.log(`Waiting ${waitMinutes} minutes for health checks...`);
  await new Promise((resolve) =>
    setTimeout(resolve, waitMinutes * 60 * 1000)
  );

  // Check for incidents since the deploy started
  const incidents = await getRecentIncidents(domainId);
  const deployTime = new Date(Date.now() - waitMinutes * 60 * 1000);

  const postDeployIncidents = incidents.filter(
    (inc) => new Date(inc.startedAt) > deployTime
  );

  if (postDeployIncidents.length > 0) {
    console.error('Post-deploy incidents detected:');
    postDeployIncidents.forEach((inc) => {
      console.error(
        `  HTTP ${inc.httpStatus} at ${inc.startedAt}`
      );
    });
    return false;
  }

  console.log('No post-deploy incidents. Deployment looks healthy.');
  return true;
}

Building a Custom Status Dashboard#

Pull monitoring data into your own dashboard:

async function getStatusDashboardData() {
  const domains = await getAllDomains();

  const dashboard = {
    total: domains.length,
    up: domains.filter((d) => d.status === 'up').length,
    down: domains.filter((d) => d.status === 'down').length,
    degraded: domains.filter((d) => d.status === 'degraded').length,
    averageResponseTime:
      domains.reduce((sum, d) => sum + (d.responseTime || 0), 0) /
      domains.length,
    sslExpiringSoon: domains.filter((d) => {
      if (!d.sslExpiresAt) return false;
      const daysLeft =
        (new Date(d.sslExpiresAt) - Date.now()) / (1000 * 60 * 60 * 24);
      return daysLeft < 30;
    }),
    domains: domains.map((d) => ({
      url: d.url,
      status: d.status,
      responseTime: d.responseTime,
      uptime: d.uptimePercentage,
      emailGrade: d.emailHealthGrade,
    })),
  };

  return dashboard;
}

Scheduled Email Health Audits#

Run email health checks across all your domains on a schedule and flag any that have dropped below a threshold:

async function weeklyEmailHealthAudit() {
  const domains = await getAllDomains();

  const results = [];
  for (const domain of domains) {
    try {
      // Extract just the domain name from the URL
      const domainName = new URL(domain.url).hostname;
      const health = await checkEmailHealth(domainName);
      results.push({
        domain: domainName,
        score: health.score,
        grade: health.grade,
      });

      // Add a small delay between requests to respect rate limits
      await new Promise((resolve) => setTimeout(resolve, 1000));
    } catch (error) {
      results.push({
        domain: domain.url,
        score: null,
        grade: 'Error',
        error: error.message,
      });
    }
  }

  // Flag domains scoring below 70
  const flagged = results.filter((r) => r.score !== null && r.score < 70);
  if (flagged.length > 0) {
    console.log('Domains with email health issues:');
    flagged.forEach((r) => {
      console.log(`  ${r.domain}: ${r.grade} (${r.score}/100)`);
    });
  }

  return results;
}

Best Practices#

Error Handling#

Always handle API errors gracefully. Network timeouts, rate limits, and server errors can occur. Implement retry logic with exponential backoff:

async function apiRequestWithRetry(method, path, body = null, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await apiRequest(method, path, body);
    } catch (error) {
      if (attempt === maxRetries) throw error;

      // Exponential backoff: 1s, 2s, 4s
      const delay = Math.pow(2, attempt - 1) * 1000;
      console.log(
        `Attempt ${attempt} failed. Retrying in ${delay}ms...`
      );
      await new Promise((resolve) => setTimeout(resolve, delay));
    }
  }
}

Pagination#

Never assume all data fits in a single response. Always handle pagination when listing domains or retrieving history:

  • Check the pagination.totalPages value in list responses.
  • Iterate through all pages to get complete data.
  • Respect the maximum page size (50 for domains).

Caching#

If you are displaying monitoring data in a dashboard that refreshes frequently, cache the API responses on your end. Domain status changes are measured in minutes, not seconds, so a 60-second cache for status data and a 5-minute cache for historical data is reasonable.

Email health data changes even less frequently. A cache TTL of 1 hour is appropriate for email health results.

Rate Limit Awareness#

Structure your integration to minimize unnecessary API calls:

  • Cache responses when possible.
  • Use pagination parameters to fetch only the data you need.
  • Avoid polling the API in tight loops. Instead, poll at intervals appropriate for your use case (every 1-5 minutes for status, hourly or daily for email health).
  • When checking multiple domains, add a small delay between requests to avoid hitting rate limits.

Secure Your API Key#

  • Store your API key in environment variables, not in source code.
  • Do not expose the API key in client-side JavaScript. All API calls should go through your backend.
  • If you suspect a key has been compromised, regenerate it immediately from the Nova Uptime dashboard settings.
  • Use a separate API key for each environment (development, staging, production) if possible.

Handle Domain URL Normalization#

The API normalizes URLs when adding domains (adding https:// if missing), but your integration should also normalize before making requests to avoid creating duplicate entries:

function normalizeUrl(url) {
  if (!url.startsWith('http://') && !url.startsWith('https://')) {
    url = 'https://' + url;
  }
  // Remove trailing slash
  return url.replace(/\/+$/, '');
}

API Access Requirements#

Nova Uptime's API is available on all plans, including Free. The pricing page has a full comparison of what each plan includes:

  • Free plan: 5 domains, API access, dashboard, email health.
  • Pro plan: API access with up to 100 domains.
  • Agency plan: API access with up to 1,000 domains and all features.

Generate your API key from the Settings page in the Nova Uptime dashboard. The full API reference, including request/response schemas for every endpoint, is available at the API documentation page.

Conclusion#

An API integration turns monitoring from a manual, dashboard-based activity into an automated, programmable component of your infrastructure. Whether you are building a multi-tenant SaaS platform, running an agency with hundreds of client sites, or automating your deployment pipeline, the API is the interface that makes monitoring scale.

Start with the basics: add domains programmatically and pull status data. Then build up to more advanced patterns like post-deploy health verification, custom dashboards, and scheduled email health audits.

The full API reference with interactive playground is available at api.novauptime.com/api-docs. You can also explore Nova Uptime's complete feature set on the features page.

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