certdesk-dev Certificate API
The Certificate API from certdesk-dev — 1 operation(s) for certificate.
The Certificate API from certdesk-dev — 1 operation(s) for certificate.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/certdesk-dev:certdesk-dev-certificate-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: certdesk Certificate API
version: 1.8.0
description: 'TLS/SSL certificate diagnostics with a verdict: status OK | WARN | CRITICAL | UNKNOWN, judged from the certificate chain the server actually serves — measured from Korean point A (full chain, CRL revocation) plus point B and an overseas point on the Globalping public measurement network — verification, expiry, renewal point, revocation (CRL), chain, plus security grade, TLS error explanations and free expiry monitoring by email (double opt-in). If the server cannot be measured the status is UNKNOWN; CT logs are then shown only as an estimate and days left is null. No API key. Limit: 30 requests per minute per IP; results cached per domain for 10 minutes (security: 30 minutes). Free and best-effort. Docs: https://certdesk.dev/en/api/ · MCP server: https://certdesk.dev/mcp'
contact:
email: hello@certdesk.dev
url: https://certdesk.dev/en/api/
servers:
- url: https://certdesk.dev/api/v1
tags:
- name: Certificate
paths:
/certificate:
get:
operationId: checkCertificate
summary: Check one domain's TLS certificate in depth
description: 'Verdict for one domain from the certificate chain the server actually serves (measured from a Korean network): status, headline, reasons (each with a knowledge-base link), the served leaf, revocation (CRL), the served chain in the order sent (missing intermediates marked), a newer certificate issued but not deployed (CT logs), and the issuing CA''s incident history. If the server cannot be measured: status UNKNOWN, cert.daysLeft null, CT logs only as `estimate`. Only port 443 for now (other ports → 400 PORT_UNSUPPORTED); internationalized names are converted to punycode; IP addresses and private names → 400. Uncached measurements are limited to 10 per minute per IP (429 MEASURE_LIMIT).'
parameters:
- $ref: '#/components/parameters/domain'
- $ref: '#/components/parameters/port'
- $ref: '#/components/parameters/client'
- name: fresh
in: query
required: false
description: 1 = skip the cache and re-measure now (once per minute per domain → 429 FRESH_LIMIT)
schema:
type: string
enum:
- '1'
- $ref: '#/components/parameters/warnDays'
- $ref: '#/components/parameters/critDays'
responses:
'200':
description: Diagnosis
content:
application/json:
schema:
$ref: '#/components/schemas/CertificateCheck'
'400':
$ref: '#/components/responses/Error'
'429':
$ref: '#/components/responses/RateLimited'
tags:
- Certificate
components:
schemas:
Served:
type:
- object
- 'null'
description: Leaf certificate the server actually sent
properties:
subject:
type: object
issuer:
type: object
serial:
type:
- string
- 'null'
sha256:
type:
- string
- 'null'
san:
type: array
items:
type: string
notBefore:
type:
- string
- 'null'
format: date-time
notAfter:
type: string
format: date-time
lifetimeDays:
type:
- integer
- 'null'
renewalDue:
type:
- string
- 'null'
format: date-time
description: notBefore + 2/3 of lifetime
protocol:
type:
- string
- 'null'
cipher:
type:
- string
- 'null'
ip:
type:
- string
- 'null'
family:
type:
- string
- 'null'
port:
type: integer
chainLength:
type: integer
measuredBy:
type: string
description: kr-a normally; a Globalping point id when point A was unavailable (chainLength and revocation are then null)
starttls:
type:
- string
- 'null'
enum:
- smtp
- imap
- pop3
- ldap
- null
nodes:
type: array
description: 'Other A/AAAA addresses measured from point A (up to 4): leaf sha256, notAfter, same as the measured address'
items:
type: object
properties:
ip:
type: string
family:
type: string
sha256:
type:
- string
- 'null'
notAfter:
type:
- string
- 'null'
same:
type:
- boolean
- 'null'
error:
type:
- string
- 'null'
CertSummary:
type:
- object
- 'null'
properties:
notAfter:
type: string
format: date-time
description: RFC 3339 UTC
daysLeft:
type:
- integer
- 'null'
description: floor((notAfter − now) / 1 day) of the served leaf; null when the server could not be measured
issuer:
type:
- string
- 'null'
source:
type: string
enum:
- served
- ct
newerInCt:
type: boolean
description: Same as `newer != null` on the parent object
ctLatest:
type:
- object
- 'null'
properties:
notAfter:
type: string
issuer:
type:
- string
- 'null'
hoursLeft:
type:
- integer
- 'null'
description: Set when less than 72 hours are left
renewalDue:
type:
- string
- 'null'
format: date-time
estimate:
type: boolean
description: true = CT-log estimate (server not measured)
Thresholds:
type: object
description: Expiry thresholds actually applied
properties:
warnDays:
type: integer
critDays:
type: integer
custom:
type: boolean
description: true when warnDays/critDays were passed
adjusted:
type: boolean
description: true when the defaults were shrunk for a short-lived certificate
Reason:
type: object
properties:
code:
type: string
description: e.g. EXPIRED, NOT_YET_VALID, EXPIRES_SOON (within critDays), HOSTNAME_MISMATCH, SELF_SIGNED, UNTRUSTED_ROOT, INCOMPLETE_CHAIN, ISSUER_NOT_FOUND, EXPIRED_INTERMEDIATE, REVOKED, LEGACY_TLS_ONLY, WEAK_KEY, WEAK_SIGNATURE, CHAIN_ORDER, EXTRA_CERTS, RENEWAL_OVERDUE (within warnDays), NODE_EXPIRING (another node serves a certificate within the thresholds), NODE_MISMATCH (addresses serve different certificates), STARTTLS_UNSUPPORTED, STARTTLS_FAILED, SECONDARY_UNAVAILABLE, NEWER_NOT_DEPLOYED, PROBE_UNAVAILABLE, DNS_NXDOMAIN, DNS_NO_ADDRESS, CONNECT_TIMEOUT, CONNECT_REFUSED, TLS_HANDSHAKE_FAILED … POINT_MISMATCH, REGION_DIFF, DIFFERENT_CERTS, PRIMARY_DOWN, FAMILY_MISMATCH, IPV6_UNREACHABLE, IPV6_NOT_MEASURED …
severity:
type: string
enum:
- critical
- unknown
- warn
- info
message:
type: string
description: One line, Korean
messageEn:
type: string
detail:
type:
- string
- 'null'
description: Raw detail (e.g. missing intermediate names, original error text)
explain:
type:
- string
- 'null'
description: Knowledge-base id for /api/v1/explain
explainUrl:
type:
- string
- 'null'
format: uri
required:
- code
- severity
- message
CertificateCheck:
type: object
properties:
domain:
type: string
tls:
type: object
description: 'Old field kept for compatibility: the overseas measurement point''s real TLS verification on the Globalping network ({kind:''globalping'', ok, error, where}); ok=null when unavailable'
tlsKr:
type:
- object
- 'null'
description: Raw result from the Korean measurement point (served chain without DER, verification, revocation)
cert:
$ref: '#/components/schemas/CertSummary'
chain:
type: array
items:
type: object
description: Served chain in the order the server sent it (source=served), missing intermediates fetched via AIA (source=aia, missing=true) and the trust-store root; CT-based (source=ct-log) only as an estimate
revoked:
type:
- boolean
- 'null'
description: From the served leaf's CRL; null = not checked
incidents:
type: array
items:
type: object
checkedAt:
type: string
format: date-time
status:
type: string
enum:
- OK
- WARN
- CRITICAL
- UNKNOWN
description: OK = no issue; WARN = past renewal point, chain order/extra certs, weak key/signature; CRITICAL = verification failed (any reason), revoked, expired or ≤7 days left, TLS below 1.2 only; UNKNOWN = could not be measured (never treat as OK)
headline:
type: string
description: One-line verdict (Korean)
headlineEn:
type: string
reasons:
type: array
items:
$ref: '#/components/schemas/Reason'
served:
$ref: '#/components/schemas/Served'
revocation:
type:
- object
- 'null'
properties:
status:
type: string
enum:
- good
- revoked
- unknown
method:
type:
- string
- 'null'
url:
type:
- string
- 'null'
checkedAt:
type:
- string
- 'null'
detail:
type:
- string
- 'null'
estimate:
type:
- object
- 'null'
description: 'Only when the server could not be measured: latest certificate in CT logs (an estimate, not the installed certificate)'
newer:
type:
- object
- 'null'
description: A certificate with the same SAN set, issued later and expiring later, found in CT logs but not served (CDN backup certificates from a different CA issued within 14 days are excluded)
measuredFrom:
type: array
description: 'Per-point results: kr-a (own probe, chain and revocation), kr-b (Globalping, KT Seoul), intl-* (Globalping, Tokyo)'
items:
type: object
properties:
id:
type: string
label:
type: string
provider:
type: string
enum:
- certdesk
- globalping
network:
type:
- string
- 'null'
ok:
type:
- boolean
- 'null'
description: Verification passed at this point; null = not measured
reason:
type:
- string
- 'null'
notAfter:
type:
- string
- 'null'
format: date-time
checkedAt:
type:
- string
- 'null'
chainSource:
type:
- string
- 'null'
enum:
- served
- ct-estimate
- null
requesterCountry:
type:
- string
- 'null'
thresholds:
$ref: '#/components/schemas/Thresholds'
todo:
type:
- object
- 'null'
description: What to do now for the most severe reason (Korean, up to 2 lines) and a guide URL; null when OK
properties:
items:
type: array
items:
type: string
url:
type:
- string
- 'null'
port:
type: integer
target:
type: string
description: host, or host:port when the port is not 443
required:
- domain
- status
- headline
- reasons
parameters:
port:
name: port
in: query
required: false
description: 'TLS port (default 443; or pass domain as host:port). Implicit TLS: 443 8443 4443 9443 6443 636 465 993 995 853 5061 3269 8883. STARTTLS: 25 587 (SMTP), 143 (IMAP), 110 (POP3), 389 (LDAP). Other ports → 400 PORT_UNSUPPORTED.'
schema:
type: integer
warnDays:
name: warnDays
in: query
required: false
description: Days before expiry that count as WARN (1-365, default 30). The default shrinks to 1/3 of the lifetime for certificates shorter than 90 days; explicit values are used as given. Also applies to domain registration expiry.
schema:
type: integer
minimum: 1
maximum: 365
domain:
name: domain
in: query
required: true
schema:
type: string
examples:
- example.com
critDays:
name: critDays
in: query
required: false
description: Days before expiry that count as CRITICAL 'expiring soon' (1-365, default 15, must be < warnDays → otherwise 400 INVALID_THRESHOLD). The default shrinks to 1/6 of the lifetime for certificates shorter than 90 days.
schema:
type: integer
minimum: 1
maximum: 365
client:
name: client
in: query
required: false
description: Optional name of your app or tool, used only for aggregate usage counts
schema:
type: string
responses:
Error:
description: Invalid input
content:
application/json:
schema:
type: object
properties:
error:
type: string
code:
type: string
description: EMPTY, INVALID_DOMAIN, INVALID_PORT, PORT_UNSUPPORTED, IP_LITERAL, PRIVATE_ADDRESS (400); MEASURE_LIMIT, FRESH_LIMIT (429)
RateLimited:
description: More than 30 requests per minute from this IP
headers:
retry-after:
schema:
type: integer