Skip to content
NOCERTNOCERTDocs
WebsiteOpen NOCERT

API

The API returns what the Certificates and Sentinels screens show, as JSON, for tools such as Nagios, Icinga, Centreon or Zabbix. It is read-only and included in every plan, Free included.

Owners manage tokens in Settings → API. Select Create token, give it the name of the tool that will use it, and pick an expiration: none, 30 days, 90 days or 1 year.

The token is shown once. Store it in your monitoring tool or secret manager before you close the dialog; if you lose it, revoke it and create another.

A token belongs to the organization, not to the person who created it, so it keeps working after that person leaves. It reads the whole organization, Sentinels included, whatever viewer scopes you set on members. Every owner receives an email when a token is created. An organization holds up to 10 tokens and can create 10 per hour.

Last used in the token list is updated at most every five minutes. Revoking a token, or reaching its expiration date, makes every call with it fail at once.

Send the token in the Authorization header. A token placed in the query string is ignored.

Terminal window
curl -H "Authorization: Bearer $NOCERT_TOKEN" https://app.nocert.io/api/v1/summary

The API is meant for checks that run every few minutes, and the limits are shared by all the tokens of an organization:

  • a burst of up to 60 requests, then one request every 10 seconds on average, which is 6 per minute;
  • at most 4 requests in progress at once.

A burst covers paging through 6,000 certificates. Above a limit, the API answers 429 Too Many Requests with a Retry-After header giving the seconds to wait.

Requests carry no body, and every query parameter must be one the endpoint documents: an unknown, repeated or badly encoded parameter is refused with 400, so a typo cannot silently fall back to a default.

The numbers a monitoring check alerts on, in one call.

Parameter Description
expiring_within_days Horizon of the expiring counter, 1 to 365. Optional, default 30.
{
"generated_at": "2026-10-18T16:00:00Z",
"certificates": {
"total": 120,
"expired": 1,
"expiring": 4,
"expiring_within_days": 30,
"chain_issues": 2,
"next_expiry": {
"fingerprint": "3f1c…",
"name": "www.example.com",
"not_after": "2026-10-30T12:00:00Z",
"days_remaining": 12,
"url": "https://app.nocert.io/certificates/3f1c…"
}
},
"sentinels": { "online": 3, "stale": 0, "offline": 1, "never_seen": 0 }
}

The certificates counted are the leaf certificates of the Active tab of Certificates: excluded certificates never appear, and a certificate replaced or no longer seen on your network leaves the counts. Every counter is computed at generated_at.

  • total counts every one of them, muted ones included. It is not your billed count. The other counters leave out muted certificates, so what you silenced in NoCert does not page you elsewhere. A certificate can be in several counters.
  • expired counts certificates whose not_after is before generated_at, however long ago.
  • expiring counts certificates not expired yet whose days_remaining is between 0 and expiring_within_days, both included. Certificates valid for seven days or less are left out: they renew automatically and are always about to expire. One of them that does expire still counts in expired.
  • next_expiry is the soonest of the certificates expiring would count, with no horizon. It is null when there is none. A check can compare days_remaining with its own warning and critical thresholds in a single call.
  • chain_issues counts invalid or incomplete chains and chains ending at a root that browsers have distrusted.
  • days_remaining counts whole UTC days, as the app does: 0 means the certificate expires today, or already did earlier today. Use expired or not_after to tell the two apart.
  • A Sentinel is online when it reported in the last 7 minutes, stale up to 30 minutes, offline beyond that or when it is disabled, and never_seen when it enrolled but never reported. A Sentinel paused by your plan keeps reporting and stays online.

The same certificates, 100 per page, soonest expiry first.

{
"certificates": [
{
"fingerprint": "3f1c…",
"name": "www.example.com",
"subject_alt_names": ["www.example.com", "example.com"],
"issuer": "R11",
"issuer_organization": "Let's Encrypt",
"not_before": "2026-08-01T12:00:00Z",
"not_after": "2026-10-30T12:00:00Z",
"days_remaining": 12,
"key_algorithm": "ECDSA",
"key_size": 256,
"chain_status": "valid",
"root_distrusted": false,
"muted": false,
"first_seen": "2026-08-01T13:05:00Z",
"url": "https://app.nocert.io/certificates/3f1c…"
}
],
"next_cursor": "WzE3OTM…"
}

To read the next page, pass next_cursor back as ?cursor=. It is null on the last page. Treat the cursor as opaque: its content can change.

  • fingerprint is the SHA-256 of the certificate in lowercase hexadecimal.
  • subject_alt_names holds every Subject Alternative Name as written in the certificate: DNS names, IP addresses, email addresses and URIs, in one list, at most 1000.
  • chain_status is valid, incomplete, invalid or unknown (not verified yet).
  • key_size is 0 when the algorithm has no key size.
  • url opens the certificate in NoCert, where you find every place it is deployed.

key_algorithm, chain_status and the Sentinel connection can gain values over time. Handle one you do not know instead of failing on it.

Every Sentinel of the organization, up to 10,000, in one response.

{
"sentinels": [
{
"id": "6d90…",
"name": "dc1-sentinel",
"hostname": "dc1.internal",
"version": "v0.1.13",
"last_seen_at": "2026-10-18T15:57:00Z",
"connection": "online"
}
]
}

connection follows the rule of the summary. last_seen_at is null for a Sentinel that never reported.

Errors use the application/problem+json format (RFC 9457), with the HTTP status in status and a short explanation in detail.

Status Meaning
400 A parameter or the cursor is invalid.
401 The token is missing, malformed, revoked or expired.
429 Rate limit reached. Wait for Retry-After.
503 Temporary failure on our side, with a Retry-After. Do not treat it as “nothing expires”.

New fields can appear in responses. Existing fields keep their name and meaning, so ignore what you do not use.

A plugin-style check needs curl and jq. It exits 2 on an expired certificate, 1 when one expires within the horizon, and 3 when the API cannot be read.

#!/bin/sh
# Usage: check_nocert [days] NOCERT_TOKEN must be set.
days="${1:-30}"
summary=$(curl -fsS -H "Authorization: Bearer $NOCERT_TOKEN" \
"https://app.nocert.io/api/v1/summary?expiring_within_days=$days") \
|| { echo "UNKNOWN - NoCert API unreachable"; exit 3; }
expired=$(printf '%s' "$summary" | jq -r '.certificates.expired')
expiring=$(printf '%s' "$summary" | jq -r '.certificates.expiring')
perf="expired=$expired expiring=$expiring"
if [ "$expired" -gt 0 ]; then
echo "CRITICAL - $expired expired, $expiring expiring within $days days | $perf"
exit 2
fi
if [ "$expiring" -gt 0 ]; then
echo "WARNING - $expiring expiring within $days days | $perf"
exit 1
fi
echo "OK - nothing expires within $days days | $perf"
exit 0

No template is needed: one HTTP agent item reads the summary and dependent items split it.

  1. Create a secret text macro {$NOCERT_TOKEN} holding the token.
  2. Add an item of type HTTP agent, key nocert.summary, URL https://app.nocert.io/api/v1/summary, header Authorization with value Bearer {$NOCERT_TOKEN}, update interval 5m, type of information Text.
  3. Add dependent items on nocert.summary, type of information Numeric (unsigned), each with one JSONPath preprocessing step: key nocert.expired with $.certificates.expired, key nocert.expiring with $.certificates.expiring, key nocert.sentinels.offline with $.sentinels.offline.
  4. Add triggers on them. On a host named NoCert, last(/NoCert/nocert.expired)>0 with severity High fires on any expired certificate still deployed.