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.

GET/api/v1/products

Product 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 }
}
GET/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
}
GET/api/v1/products/{slug}/releases

Release 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 }
}
GET/api/v1/products/{slug}/versions

Known 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"]
}
GET/api/v1/products/{slug}/cves

CVE 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 }
}
GET/api/v1/products/{slug}/eol

EOL 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"
    }
  ]
}
GET/api/v1/releases/recent

Recent 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 }
}
GET/api/v1/cves

Global 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 }
}
GET/api/v1/categories

Categories

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.