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
Set targets in the dashboard: your own endpoints (JSON) or chat channels (Slack, Mattermost, Rocket.Chat). Webhooks deliver new releases and the BSI/CISA KEV alerts included in your account as soon as detected, regardless of email frequency. “Security updates only” filters ordinary releases and continues to include security alerts.
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"
}Security alerts: advisory.detected (BSI) and vulnerability.detected (CISA KEV)
Security alerts follow the same entitlement rules as email: Pro for BSI; Pro or existing KEV grandfathering for KEV. BSI emits once for newly imported advisories published within the preceding three days; historical imports and revisions do not emit further events. KEV emits new exact matches. Only products already tracked are included. products lists matched products; match_scope: product does not confirm that your installed version is affected. known_exploited is true for KEV and null for BSI (no assertion).
{
"event": "advisory.detected",
"id": "bsi-cladvisory123",
"source": "BSI",
"reference": "WID-SEC-W-2026-EXAMPLE",
"title": "Example product: security advisory",
"cves": ["CVE-2026-0001"],
"products": [{ "name": "Example product", "slug": "example", "url": "https://patchletter.com/de/software/example" }],
"url": "https://example.com/advisory",
"published_at": "2026-10-01T00:00:00.000Z",
"detected_at": "2026-10-01T08:00:00.000Z",
"known_exploited": null,
"security": true,
"match_scope": "product"
}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 event — deduplicate on it. X-Patchletter-Event identifies the event type, also provided as event in generic JSON. KEV emits one event per CVE and matched product; BSI emits one per advisory and target. Existing release payloads remain unchanged.
Fair use
The public API 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.