Skip to content

API Enterno.io

Programmatic access to all enterno.io tools: HTTP headers, DNS, SSL, ping, IP geolocation, health scores, monitors and status pages. REST API with JSON responses.

Interactive documentation (Swagger) → MCP Server (Claude Desktop) →
API v1 & v2 sunset 2026-10-01 — migrate to v4 before then. See the migration guide for the step-by-step path. v3 is frozen but supported; v4 is the current version.

Authentication

All API v4 requests require a key via X-API-Key header. Use X-Idempotency-Key to safely retry write requests.

Saved in the browser. Used by all forms below.

Get an API key by creating an account or in your dashboard.

curl -H "X-API-Key: YOUR_KEY" \
  "https://enterno.io/api/v4/webhooks"

Every response includes an X-Request-Id header for tracing.

Idempotency Key

Add X-Idempotency-Key: unique-id to any write request. Enterno.io returns the cached response for duplicate requests within 60 seconds.

curl -X POST "https://enterno.io/api/v4/webhooks" \
  -H "X-API-Key: YOUR_KEY" \
  -H "X-Idempotency-Key: create-wh-001" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://your-app.com/hook"}'

Response Format

API v4 uses the same JSON envelope as v3 with "api_version": "4.0" in meta.

{
  "data": { ... },
  "meta": {
    "request_id": "a1b2c3d4e5f6a7b8c9d0e1f2",
    "duration_ms": 8,
    "cached": false,
    "api_version": "4.0"
  }
}

On error:

{
  "error": {
    "code": "not_found",
    "message": "Resource not found"
  },
  "meta": {
    "request_id": "a1b2c3d4e5f6a7b8c9d0e1f2",
    "duration_ms": 2,
    "api_version": "4.0"
  }
}

Rate Limits

Rate limit information is included in response headers:

HeaderDescription
X-RateLimit-LimitMaximum requests per minute
X-RateLimit-RemainingRemaining requests in current window
X-RateLimit-ResetUnix timestamp when rate limit resets
Retry-AfterSeconds to wait (only on 429)
Plan Requests/min Daily Limit Scopes
Free10100check, dns
Pro605 000check, dns, ssl, ip, ping, monitors, webhook
Business12050 000check, dns, ssl, ip, ping, monitors, webhook

Scopes

Each API key has a set of permitted scopes. A request to an inaccessible endpoint will return a 403 error.

Scope Endpoint Description
check/api/v3/check.phpHTTP header check + health score
dns/api/v3/dns.phpDNS lookup + DNSSEC
ssl/api/v3/ssl.phpSSL/TLS check + chain details
ip/api/v3/ip.phpIP geolocation
ping/api/v3/ping.phpPing, port check, traceroute
monitors/api/v3/monitors.phpMonitor CRUD + status pages
webhook/api/v4/webhooks, /api/v4/eventsWebhook subscription and event log CRUD

Webhooks — Manage Subscriptions

Create webhook subscriptions to receive HTTP POST notifications when monitors, SSL certificates, or domains change state.

Supported event types

ScopeDescription
monitor.downMonitor went down
monitor.upMonitor recovered
monitor.degradedMonitor response degraded
ssl.expiringSSL certificate expiring soon
ssl.expiredSSL certificate expired
domain.expiringDomain expiring soon
testManual test event

Webhook limits by plan

Plan Max webhooks Event restrictions
Free1monitor.down, monitor.up, test
Starter5All events
Pro20All events
Business50All events

List webhooks

GET /api/v4/webhooks scope: webhook

Returns all webhook subscriptions for the authenticated user.

curl -H "X-API-Key: YOUR_KEY" \
  "https://enterno.io/api/v4/webhooks"

Create webhook

POST /api/v4/webhooks scope: webhook
NameTypeRequiredDescription
urlstringYesDestination URL (HTTPS required)
namestringNoSubscription name (optional)
secretstringNoHMAC-SHA256 secret for payload signature (optional)
eventsarrayNoEvent types to subscribe to (default: all)
curl -X POST "https://enterno.io/api/v4/webhooks" \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/hooks/enterno",
    "name": "Production alerts",
    "events": ["monitor.down", "monitor.up", "ssl.expiring"]
  }'

Response

{
  "data": {
    "id": 12,
    "name": "Production alerts",
    "url": "https://your-app.com/hooks/enterno",
    "secret": "ent_wh_sk_***",
    "events": ["monitor.down", "monitor.up", "ssl.expiring"],
    "is_active": true,
    "created_at": "2026-03-28T06:00:00Z"
  },
  "meta": {"request_id": "...", "duration_ms": 18, "api_version": "4.0"}
}

Update webhook

PUT /api/v4/webhooks?id=N scope: webhook
curl -X PUT "https://enterno.io/api/v4/webhooks?id=12" \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"is_active": false}'

Delete webhook

DELETE /api/v4/webhooks?id=N scope: webhook
curl -X DELETE "https://enterno.io/api/v4/webhooks?id=12" \
  -H "X-API-Key: YOUR_KEY"

Verifying webhook signatures

Every delivery to your webhook URL is signed with your shared secret. Two headers enable request-integrity + replay-protection checks:

Header Value Purpose
X-Enterno-Timestamp Unix timestamp (seconds) at dispatch Reject if > 300 s skew from receiver clock
X-Enterno-Signature-V2 sha256=<hex> HMAC-SHA256(secret, timestamp + "." + body)
X-Enterno-Signature sha256=<hex> Legacy — HMAC over body only, no replay protection. Kept for existing integrations. Will be removed 2026-07-01.

PHP verifier (recommended)

<?php
$secret  = getenv('ENTERNO_WEBHOOK_SECRET');          // your shared secret
$ts      = $_SERVER['HTTP_X_ENTERNO_TIMESTAMP'] ?? '';
$sig     = $_SERVER['HTTP_X_ENTERNO_SIGNATURE_V2'] ?? '';
$body    = file_get_contents('php://input');

// 1. Replay window — reject timestamps older than 5 minutes
if (abs(time() - (int)$ts) > 300) {
    http_response_code(400);
    exit('stale timestamp');
}

// 2. Compute expected signature
$expected = 'sha256=' . hash_hmac('sha256', $ts . '.' . $body, $secret);

// 3. Timing-safe compare
if (!hash_equals($expected, $sig)) {
    http_response_code(401);
    exit('bad signature');
}

$payload = json_decode($body, true);
// …handle the event…

Node.js verifier

const crypto = require('crypto');

function verify(req, secret) {
  const ts   = req.headers['x-enterno-timestamp'];
  const sig  = req.headers['x-enterno-signature-v2'];
  const body = req.rawBody;                             // use a raw-body parser

  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300)
    throw new Error('stale timestamp');

  const expected =
    'sha256=' + crypto.createHmac('sha256', secret)
                      .update(ts + '.' + body)
                      .digest('hex');

  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig)))
    throw new Error('bad signature');
}

Migration window: legacy X-Enterno-Signature (body-only, no timestamp) is still sent alongside V2 until 2026-07-01. If you still depend on it, migrate to V2 before that date.

Send test event

POST /api/v4/webhooks?action=test&id=N scope: webhook
curl -X POST "https://enterno.io/api/v4/webhooks?action=test&id=12" \
  -H "X-API-Key: YOUR_KEY"

Events — Delivery History

View webhook delivery attempts, retry counts, response codes, and failure reasons.

GET /api/v4/events scope: webhook
NameTypeDescription
webhook_idintegerFilter by webhook ID (optional)
event_typestringFilter by event type
statusstringFilter: delivered, retrying, failed, dead, pending
pageintegerPage number (default 1)
per_pageintegerItems per page, max 100 (default 25)
curl -H "X-API-Key: YOUR_KEY" \
  "https://enterno.io/api/v4/events?webhook_id=12&status=failed"

Response

{
  "data": [
    {
      "id": 501,
      "webhook_id": 12,
      "event_type": "monitor.down",
      "status": "delivered",
      "http_status_code": 200,
      "attempts": 1,
      "created_at": "2026-03-28T06:05:00Z",
      "delivered_at": "2026-03-28T06:05:01Z"
    }
  ],
  "meta": {
    "request_id": "...",
    "duration_ms": 12,
    "api_version": "4.0",
    "pagination": {"total": 1, "page": 1, "per_page": 25, "pages": 1}
  }
}

Check — HTTP Header Check

Get HTTP response headers with detailed timing breakdown (DNS, connect, TLS, TTFB).

GET POST /api/v4/check.php scope: check

Parameters

NameTypeRequiredDescription
urlstringYesURL to check
methodstringNoGET, HEAD, POST (default: GET)
followstringNo0/1 (default: 1)
timeoutintegerNo1-30 (default: 15)
uastringNoCustom User-Agent

Example

curl -H "X-API-Key: YOUR_KEY" \
  "https://enterno.io/api/v4/check.php?url=https://example.com"

Response

{
  "data": {
    "url": "https://example.com",
    "final_url": "https://example.com/",
    "method": "GET",
    "code": 200,
    "http_version": "HTTP/2",
    "ip": "93.184.216.34",
    "headers": [...],
    "timing": {"dns_ms":12,"connect_ms":45,"tls_ms":78,"ttfb_ms":120,"transfer_ms":142,"redirect_ms":0,"redirect_count":0},
    "elapsed_ms": 142
  },
  "meta": {"request_id":"req_01...","duration_ms":150,"cached":false,"api_version":"4.0"}
}

DNS — DNS Lookup

Get DNS records with optional DNSSEC validation.

GET POST /api/v4/dns.php Scope: dns

Parameters

NameTypeRequiredDescription
domainstringYesDomain name
typesstringNoComma-separated: A,AAAA,MX,NS,TXT,CNAME,SOA
dnssecstringNo1 to check DNSSEC status

Example

curl -H "X-API-Key: YOUR_KEY" \
  "https://enterno.io/api/v4/dns.php?domain=example.com&dnssec=1"

SSL — SSL/TLS Check

Check SSL certificate with detailed chain information.

GET POST /api/v4/ssl.php Scope: ssl

Parameters

NameTypeRequiredDescription
hoststringYesHostname
portintegerNoPort (default 443)

Example

curl -H "X-API-Key: YOUR_KEY" \
  "https://enterno.io/api/v4/ssl.php?host=example.com"

IP — Geolocation

Determine location, ISP and organization by IP address or domain.

GET POST /api/v4/ip.php Scope: ip

Parameters

NameTypeRequiredDescription
hoststringYesIP address or domain

Example

curl -H "X-API-Key: YOUR_KEY" \
  "https://enterno.io/api/v4/ip.php?host=8.8.8.8"

Ping — Ping, Ports & Traceroute

Ping a host, check ports, or run a traceroute.

GET POST /api/v4/ping.php Scope: ping

Parameters

NameTypeRequiredDescription
hoststringYesHost or IP
actionstringNoping, ports, traceroute (default: ping)
countintegerNoPing count 1-10 (default 4)
portsstringNoComma-separated ports (for action=ports)
max_hopsintegerNoMax hops 1-30 (for action=traceroute)

Example

curl -H "X-API-Key: YOUR_KEY" \
  "https://enterno.io/api/v4/ping.php?host=example.com&action=traceroute"

Health — Website Health Score

Comprehensive website health analysis: security headers (30pts), SSL/TLS (25pts), performance (25pts), best practices (20pts). Returns a score 0-100 with grade A+ to F.

GET POST /api/v4/health.php scope: check

Parameters

NameTypeRequiredDescription
urlstringYesURL to analyze

Example

curl -H "X-API-Key: YOUR_KEY" \
  "https://enterno.io/api/v4/health.php?url=example.com"

Returns score 0–100, letter grade A+–F, Security Headers / SSL / Performance / Best Practices category breakdown, and prioritised recommendations. Cached 2 minutes per URL.

Monitors — CRUD

Create, read, update and delete uptime monitors via API.

List monitors

GET /api/v4/monitors.php scope: monitors
NameTypeDescription
pageintegerPage number (default 1)
per_pageintegerItems per page, max 100 (default 25)
statusstringFilter: up, down, unknown

Get single monitor

GET /api/v4/monitors.php?id=N

Returns monitor details with 20 most recent checks.

Create monitor

POST /api/v4/monitors.php
curl -X POST "https://enterno.io/api/v4/monitors.php" \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Idempotency-Key: create-mon-example-001" \
  -d '{
    "url": "https://example.com",
    "check_type": "http",
    "interval_minutes": 5,
    "expected_code": 200,
    "notify_email": true
  }'

Update monitor

PUT /api/v4/monitors.php?id=N
curl -X PUT "https://enterno.io/api/v4/monitors.php?id=5" \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"interval_minutes": 10, "is_active": false}'

Delete monitor

DELETE /api/v4/monitors.php?id=N
curl -X DELETE "https://enterno.io/api/v4/monitors.php?id=5" \
  -H "X-API-Key: YOUR_KEY"

Batch — async URL checker

Submit up to 100 URLs in one call (plan-dependent: free→5, starter→20, pro→50, business→100). Returns a job_id immediately; poll for results.

Submit a batch

POST /api/v4/batch.php scope: check
curl -X POST "https://enterno.io/api/v4/batch.php" \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "urls": ["https://example.com", "https://example.org"],
    "type": "check"
  }'
{
  "data": {
    "job_id": "bq_01ARZ3...",
    "status": "pending",
    "type": "check",
    "total": 2,
    "poll_url": "/api/v4/batch.php?job_id=bq_01ARZ3...",
    "invalid_urls": []
  },
  "meta": {"request_id":"req_...","duration_ms":8,"api_version":"4.0"}
}

Poll status

GET /api/v4/batch.php?job_id=JOB_ID
curl -H "X-API-Key: YOUR_KEY" \
  "https://enterno.io/api/v4/batch.php?job_id=bq_01ARZ3..."

Status — Public Status Page

Get status page data as JSON. No authentication required when slug is provided.

GET /api/v4/status.php?slug=SLUG public

Example

curl "https://enterno.io/api/v4/status.php?slug=my-company"

Response

{
  "data": {
    "title": "My Company Status",
    "slug": "my-company",
    "overall_status": "operational",
    "monitors_total": 5,
    "monitors_down": 0,
    "monitors": [
      {"id": 1, "name": "example.com", "status": "up", "response_time_ms": 120}
    ],
    "recent_incidents": []
  },
  "meta": {"request_id":"req_...","duration_ms":15,"cached":false,"api_version":"4.0"}
}

Error Codes

HTTP Code Error Code Description
400missing_parameterMissing or invalid parameters
401auth_requiredMissing API key
401invalid_api_keyInvalid or inactive API key
403insufficient_scopeKey does not have the required scope
404not_foundResource not found
405method_not_allowedHTTP method not allowed
409conflictIdempotent request already in progress
429rate_limit_exceededRate limit exceeded
429daily_limit_exceededDaily API request limit exceeded

Migrating from earlier versions

See the full step-by-step guide at /docs/api-v4-migration.md. Quick map below.

FromTo v4Description
/api/v1/{check,dns,ssl,ip,ping}.php/api/v4/{check,dns,ssl,ip,ping}.phpFlat JSON → {data,meta} envelope. Auth header unchanged.
/api/v2/{check,dns,ssl,ip,ping}.php/api/v4/{check,dns,ssl,ip,ping}.php{ok,data,meta}{data,meta}. Error shape changes to {error:{code,message}}.
/api/v3/*/api/v4/*Identical contract — just swap /v3//v4/.
Webhook config (UI only)/api/v4/webhooksProgrammatic webhook CRUD — v4-only.
Delivery log (UI only)/api/v4/eventsWebhook delivery history with pagination and filters.

Timeline: v1 and v2 return 410 Gone after 2026-10-01. v3 remains on a freeze (no new features), no sunset date set — migrate opportunistically. Every v1/v2 response already carries Deprecation + Sunset headers (F2.2 / M-08).

Heartbeat / dead-man's switch

Per-monitor token endpoint for cron jobs and scheduled tasks to ping us regularly. If no ping arrives in the expected interval, the monitor flips to down and alerts fire — same pathway as HTTP/SSL/DNS monitors. No API key required; the token in the URL is the auth.

GET POST HEAD /api/heartbeat/<TOKEN> public

The token is generated when you create a heartbeat monitor in the dashboard. Put the URL into your cron job:

# every 10 minutes
*/10 * * * * curl -fsS https://enterno.io/api/heartbeat/YOUR_TOKEN > /dev/null

Response

{"status":"ok","next_ping_expected":1745197200}

Rate-limited to 30 req/min/IP via getClientIp(). Returns {"status":"ok", ...} for valid + active tokens, same response shape for inactive/unknown tokens (enumeration-safe).

Webhook receivers — verifying our signatures

When enterno.io dispatches a webhook (monitor.down, ssl.expiring, etc.), every POST carries three headers to let you verify authenticity and replay-protect:

HeaderDescription
X-Enterno-EventEvent type: monitor.down, monitor.up, monitor.degraded, ssl.expiring, ssl.expired, domain.expiring, test.
X-Enterno-TimestampUnix timestamp of dispatch. Reject if abs(now - ts) > 300s (replay guard).
X-Enterno-Signature-V2HMAC-SHA256 over "{timestamp}.{raw_body}". Prefix sha256=.
X-Enterno-Signature (legacy)HMAC-SHA256 body-only — sunset 2026-07-01. Migrate to V2 now.

PHP

$raw = file_get_contents(\'php://input\');
$ts  = (int) ($_SERVER[\'HTTP_X_ENTERNO_TIMESTAMP\'] ?? 0);
$sig = $_SERVER[\'HTTP_X_ENTERNO_SIGNATURE_V2\'] ?? \'\';

// 1. replay guard
if (abs(time() - $ts) > 300) { http_response_code(401); exit(\'stale\'); }

// 2. verify signature (timing-safe)
$expected = \'sha256=\' . hash_hmac(\'sha256\', $ts . \'.\' . $raw, $webhookSecret);
if (!hash_equals($expected, $sig)) { http_response_code(401); exit(\'bad sig\'); }

// 3. process event
$event = json_decode($raw, true);
// ... your logic
http_response_code(200);

Node.js

const crypto = require(\'crypto\');
const secret = process.env.ENTERNO_WEBHOOK_SECRET;
app.post(\'/webhook\', express.raw({ type: \'application/json\' }), (req, res) => {
  const ts  = parseInt(req.get(\'X-Enterno-Timestamp\') || \'0\', 10);
  const sig = req.get(\'X-Enterno-Signature-V2\') || \'\';
  if (Math.abs(Date.now() / 1000 - ts) > 300) return res.status(401).send(\'stale\');

  const expected = \'sha256=\' + crypto.createHmac(\'sha256\', secret)
    .update(ts + \'.\' + req.body.toString())
    .digest(\'hex\');
  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig))) {
    return res.status(401).send(\'bad sig\');
  }

  const event = JSON.parse(req.body.toString());
  // ... your logic
  res.status(200).send();
});