PEM certificate decoder API
Decodes one PEM certificate: subject, issuer, validity and days left, DNS and IP names, key type and size, signature algorithm, CA flag, key usages, self-signed and SHA-256 and SHA-1 fingerprints. Email addresses are never returned. Nothing is fetched, so trust and revocation are not checked.
POST https://api.lookproof.dev/v1/tls/pem-decode
Updated 2026-10-09
Examples use the Base (eip155:8453) network.
Input
A JSON body with content-type: application/json.
| Name | Type | Required | Limits | Description |
|---|---|---|---|---|
pem | string | Yes | 1–16384 characters | One PEM CERTIFICATE block, at most 16384 characters. Private keys are refused. |
Example request
curl -i -X POST https://api.lookproof.dev/v1/tls/pem-decode \
-H 'content-type: application/json' \
-d '{"pem": "-----BEGIN CERTIFICATE-----\nMIIBqjCCAVCgAwIBAgIDDoo/MAoGCCqGSM49BAMCMB0xGzAZBgNVBAMMEkV4YW1w\nbGUgSXNzdWluZyBDQTAeFw0yNjA5MDEwMDAwMDBaFw0yNjExMzAwMDAwMDBaMDAx\nGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTEUMBIGA1UECgwLRXhhbXBsZSBMdGQw\nWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATyMmt7KcF7ouU025TiogjNkCQ2iiS/\n4t5guVuIjznTSD3p0AA45RGsaR10XOStJvi9BN4ruU9SHi9OkKpyP7Y7o2wwajAM\nBgNVHRMBAf8EAjAAMEUGA1UdEQQ+MDyCD3d3dy5leGFtcGxlLmNvbYILZXhhbXBs\nZS5jb22HBMAAAgqBFmhvc3RtYXN0ZXJAZXhhbXBsZS5jb20wEwYDVR0lBAwwCgYI\nKwYBBQUHAwEwCgYIKoZIzj0EAwIDSAAwRQIhAN6TOxiBdfALdsoU3cuapoJrFFm1\neOCqJSG8/3SW9/LOAiAaGfFCWjWof5LnsIGPntTa6TGwqFHv/wxuOpq37L+PNw==\n-----END CERTIFICATE-----\n"}'import asyncio
import os
from eth_account import Account
from x402 import x402Client
from x402.http.clients import x402HttpxClient
from x402.mechanisms.evm import EthAccountSigner
from x402.mechanisms.evm.exact import register_exact_evm_client
async def main() -> None:
signer = EthAccountSigner(Account.from_key(os.environ["EVM_PRIVATE_KEY"]))
client = register_exact_evm_client(x402Client(), signer, networks="eip155:8453")
async with x402HttpxClient(client) as http:
response = await http.post('https://api.lookproof.dev/v1/tls/pem-decode', json={'pem': '-----BEGIN CERTIFICATE-----\nMIIBqjCCAVCgAwIBAgIDDoo/MAoGCCqGSM49BAMCMB0xGzAZBgNVBAMMEkV4YW1w\nbGUgSXNzdWluZyBDQTAeFw0yNjA5MDEwMDAwMDBaFw0yNjExMzAwMDAwMDBaMDAx\nGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTEUMBIGA1UECgwLRXhhbXBsZSBMdGQw\nWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATyMmt7KcF7ouU025TiogjNkCQ2iiS/\n4t5guVuIjznTSD3p0AA45RGsaR10XOStJvi9BN4ruU9SHi9OkKpyP7Y7o2wwajAM\nBgNVHRMBAf8EAjAAMEUGA1UdEQQ+MDyCD3d3dy5leGFtcGxlLmNvbYILZXhhbXBs\nZS5jb22HBMAAAgqBFmhvc3RtYXN0ZXJAZXhhbXBsZS5jb20wEwYDVR0lBAwwCgYI\nKwYBBQUHAwEwCgYIKoZIzj0EAwIDSAAwRQIhAN6TOxiBdfALdsoU3cuapoJrFFm1\neOCqJSG8/3SW9/LOAiAaGfFCWjWof5LnsIGPntTa6TGwqFHv/wxuOpq37L+PNw==\n-----END CERTIFICATE-----\n'})
response.raise_for_status()
print(response.json())
asyncio.run(main())
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.EVM_PRIVATE_KEY);
const fetchWithPayment = wrapFetchWithPaymentFromConfig(fetch, {
schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }],
});
const response = await fetchWithPayment("https://api.lookproof.dev/v1/tls/pem-decode", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({"pem": "-----BEGIN CERTIFICATE-----\nMIIBqjCCAVCgAwIBAgIDDoo/MAoGCCqGSM49BAMCMB0xGzAZBgNVBAMMEkV4YW1w\nbGUgSXNzdWluZyBDQTAeFw0yNjA5MDEwMDAwMDBaFw0yNjExMzAwMDAwMDBaMDAx\nGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTEUMBIGA1UECgwLRXhhbXBsZSBMdGQw\nWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATyMmt7KcF7ouU025TiogjNkCQ2iiS/\n4t5guVuIjznTSD3p0AA45RGsaR10XOStJvi9BN4ruU9SHi9OkKpyP7Y7o2wwajAM\nBgNVHRMBAf8EAjAAMEUGA1UdEQQ+MDyCD3d3dy5leGFtcGxlLmNvbYILZXhhbXBs\nZS5jb22HBMAAAgqBFmhvc3RtYXN0ZXJAZXhhbXBsZS5jb20wEwYDVR0lBAwwCgYI\nKwYBBQUHAwEwCgYIKoZIzj0EAwIDSAAwRQIhAN6TOxiBdfALdsoU3cuapoJrFFm1\neOCqJSG8/3SW9/LOAiAaGfFCWjWof5LnsIGPntTa6TGwqFHv/wxuOpq37L+PNw==\n-----END CERTIFICATE-----\n"}),
});
console.log(await response.json());
curl: unpaid request — returns 402 with the price (PAYMENT-REQUIRED). The Python and JavaScript clients pay it and send the request again. Only ordinary wallets (EOA) can pay; smart-contract wallets get a 402 without charge. See payments.
Example response
{
"checked_at": "2026-10-24T12:00:00Z",
"data": {
"days_remaining": 36,
"decoded": true,
"extended_key_usage": [
"serverAuth"
],
"is_ca": false,
"issuer": "CN=Example Issuing CA",
"key_bits": 256,
"key_type": "ec",
"key_usage": [],
"not_after": "2026-11-30T00:00:00Z",
"not_before": "2026-09-01T00:00:00Z",
"notes": [
"Nothing is fetched: the chain, revocation and trust in the issuer are not checked."
],
"omitted": [
"san_email"
],
"path_length": null,
"san_count": 4,
"san_dns": [
"www.example.com",
"example.com"
],
"san_ip": [
"192.0.2.10"
],
"self_issued": false,
"self_signed": false,
"serial_number": "e8a3f",
"sha1_fingerprint": "75712bf2227c215c28a2e214ed2f79c007d70769",
"sha256_fingerprint": "4d0289557971058d3eeee0dea9449d2da6709778c69c92849f2996e4f3120227",
"signature_algorithm": "ecdsa-with-SHA256",
"subject": "O=Example Ltd,CN=www.example.com",
"subject_cn": "www.example.com",
"validity_days": 90,
"version": "v3"
},
"endpoint": "tls.pem_decode",
"findings": [
{
"code": "PEM_PERSONAL_DATA_OMITTED",
"evidence": {
"count": 1,
"items": [
{
"fields": "san_email"
}
]
},
"explanation": "The block holds an email address (emailAddress in a name, or an rfc822Name in the subject alternative names) or a name type that can identify a person, such as a user principal name. Lookproof never returns or stores those. The evidence lists which kinds were present; all other fields are shown.",
"message": "Email addresses and personal name types were left out of the result.",
"recommendation": null,
"severity": "info"
}
],
"meta": {
"cache_hit": false,
"duration_ms": 42,
"fresh_until": "2026-10-24T12:05:00Z",
"observed_at": "2026-10-24T12:00:00Z",
"resolver": null,
"source": "offline"
},
"request_id": "req_aaaaaaaaaaaaaaaaaaaaaaaaaa",
"schema_version": "1.0",
"status": "info"
}
Response fields
| Field | Meaning |
|---|---|
request_id | Unique id of this request. Quote it when you contact support. |
endpoint | The endpoint id, for example mail.spf. |
schema_version | Version of the response format, currently 1.0. |
checked_at | When the check ran (ISO 8601, UTC). |
status | ok, info, warning or error from the worst finding, or unknown when the check was incomplete. unknown is not an error in the domain. |
data | The check's own data. Its fields are listed below. |
findings | What was found: code, severity, message, explanation and evidence. |
meta | Duration, cache use, resolver and how long the result stays fresh. |
Fields in data
| Field | Type |
|---|---|
decoded | boolean |
subject | string or null |
subject_cn | string or null |
issuer | string or null |
serial_number | string or null |
version | one of v1, v3 or null |
not_before | string or null |
not_after | string or null |
days_remaining | integer or null |
validity_days | integer or null |
san_dns | array of string |
san_ip | array of string |
san_count | integer |
omitted | array of one of subject_email, issuer_email, san_email, san_other |
key_type | one of rsa, ec, ed25519, ed448, dsa, other or null |
key_bits | integer or null |
signature_algorithm | string or null |
self_issued | boolean |
self_signed | boolean |
is_ca | boolean or null |
path_length | integer or null |
key_usage | array of string |
extended_key_usage | array of string |
sha256_fingerprint | string or null |
sha1_fingerprint | string or null |
notes | array of string |
Status codes
| Status | Code | Meaning | Do you pay? |
|---|---|---|---|
| 200 | — | The result is delivered, also when it has negative findings. | Yes: charged once per payment; redelivery of the same payment is free |
| 402 | — | Payment is required, or the attached payment was refused. The body states the price. | No |
| 400 | bad_request | The request is malformed. | No |
| 409 | payment_in_progress, payment_bound_to_other_request | This payment is already being processed, or it is bound to a different request. No new payment is taken. | No |
| 413 | payload_too_large | The body is larger than 64 KB. | No |
| 422 | invalid_input | The input failed validation on a request that carries a payment; nothing is charged. Without a payment, invalid input gets a 402 whose error field names the problem. | No |
| 429 | rate_limited | Too many requests from this IP address or payer. | No |
| 502 | check_failed | The check could not be completed. | No |
| 503 | busy, unavailable, payments_unavailable | The service is busy or temporarily unavailable. | No |
| 504 | deadline_exceeded | The check ran out of time. | No |
Errors use the common error body. See errors.
Findings this endpoint can return
Certificates (6)
- Error
CERT_EXPIREDThe pasted certificate has expired. - Warning
CERT_EXPIRING_SOONThe pasted certificate expires within 30 days. - Info
CERT_LONG_VALIDITYThe certificate is valid longer than public TLS certificates may be. - Error
CERT_NOT_YET_VALIDThe pasted certificate is not valid yet. - Warning
CERT_NO_SANThe certificate names no host in its subject alternative names. - Info
CERT_SELF_SIGNEDThe pasted certificate is self-signed.
PEM data (4)
- Error
PEM_INVALIDThe PEM block cannot be decoded. - Info
PEM_PERSONAL_DATA_OMITTEDEmail addresses and personal name types were left out of the result. - Warning
PEM_WEAK_KEYThe pasted certificate or request uses a weak public key. - Warning
PEM_WEAK_SIGNATUREThe pasted certificate or request is signed with SHA-1 or MD5.