SSL Certificate Check API
Last updated: 2026-10-19
POST /api/v2/tls/check with Content-Type: application/json
Completes a real TLS handshake with the target and returns its certificate and connection details. Only the handshake is performed — no HTTP request is sent and no page content is downloaded. Base URL, response format and rate limits are shared across endpoints — see the API documentation.
| Parameter | Description |
|---|---|
host |
Domain or IP literal (IPv4, or IPv6 with or without brackets). Required. Pass a host, not a URL: strip the scheme, path and query first. Unicode domains are converted to punycode. |
port |
TCP port, 1–65535; defaults to 443. |
Domains are resolved through A and AAAA queries (IPv4 first). Up to two connectable addresses are tried, sharing an 8-second total budget; a single handshake times out after 5 seconds and DNS resolution after 3 seconds. Private, loopback, link-local and reserved addresses are never probed. An IP target is connected to directly.
The handshake completes even when the certificate is invalid, so a successful response means TLS was established — check validation for the certificate verdict.
Example:
curl -X POST "https://tcp.lc/api/v2/tls/check" \
-H "Content-Type: application/json" \
-d '{"host":"example.com","port":443}'
Response data fields:
| Field | Description |
|---|---|
host |
Normalized target: a domain (punycode for IDNs) or an IP literal. |
targetType |
domain or ip. |
port |
Port that was checked. |
ip |
Address that actually completed the handshake. |
addresses |
Resolved connectable addresses, IPv4 first; private and reserved addresses are filtered out. For an IP target this is the target itself. |
elapsed |
Server-side time for the whole check, in milliseconds. |
connection |
Negotiated TLS details; see below. |
certificate |
Certificate details; see below. |
validation |
Certificate verdict; see below. |
connection fields:
| Field | Description |
|---|---|
version |
Negotiated TLS version, such as TLSv1.3 or TLSv1.2. |
cipher |
Negotiated cipher suite, such as TLS_AES_128_GCM_SHA256. |
forwardSecrecy |
true when the connection uses forward secrecy (all TLS 1.3 suites, or ECDHE/DHE on older versions). |
alpn |
ALPN result (h2 or http/1.1), or null when the server ignored ALPN. |
http2 |
true when ALPN selected h2. |
certificate fields:
| Field | Description |
|---|---|
subject |
Certificate subject as a single-line RDN string, usually starting with CN=. |
issuer |
Issuing certificate authority, also as an RDN string. |
validFrom |
Start of the validity period, ISO 8601. |
validTo |
End of the validity period, ISO 8601. |
daysRemaining |
Days until validTo, rounded down; negative when the certificate has expired. |
san |
DNS names from the subject alternative name extension, wildcards preserved. |
publicKeyAlgorithm |
RSA, RSA-PSS, ECDSA, Ed25519, Ed448 or DSA. |
publicKeyBits |
Key size, such as 2048 or 256; null when unknown. |
publicKeyCurve |
Named curve for EC keys, such as prime256v1; null otherwise. |
validation fields:
| Field | Description |
|---|---|
valid |
true when the chain is trusted, the certificate is within its validity period and it matches the hostname. |
issues |
Problem categories; empty when valid is true. |
errorCode |
Raw OpenSSL/Node error code, such as CERT_HAS_EXPIRED, for troubleshooting; null when valid. |
issues values:
| Value | Meaning |
|---|---|
expired |
The certificate has expired. |
notYetValid |
The validity period starts in the future. |
hostname |
The certificate does not match the requested hostname. |
selfSigned |
The certificate is self-signed. |
untrusted |
The chain is not trusted, for example a missing intermediate certificate or an unknown issuer. |
unknown |
Validation failed for another reason. |
Example:
{
"code": 200,
"msg": "ok",
"data": {
"host": "example.com",
"targetType": "domain",
"port": 443,
"ip": "93.184.215.14",
"addresses": ["93.184.215.14", "2606:2800:21f:cb07:6820:80da:af6b:8b2c"],
"elapsed": 182,
"connection": {
"version": "TLSv1.3",
"cipher": "TLS_AES_128_GCM_SHA256",
"forwardSecrecy": true,
"alpn": "http/1.1",
"http2": false
},
"certificate": {
"subject": "CN=example.com",
"issuer": "C=US, O=DigiCert Inc, CN=DigiCert TLS RSA SHA256 2020 CA1",
"validFrom": "2026-01-01T00:00:00.000Z",
"validTo": "2027-01-01T23:59:59.000Z",
"daysRemaining": 74,
"san": ["example.com", "www.example.com"],
"publicKeyAlgorithm": "RSA",
"publicKeyBits": 2048,
"publicKeyCurve": null
},
"validation": {
"valid": true,
"issues": [],
"errorCode": null
}
}
}
An invalid certificate is not an API error. The response is still code: 200; the verdict is in validation. Only that object is shown here:
{
"code": 200,
"msg": "ok",
"data": {
"validation": {
"valid": false,
"issues": ["expired"],
"errorCode": "CERT_HAS_EXPIRED"
}
}
}
Errors
Failures use code: -1000, with msg set to a stable identifier. data.args may carry details.
msg |
Meaning |
|---|---|
api.param.host.invalid |
host is missing or cannot be parsed as a domain or IP literal. data.args.host carries the input. |
api.param.port.invalid |
The port is not an integer between 1 and 65535. data.args.port carries it. |
api.param.host.private |
Every resolved address is private or reserved, so it cannot be probed. |
api.notfound.domain |
The domain does not exist (NXDOMAIN). data.args.name carries it. |
api.notfound.address |
The domain has no A or AAAA records. data.args.name carries it. |
api.upstream.timeout |
DNS resolution timed out. |
api.upstream.error |
DNS resolution failed for another reason. |
api.upstream.tls.timeout |
The TLS handshake timed out. |
api.upstream.tls.refused |
The connection was refused — nothing is listening on the port, or TLS is not enabled. |
api.upstream.tls.handshake |
The TLS handshake failed; the server may not support HTTPS or its TLS configuration is incompatible. |
api.upstream.tls.error |
The handshake failed for another network reason. |
api.rate.limitedDaily |
The daily quota is exhausted. A Retry-After header is included. |
api.server.internal |
Server or configuration error. |
Example:
{
"code": -1000,
"msg": "api.param.host.private",
"data": null
}
Notes
- For an IP target, hostname matching is skipped because an IP may not appear in the certificate SAN. Expiry and chain trust are still verified.
- Behind a CDN or a domain with several addresses, different edge nodes can serve different certificates.
ipreports the address that was actually used. validationcovers chain trust, validity dates and hostname matching. Revocation (CRL/OCSP) is not checked.- Only the TLS handshake is made; the server never sends an HTTP request.
Rate limits and caching
Rate limiting is per client IP: 10 requests per minute and 100 per day. Every request counts, including failed checks. The daily quota resets at 00:00 UTC+8. When the per-minute limit is exceeded, the endpoint returns api.rate.limited; when the daily quota is exhausted, it returns api.rate.limitedDaily. Both include a Retry-After header in seconds.
Successful responses carry Cache-Control: public, max-age=300, s-maxage=600. Error responses are sent with Cache-Control: no-store.