Security — entry 020 of 30
Microsoft Security Response Center (MSRC)
MSRC's CVRF API serves Microsoft's own machine-readable security-update bulletins: list every bulletin's ID and dates from `/updates`, then pull one full document by ID (e.g. "2026-Jan") from `/cvrf/{id}` -- as CVRF XML by default, or the same CVRF document serialised to JSON under an `Accept: application/json` header. It's a fully public, unauthenticated REST endpoint with no signup or key required, backing tools like the community MsrcSecurityUpdates PowerShell module. Two traps confirmed live: the OData-shaped response envelope is misleading (most `$filter`/`$top` syntax 500s, and there's no CVE-to-bulletin lookup at all), and the separately documented `/csaf/{id}` route rejected all 17 id formats probed with "Invalid ID format" -- so despite the CSAF branding, the JSON this API actually returns is CVRF, not CSAF. The old stored URL pointed at the unrelated Report Abuse API's developer page; the live developer portal for this security-updates API now redirects straight to its published OpenAPI/Swagger definition.
MSRC's CVRF API serves Microsoft's own security-update bulletins. /updates lists every bulletin's ID and dates; /cvrf/{id} (e.g. 2026-Jan) fetches one full bulletin's complete vulnerability detail, as CVRF XML by default or the same CVRF document serialised to JSON under an Accept: application/json header. Despite the API's name, that JSON is not CSAF — see the gotchas. The response also carries an @odata.context field that makes it look like a full OData service, but most OData query syntax is not actually implemented — check the gotchas below before reaching for $filter or $top on anything but the two documented date fields.
GreatAPIs Score
Auth quickstart
- No API key, signup, or auth header is required — both endpoints below are public, keyless reads, confirmed live.
/cvrf/{id}defaults to XML, and the format is selected by theAcceptheader rather than by the URL. To get JSON instead, send an explicitAccept: application/jsonrequest header — confirmed live in this run: a bare request returnscontent-type: application/xml, while adding that header on the same URL returnscontent-type: application/jsonwith the same document reshaped. Query-parameter format selection does not work:$format=json,%24format=json,format=json,$format=application/json, andapi-version=v3.0&$format=jsonwere each probed on/cvrf/1999-Sepand all five were silently ignored, still returningapplication/xmlwith a byte-identical 18,699-byte body; a.jsonsuffix on the id (/cvrf/1999-Sep.json) instead returns404 text/plainwith the bodyAn exception was thrown.
Your key is stored only in this browser (localStorage) and sent directly to the API — never to greatapis.
List bulletins filtered by initial release date
GEThttps://api.msrc.microsoft.com/cvrf/v3.0/updates?$filter=InitialReleaseDate gt 2026-01-01
{"@odata.context":"https://api.msrc.microsoft.com/$metadata#Updates","value":[{"ID":"2026-Apr","Alias":"2026-Apr","DocumentTitle":"April 2026 Security Updates","Severity":null,"InitialReleaseDate":"2026-04-14T07:00:00Z","CurrentReleaseDate":"2026-07-12T14:49:49Z","CvrfUrl":"https://api.msrc.microsoft.com/cvrf/v3.0/cvrf/2026-Apr"},{"ID":"2026-Feb","Alias":"2026-Feb","DocumentTitle":"February 2026 Security Updates","Severity":null,"InitialReleaseDate":"2026-02-10T08:00:00Z","CurrentReleaseDate":"2026-07-12T15:02:20Z","CvrfUrl":"https://api.msrc.microsoft.com/cvrf/v3.0/cvrf/2026-Feb"},{"ID":"2026-Jan","Alias":"2026-Jan","DocumentTitle":"January 2026 Security Updates","Severity":null,"InitialReleaseDate":"2026-01-13T08:00:00Z","CurrentReleaseDate":"2026-07-13T01:44:37Z","CvrfUrl":"https://api.msrc.microsoft.com/cvrf/v3.0/cvrf/2026-Jan"},{"ID":"2026-Jul","Alias":"2026-Jul","DocumentTitle":"July 2026 Security Updates","Severity":null,"InitialReleaseDate":"2026-07-14T07:00:00Z","CurrentReleaseDate":"2026-07-25T00:29:54Z","CvrfUrl":"https://api.msrc.microsoft.com/cvrf/v3.0/cvrf/2026-Jul"},{"ID":"2026-Jun","Alias":"2026-Jun","DocumentTitle":"June 2026 Security Updates","Severity":null,"InitialReleaseDate":"2026-06-09T07:00:00Z","CurrentReleaseDate":"2026-07-21T14:40:56Z","CvrfUrl":"https://api.msrc.microsoft.com/cvrf/v3.0/cvrf/2026-Jun"},{"ID":"2026-Mar","Alias":"2026-Mar","DocumentTitle":"March 2026 Security Updates","Severity":null,"InitialReleaseDate":"2026-03-10T07:00:00Z","CurrentReleaseDate":"2026-07-12T14:41:49Z","CvrfUrl":"https://api.msrc.microsoft.com/cvrf/v3.0/cvrf/2026-Mar"},{"ID":"2026-May","Alias":"2026-May","DocumentTitle":"May 2026 Security Updates","Severity":null,"InitialReleaseDate":"2026-05-12T07:00:00Z","CurrentReleaseDate":"2026-07-15T01:39:24Z","CvrfUrl":"https://api.msrc.microsoft.com/cvrf/v3.0/cvrf/2026-May"}]}This exact $filter is the one OData query this run confirmed actually works (see gotchas) — captured directly from a live request, unmodified. Without any filter, /updates returns every bulletin back to 1999 (48,099 bytes at probe time), which is why this example filters instead of truncating.
Developer reference
https://api.msrc.microsoft.com/cvrf/v3.0Gotchas & limits
- Despite the OData-shaped
@odata.contextenvelope, most OData query syntax 500s on this API: confirmed live,/updates?$top=1returns500 An exception was thrown.and/updates?$filter=ID eq '2026-Jan'returns500 {"message":"Internal server error"}. Only$filteron the two fields the project's own docs mention —InitialReleaseDateandCurrentReleaseDate— actually works, as in the worked example above. - Both endpoints content-negotiate on
Accept, but their defaults are OPPOSITE — this is the trap, not a missing format. All four cases probed live in this run on/cvrf/1999-Sepand/updates:/cvrf/{id}returnsapplication/xmlon a bare request (18,699 bytes) andapplication/jsonunderAccept: application/json(12,940 bytes), while/updatesreturnsapplication/jsonon a bare request (48,099 bytes) andapplication/xmlunderAccept: application/xml(66,040 bytes, an<ArrayOfUpdate>document).Accept: text/xmlalso works on/updates, returningtext/xmlwith the same 66,040 bytes. Sending theAcceptthat matches an endpoint's own default is a no-op (probed both ways). So always setAcceptexplicitly rather than relying on a default that differs per endpoint. - That JSON is CVRF serialised to JSON, NOT CSAF — don't wire a CSAF parser to it. Confirmed live on
/cvrf/1999-SepunderAccept: application/json, the document has exactly 7 PascalCase CVRF top-level keys:DocumentTitle,DocumentType,DocumentPublisher,DocumentTracking,DocumentNotes,ProductTree, andVulnerability. CSAF 2.0's required top-level keys (document,product_tree,vulnerabilities, snake_case) are all absent — checked explicitly, all three missing. - An invalid document ID on
/cvrf/{id}(confirmed live with/cvrf/not-a-real-id) returns a plain-text404body reading literallyFileNotFound, not a JSON error object. - A
/csaf/{id}route IS documented — it is the first of the 14 paths in the live swagger atapi.msrc.microsoft.com/cvrf/v3.0/swagger/v3/swagger.json, carrying its ownCsaftag — but every id format probed in this run returned400 Invalid ID format. The spec declares the id as a baretype: stringwith nopattern,example, or description, and 17 candidate formats were tried:2026-Jan,2026-jan,2026Jan,Jan-2026,2026-01,2026,2026-Jan.json,2026-Jan-csaf,cvrf-2026-Jan,CVE-2026-21441,CVE-2021-44228,2026-21441,msrc_cvrf_2026-Jan,msrc_cve-2026-21441,msrc_cve-2026-21441.json,all, andlatest. All 17 returned the same rejection, so the route is documented but was not usable from here — treat it as unusable unless you find a published id format, rather than assuming it is absent. The error body shape depends on the request: bare it is400 text/plainwith a 17-byteInvalid ID format, while underAccept: application/jsonit is the same message JSON-quoted (19 bytes). - Note that the copy of
swagger.jsoncommitted in the GitHub repo behind this entry'sdocsUrlis STALE relative to the live spec: it lists only 9 paths and no/csafat all, while the live swagger served by the API host lists 14 including/csaf/{id}. Enumerate the live spec, not the repo copy. The repo's README doesn't mention CSAF anywhere either (0 hits forcsaf, 2 forcvrf, confirmed live). - There is no CVE-to-bulletin resolution endpoint.
/cvrf/v3.0/cves/CVE-2021-44228404s with an empty body (confirmed live), and no/cvespath appears among the live swagger's 14 paths — which are/csaf/{id},/cvrf/{id},/cvrf({id}),/updates,/Updates,/Updates/$count,/updates({key}),/updates/{key},/Updates({key}),/Updates/{key}, and the four metadata paths/,/.,/$metadata,/./$metadata. Don't build around a CVE lookup that isn't there. - No CORS support: confirmed live,
/updatesreturns noaccess-control-allow-originheader at all, whether the request is bare or carries anOrigin: https://example.comheader — matches this entry's storedcors: no.