The patchletter API returns update data as JSON
patchletter answers, via GET, which Proxmox VE version is current, what came before it, which KEV hits are attached and when support ends. Reading is free and needs no account.
Auth & rate limits
A personal API key is optional and raises your rate limit. You create one in the dashboard.
Without a key
60 req/min · IP
60 requests/minute per IP address. The default for every unauthenticated request.
With an API key
600 req/min · Key
600 requests/minute per key — 10× more. Free, one key per account.
Create an API key in the dashboard →Send the key as a bearer token or as its own header — both are equivalent:
curl -H "Authorization: Bearer pk_live_YOUR_KEY" "https://patchletter.com/api/v1/products"
# äquivalent:
curl -H "X-API-Key: pk_live_YOUR_KEY" "https://patchletter.com/api/v1/products"Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (a Unix timestamp). Exceeding the limit returns 429 rate_limited with a Retry-After header:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1770000000
Retry-After: 23
{ "error": { "code": "rate_limited", "message": "Rate limit exceeded — try again later." } }Resources
All endpoints live under the base URL https://patchletter.com/api/v1 and return JSON. Lists are wrapped as { data, pagination }; pagination.nextCursor is an opaque string — pass it back to ?cursor= as-is, don't parse it.
/api/v1/productsProduct list
Filtered, sorted, paginated list of all active products. Filters: category (exact category slug), q (free-text search over name/vendor/slug), cursor, limit (1-100, default 50).
Example
curl "https://patchletter.com/api/v1/products?category=server-datenbanken&limit=2"Example response
{
"data": [
{
"slug": "nginx",
"name": "nginx",
"vendor": "F5 / nginx",
"category": "server-datenbanken",
"subcategory": "Reverse Proxy",
"websiteUrl": "https://nginx.org",
"isHardware": false,
"latestVersion": "1.27.4",
"latestReleasedAt": "2026-02-05T00:00:00.000Z"
},
{
"slug": "postgresql",
"name": "PostgreSQL",
"vendor": "PostgreSQL Global Development Group",
"category": "server-datenbanken",
"subcategory": null,
"websiteUrl": "https://www.postgresql.org",
"isHardware": false,
"latestVersion": "17.2",
"latestReleasedAt": "2026-01-09T00:00:00.000Z"
}
],
"pagination": { "nextCursor": "postgresql", "limit": 2 }
}/api/v1/products/{slug}Product detail
A single product including its latest version, channel and EOL summary. eol is null when the product isn't tracked on endoflife.date. Unknown slug → 404.
Example
curl "https://patchletter.com/api/v1/products/nginx"Example response
{
"slug": "nginx",
"name": "nginx",
"vendor": "F5 / nginx",
"category": "server-datenbanken",
"subcategory": "Reverse Proxy",
"description": "Webserver und Reverse Proxy — überall.",
"websiteUrl": "https://nginx.org",
"isHardware": false,
"latestVersion": "1.27.4",
"latestChannel": "STABLE",
"latestReleasedAt": "2026-02-05T00:00:00.000Z",
"eol": null
}/api/v1/products/{slug}/releasesRelease history
Chronological release list for a product, newest version first. Filters: channel (STABLE, LTS, BETA, SECURITY, FIRMWARE), cursor, limit.
Example
curl "https://patchletter.com/api/v1/products/nginx/releases?limit=2"Example response
{
"data": [
{
"version": "1.27.4",
"versionNorm": "1.27.4",
"channel": "STABLE",
"releasedAt": "2026-02-05T00:00:00.000Z",
"detectedAt": "2026-02-06T03:00:12.000Z",
"notesUrl": "https://nginx.org/en/CHANGES",
"summary": "Security fix for a request-smuggling edge case in HTTP/2 handling.",
"isSecurity": true
},
{
"version": "1.27.3",
"versionNorm": "1.27.3",
"channel": "STABLE",
"releasedAt": "2025-12-16T00:00:00.000Z",
"detectedAt": "2025-12-17T03:00:05.000Z",
"notesUrl": "https://nginx.org/en/CHANGES",
"summary": null,
"isSecurity": false
}
],
"pagination": { "nextCursor": "eyJ2ZXJzaW9uTm9ybSI6IjEuMjcuMyIsImlkIjoiY2xxeHh4In0", "limit": 2 }
}/api/v1/products/{slug}/versionsKnown versions
Deduplicated dropdown vocabulary of all known version strings for a product ("your version"), prioritizing STABLE/LTS.
Example
curl "https://patchletter.com/api/v1/products/nginx/versions"Example response
{
"versions": ["1.27.4", "1.27.3", "1.26.3", "1.26.2", "1.24.0"]
}/api/v1/products/{slug}/cvesCVE hits for a product
CVE/KEV feed for a single product, newest detection first. Filters: severity (CRITICAL, HIGH, MEDIUM, LOW — case-insensitive), cursor, limit. severity/cvss come from the NVD and are null while no rating exists there yet.
Example
curl "https://patchletter.com/api/v1/products/nginx/cves?severity=high"Example response
{
"data": [
{
"cveId": "CVE-2026-31337",
"title": "Example: heap buffer overflow when parsing a malformed HTTP/2 header block",
"severity": "HIGH",
"cvss": 8.6,
"knownExploited": true,
"ransomware": false,
"dueDate": "2026-08-01T00:00:00.000Z",
"publishedAt": "2026-07-10T00:00:00.000Z",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-31337"
}
],
"pagination": { "nextCursor": null, "limit": 50 }
}/api/v1/products/{slug}/eolEOL cycles for a product
Raw cycles from the endoflife.date cache. cycles is an empty array (not an error) when the product isn't listed there.
Example
curl "https://patchletter.com/api/v1/products/postgresql/eol"Example response
{
"slug": "postgresql",
"name": "PostgreSQL",
"cycles": [
{
"cycle": "17",
"releaseDate": "2024-09-26",
"eol": "2029-11-08",
"latest": "17.2",
"lts": false,
"support": "2027-11-11"
},
{
"cycle": "16",
"releaseDate": "2023-09-14",
"eol": "2028-11-09",
"latest": "16.6",
"lts": false,
"support": "2026-11-12"
}
]
}/api/v1/releases/recentRecent releases (across all products)
Newest release detections across all active products, optionally security-only (securityOnly=1). Filters: cursor, limit.
Example
curl "https://patchletter.com/api/v1/releases/recent?securityOnly=1&limit=2"Example response
{
"data": [
{
"product": { "slug": "nginx", "name": "nginx" },
"version": "1.27.4",
"prevVersion": "1.27.3",
"channel": "STABLE",
"isSecurity": true,
"date": "2026-02-05T00:00:00.000Z"
},
{
"product": { "slug": "fortios", "name": "FortiOS" },
"version": "7.6.3",
"prevVersion": "7.6.2",
"channel": "FIRMWARE",
"isSecurity": true,
"date": "2026-02-03T00:00:00.000Z"
}
],
"pagination": { "nextCursor": "eyJkZXRlY3RlZEF0IjoiMjAyNi0wMi0wM1QwMDowMDowMFoiLCJpZCI6ImNseXh4eHgifQ", "limit": 2 }
}/api/v1/cvesGlobal CVE/KEV feed
CVE/KEV hits across all active products, with a product reference on every row. Filters: severity, cursor, limit.
Example
curl "https://patchletter.com/api/v1/cves?severity=critical&limit=1"Example response
{
"data": [
{
"cveId": "CVE-2026-31337",
"title": "Example: heap buffer overflow when parsing a malformed HTTP/2 header block",
"severity": "CRITICAL",
"cvss": 9.1,
"knownExploited": true,
"ransomware": true,
"dueDate": "2026-08-01T00:00:00.000Z",
"publishedAt": "2026-07-10T00:00:00.000Z",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-31337",
"product": { "slug": "nginx", "name": "nginx" }
}
],
"pagination": { "nextCursor": null, "limit": 1 }
}/api/v1/categoriesCategories
The full, stable category list including the count of active products. Unpaginated — pagination.nextCursor is always null.
Example
curl "https://patchletter.com/api/v1/categories"Example response
{
"data": [
{ "slug": "browser-clients", "label": "Browser & Clients", "labelEn": "Browsers & Clients", "count": 14 },
{ "slug": "betriebssysteme", "label": "Betriebssysteme", "labelEn": "Operating Systems", "count": 22 }
],
"pagination": { "nextCursor": null, "limit": 2 }
}Machine-readable specification
The full OpenAPI 3.1 specification documents every endpoint, parameter and field in machine-readable form — perfect for generating API clients or importing into Postman/Insomnia.
View GET /api/v1/openapi.json →Webhooks — instead of polling
Rather than querying the API, you can have every detected update pushed to you: to your own endpoint (JSON) or straight into a chat channel (Slack, Mattermost, Rocket.Chat). Set targets up in the dashboard. Webhooks arrive immediately for every update — no matter how often you receive emails.
Example payload (format “JSON generic”)
POST /dein-endpunkt
X-Patchletter-Event: release.detected
X-Patchletter-Signature: sha256=1f8ac10f23c5b5bc11678d7…
X-Patchletter-Timestamp: 1786181400
{
"event": "release.detected",
"id": "clr123",
"product": { "name": "Proxmox VE", "slug": "proxmox-ve", "url": "https://patchletter.com/de/software/proxmox-ve" },
"version": "9.2",
"previous_version": "9.1",
"channel": "STABLE",
"security": true,
"notes_url": "https://example.com/notes",
"detected_at": "2026-08-08T09:30:00.000Z"
}Verifying the signature
Every delivery carries X-Patchletter-Signature (sha256=…) and X-Patchletter-Timestamp. We sign the HMAC-SHA256 over “<timestamp>.<body>” with the secret from your dashboard. Reject anything older than a few minutes — otherwise an intercepted message could be replayed.
import { createHmac, timingSafeEqual } from "node:crypto";
function istEcht(koerper, signatur, zeitstempel, secret) {
// Alles aelter als fuenf Minuten verwerfen.
if (Math.abs(Date.now() / 1000 - Number(zeitstempel)) > 300) return false;
const erwartet =
"sha256=" + createHmac("sha256", secret).update(zeitstempel + "." + koerper).digest("hex");
const a = Buffer.from(erwartet);
const b = Buffer.from(signatur);
// Laengenvergleich zuerst: timingSafeEqual wirft bei ungleicher Laenge.
return a.length === b.length && timingSafeEqual(a, b);
}Delivery is at-least-once: if your response gets lost on the way back, you may see the same message again. The id field is stable per release — deduplicate on it.
Fair use
patchletter is free and built on open sources — vendor release notes, the CISA KEV catalog and endoflife.date. Please be fair: cache responses (Cache-Control is set), don't poll more often than you need to, and credit patchletter if you republish the data. Need more volume? hello@patchletter.com.
Send your first request
A curl against /api/v1/products is enough to get going. If you query more often, create a free API key in the dashboard.