{"openapi":"3.1.0","info":{"title":"NVD API over Airnode","version":"2.0","description":"The National Vulnerability Database is NIST's repository of vulnerability data: published CVEs with NVD's analysis of each (CVSS and SSVC scores, CWE weaknesses, affected products as CPE applicability statements, references, CISA KEV status), the change history of each CVE, the Official CPE Dictionary of product names, the CPE match criteria tying the two together, and the organizations that supply the data. This Airnode wraps the read endpoints of NVD API 2.0, so a consumer receives the answer with a signature saying this Airnode obtained it from NVD at this time. This product uses the NVD API but is not endorsed or certified by the NVD.\n\nEvery response carries an EIP-191 signature from the Airnode that served it, so the data can be attributed back to its source rather than trusted on reputation."},"servers":[{"url":"https://airnode-nvd.fly.dev"}],"paths":{"/":{"get":{"operationId":"getDocumentation","summary":"This document.","description":"Describes the operations this Airnode serves and how to verify what it returns. Free.","responses":{"200":{"description":"An OpenAPI document describing this Airnode.","content":{"application/json":{"schema":{"type":"object"}}}}}},"post":{"operationId":"performRequest","summary":"Perform an operation and attest to the response.","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"title":"getCve","type":"object","required":["operation","parameters"],"additionalProperties":false,"properties":{"operation":{"const":"getCve","description":"Look up one CVE by its id, with scores, weaknesses, affected products, references and KEV status. An id NVD does not hold answers an empty page, not an error.","x-airnode-returns":{"resultsPerPage":"integer","startIndex":"integer","totalResults":"integer","format":"string","version":"string","timestamp":"string","vulnerabilities":"array"}},"parameters":{"type":"object","additionalProperties":false,"required":["cveId"],"properties":{"cveId":{"description":"The CVE id, such as CVE-2021-44228.","type":"string","examples":["CVE-2021-44228"]}}},"responseProjection":{"type":"object","additionalProperties":{"type":"string","pattern":"^/"},"maxProperties":20,"description":"Optional. Maps a name of your choosing to an RFC 6901 JSON Pointer into the upstream response, such as {\"price\": \"/ethereum/usd\"}. data is then the text of a JSON object of those names in Unicode code point order, each value as the upstream wrote it, instead of the whole body, and the projection is part of the signed request identity. No two names take the same pointer, and no pointer is inside another's value. A pointer the response does not have is a 400."}}},{"title":"searchCves","type":"object","required":["operation","parameters"],"additionalProperties":false,"properties":{"operation":{"const":"searchCves","description":"Search CVEs by keyword, product, date window, severity, weakness, KEV status or source. Every filter given has to hold, and the oldest published come first.","x-airnode-returns":{"resultsPerPage":"integer","startIndex":"integer","totalResults":"integer","format":"string","version":"string","timestamp":"string","vulnerabilities":"array"}},"parameters":{"type":"object","additionalProperties":false,"required":[],"properties":{"keywordSearch":{"description":"Words that all have to appear in the CVE's current description, each matched as a word prefix, so circle also finds circles.","type":"string","examples":["log4j"]},"keywordExactMatch":{"description":"Send an empty string to match keywordSearch as one exact phrase rather than as separate words. Needs keywordSearch. NVD reads the flag by its presence and refuses any value, true included.","type":"string","const":""},"cpeName":{"description":"A CPE 2.3 name, for the CVEs whose applicability statements match it. Part, vendor, product and version have to be given, and the components after them may be left off. searchCpes finds names.","type":"string","examples":["cpe:2.3:o:microsoft:windows_10:1607"]},"isVulnerable":{"description":"Send an empty string to keep only CVEs where the cpeName product is itself vulnerable rather than only part of the configuration. Needs cpeName and cannot be used with virtualMatchString. NVD reads the flag by its presence and refuses any value, true included.","type":"string","const":""},"pubStartDate":{"description":"Start of the window the CVE was published to NVD in. Needs pubEndDate with it, and the two span at most 120 days.","type":"string","format":"date-time"},"pubEndDate":{"description":"End of the window the CVE was published to NVD in. Needs pubStartDate with it, and the two span at most 120 days.","type":"string","format":"date-time"},"lastModStartDate":{"description":"Start of the window the CVE was last modified in. Needs lastModEndDate with it, and the two span at most 120 days.","type":"string","format":"date-time"},"lastModEndDate":{"description":"End of the window the CVE was last modified in. Needs lastModStartDate with it, and the two span at most 120 days.","type":"string","format":"date-time"},"cvssV3Severity":{"description":"The CVSS v3 severity of NVD's own Primary score to match, so a CVE only its CNA scored is not matched: of the 57 CVEs published on 2026-09-01 that some source scored CRITICAL, CRITICAL matched 8. Cannot be used with cvssV4Severity.","type":"string","enum":["LOW","MEDIUM","HIGH","CRITICAL"]},"cvssV3Metrics":{"description":"A full or partial CVSS v3 vector to match.","type":"string","examples":["AV:N/AC:L/PR:N"]},"cvssV4Severity":{"description":"The CVSS v4 severity of NVD's own Primary score to match. NVD scores almost nothing in v4 itself, so on 2026-10-20 this matched 5 CVEs across the whole database, all HIGH, while CNAs scored 14 CRITICAL on 2026-09-01 alone. Cannot be used with cvssV3Severity.","type":"string","enum":["LOW","MEDIUM","HIGH","CRITICAL"]},"cweId":{"description":"A CWE id. NVD-CWE-Other and NVD-CWE-noinfo are the placeholders NVD uses for a weakness outside CWE or not known.","type":"string","examples":["CWE-287"]},"hasKev":{"description":"Send an empty string to keep only CVEs in CISA's Known Exploited Vulnerabilities catalog. NVD reads the flag by its presence and refuses any value, true included.","type":"string","const":""},"hasCertAlerts":{"description":"Send an empty string to keep only CVEs with a US-CERT Technical Alert. NVD reads the flag by its presence and refuses any value, true included.","type":"string","const":""},"hasOval":{"description":"Send an empty string to keep only CVEs with an OVAL record from the years MITRE ran OVAL. NVD reads the flag by its presence and refuses any value, true included.","type":"string","const":""},"noRejected":{"description":"Send an empty string to leave out CVEs with the REJECT or Rejected status, which NVD includes by default. NVD reads the flag by its presence and refuses any value, true included.","type":"string","const":""},"sourceIdentifier":{"description":"The exact identifier of a data source, for the CVEs it contributed to. listSources answers every identifier.","type":"string","examples":["cve@mitre.org"]},"virtualMatchString":{"description":"A CPE match string, broader than cpeName since any component may be a wildcard. Ignored when cpeName is sent too.","type":"string","examples":["cpe:2.3:o:linux:linux_kernel"]},"resultsPerPage":{"description":"How many CVEs to return. The default page is several megabytes, so ask for what you need.","type":"integer","minimum":1,"maximum":2000,"default":2000,"examples":[20]},"startIndex":{"description":"The zero-based index of the first record to return, for paging past the first.","type":"integer","minimum":0,"default":0}}},"responseProjection":{"type":"object","additionalProperties":{"type":"string","pattern":"^/"},"maxProperties":20,"description":"Optional. Maps a name of your choosing to an RFC 6901 JSON Pointer into the upstream response, such as {\"price\": \"/ethereum/usd\"}. data is then the text of a JSON object of those names in Unicode code point order, each value as the upstream wrote it, instead of the whole body, and the projection is part of the signed request identity. No two names take the same pointer, and no pointer is inside another's value. A pointer the response does not have is a 400."}}},{"title":"getCveHistory","type":"object","required":["operation","parameters"],"additionalProperties":false,"properties":{"operation":{"const":"getCveHistory","description":"List the change events NVD recorded on a CVE, or on every CVE changed in a window, oldest first. An id NVD does not hold answers an empty page, not an error.","x-airnode-returns":{"resultsPerPage":"integer","startIndex":"integer","totalResults":"integer","format":"string","version":"string","timestamp":"string","cveChanges":"array"}},"parameters":{"type":"object","additionalProperties":false,"required":[],"properties":{"cveId":{"description":"A CVE id, such as CVE-2021-44228, for its complete change history.","type":"string","examples":["CVE-2021-44228"]},"changeStartDate":{"description":"Start of the window the change was made in. Needs changeEndDate with it, and the two span at most 120 days.","type":"string","format":"date-time"},"changeEndDate":{"description":"End of the window the change was made in. Needs changeStartDate with it, and the two span at most 120 days.","type":"string","format":"date-time"},"eventName":{"description":"One kind of change event. CVE Received and New CVE Received name the same one, and responses spell it New CVE Received. An unknown name is refused, which reaches a caller as a 502.","type":"string","enum":["CVE Received","New CVE Received","Initial Analysis","Reanalysis","CVE Modified","Modified Analysis","CVE Translated","Vendor Comment","CVE Source Update","CPE Deprecation Remap","CWE Remap","Reference Tag Update","CVE Rejected","CVE Unrejected","CVE CISA KEV Update","Data Remediation","CVE Status Change"]},"resultsPerPage":{"description":"How many events to return. The default page measured 5.2 MB, so ask for what you need.","type":"integer","minimum":1,"maximum":5000,"default":5000,"examples":[20]},"startIndex":{"description":"The zero-based index of the first record to return, for paging past the first.","type":"integer","minimum":0,"default":0}}},"responseProjection":{"type":"object","additionalProperties":{"type":"string","pattern":"^/"},"maxProperties":20,"description":"Optional. Maps a name of your choosing to an RFC 6901 JSON Pointer into the upstream response, such as {\"price\": \"/ethereum/usd\"}. data is then the text of a JSON object of those names in Unicode code point order, each value as the upstream wrote it, instead of the whole body, and the projection is part of the signed request identity. No two names take the same pointer, and no pointer is inside another's value. A pointer the response does not have is a 400."}}},{"title":"searchCpes","type":"object","required":["operation","parameters"],"additionalProperties":false,"properties":{"operation":{"const":"searchCpes","description":"Search the Official CPE Dictionary for product names by match string, keyword or id, oldest created first.","x-airnode-returns":{"resultsPerPage":"integer","startIndex":"integer","totalResults":"integer","format":"string","version":"string","timestamp":"string","products":"array"}},"parameters":{"type":"object","additionalProperties":false,"required":[],"properties":{"cpeMatchString":{"description":"A partial CPE name that names have to match, with the components left off matching anything, such as cpe:2.3:o:microsoft:windows_10 for every Windows 10 name.","type":"string","examples":["cpe:2.3:o:microsoft:windows_10:1607"]},"keywordSearch":{"description":"Words that all have to appear in a name's titles or reference links, each matched as a word prefix.","type":"string"},"keywordExactMatch":{"description":"Send an empty string to match keywordSearch as one exact phrase rather than as separate words. Needs keywordSearch. NVD reads the flag by its presence and refuses any value, true included.","type":"string","const":""},"cpeNameId":{"description":"The UUID NVD gives a CPE name, for that one record. A well-formed UUID that names nothing answers with no products rather than an error.","type":"string"},"lastModStartDate":{"description":"Start of the window the name was last modified in. Needs lastModEndDate with it, and the two span at most 120 days.","type":"string","format":"date-time"},"lastModEndDate":{"description":"End of the window the name was last modified in. Needs lastModStartDate with it, and the two span at most 120 days.","type":"string","format":"date-time"},"matchCriteriaId":{"description":"The UUID of a CPE match criteria, from a CVE's configurations or from getCpeMatch, for every name it matches.","type":"string"},"resultsPerPage":{"description":"How many names to return. The default is the largest page, so ask for what you need.","type":"integer","minimum":1,"maximum":10000,"default":10000,"examples":[20]},"startIndex":{"description":"The zero-based index of the first record to return, for paging past the first.","type":"integer","minimum":0,"default":0}}},"responseProjection":{"type":"object","additionalProperties":{"type":"string","pattern":"^/"},"maxProperties":20,"description":"Optional. Maps a name of your choosing to an RFC 6901 JSON Pointer into the upstream response, such as {\"price\": \"/ethereum/usd\"}. data is then the text of a JSON object of those names in Unicode code point order, each value as the upstream wrote it, instead of the whole body, and the projection is part of the signed request identity. No two names take the same pointer, and no pointer is inside another's value. A pointer the response does not have is a 400."}}},{"title":"getCpeMatch","type":"object","required":["operation","parameters"],"additionalProperties":false,"properties":{"operation":{"const":"getCpeMatch","description":"List the CPE match criteria in a CVE's applicability statements, with the product names each matches.","x-airnode-returns":{"resultsPerPage":"integer","startIndex":"integer","totalResults":"integer","format":"string","version":"string","timestamp":"string","matchStrings":"array"}},"parameters":{"type":"object","additionalProperties":false,"required":[],"properties":{"cveId":{"description":"A CVE id, such as CVE-2021-44228, for every criteria in its applicability statements that is not deprecated.","type":"string","examples":["CVE-2021-44228"]},"matchCriteriaId":{"description":"The UUID of one match criteria, as a CVE's configurations give it.","type":"string"},"matchStringSearch":{"description":"A CPE match string, for every criteria that conforms to it. Version ranges are not searched.","type":"string","examples":["cpe:2.3:*:cisco:adaptive_security_appliance:*"]},"resultsPerPage":{"description":"How many criteria to return, each carrying every name it matches. The default is the largest page, so ask for what you need.","type":"integer","minimum":1,"maximum":500,"default":500,"examples":[20]},"startIndex":{"description":"The zero-based index of the first record to return, for paging past the first.","type":"integer","minimum":0,"default":0}}},"responseProjection":{"type":"object","additionalProperties":{"type":"string","pattern":"^/"},"maxProperties":20,"description":"Optional. Maps a name of your choosing to an RFC 6901 JSON Pointer into the upstream response, such as {\"price\": \"/ethereum/usd\"}. data is then the text of a JSON object of those names in Unicode code point order, each value as the upstream wrote it, instead of the whole body, and the projection is part of the signed request identity. No two names take the same pointer, and no pointer is inside another's value. A pointer the response does not have is a 400."}}},{"title":"listSources","type":"object","required":["operation","parameters"],"additionalProperties":false,"properties":{"operation":{"const":"listSources","description":"List the organizations that supply NVD's data, with their identifiers and acceptance levels.","x-airnode-returns":{"resultsPerPage":"integer","startIndex":"integer","totalResults":"integer","format":"string","version":"string","timestamp":"string","sources":"array"}},"parameters":{"type":"object","additionalProperties":false,"required":[],"properties":{"sourceIdentifier":{"description":"One identifier, a name or a UUID, for the source that uses it.","type":"string","examples":["cve@mitre.org","6b3ad84c-e1a6-4bf7-a703-f496b71e49db"]},"lastModStartDate":{"description":"Start of the window the source was last modified in. Needs lastModEndDate with it, and the two span at most 120 days.","type":"string","format":"date-time"},"lastModEndDate":{"description":"End of the window the source was last modified in. Needs lastModStartDate with it, and the two span at most 120 days.","type":"string","format":"date-time"},"resultsPerPage":{"description":"How many sources to return.","type":"integer","minimum":1,"maximum":1000,"examples":[20]},"startIndex":{"description":"The zero-based index of the first record to return, for paging past the first.","type":"integer","minimum":0,"default":0}}},"responseProjection":{"type":"object","additionalProperties":{"type":"string","pattern":"^/"},"maxProperties":20,"description":"Optional. Maps a name of your choosing to an RFC 6901 JSON Pointer into the upstream response, such as {\"price\": \"/ethereum/usd\"}. data is then the text of a JSON object of those names in Unicode code point order, each value as the upstream wrote it, instead of the whole body, and the projection is part of the signed request identity. No two names take the same pointer, and no pointer is inside another's value. A pointer the response does not have is a 400."}}}]}}}},"responses":{"200":{"description":"The upstream response and the attestation over it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Attestation"}}}},"400":{"description":"The operation exists but the parameters or the projection are missing, unknown or malformed, a path parameter is empty, . or .. or has such a part between slashes, or the projection names too many fields, an empty or repeated pointer, or a path the response does not have."},"404":{"description":"No such operation."},"502":{"description":"The upstream API could not be reached or refused the request."}}}}},"components":{"schemas":{"Attestation":{"type":"object","required":["airnode","requestHash","timestamp","data","signature"],"properties":{"airnode":{"type":"string","description":"The address whose signature attests to this response."},"requestHash":{"type":"string","description":"Identifies the request this response answers."},"timestamp":{"type":"string","description":"Unix seconds at which the response was signed."},"data":{"type":"string","description":"The upstream response body exactly as received, less a leading byte order mark. With a projection, the text of a JSON object keyed by the names it asked for in Unicode code point order, each value as the upstream wrote it."},"signature":{"type":"string","description":"The attestation itself."}}}}},"x-airnode":{"address":"0x41dd84D5B6503D9aA3c374a49F546e335423a1b8","version":"0.2.0","attestation":{"scheme":"eip191-secp256k1","digest":"keccak256(abi.encodePacked(bytes32 requestHash, uint256 timestamp, bytes dataBytes))","dataBytes":"UTF-8 of data","requestHash":"keccak256(utf8(JSON.stringify([operation, parameterEntriesSortedByName]))), where an object value is itself entries sorted by name, at any depth, and an array keeps its order. Names are compared by Unicode code point. A request that carries a projection appends its entries, sorted the same way, as a third element","verify":"Recover the signer of the signature over the digest and check it equals the address above."},"passthrough":{"url":"https://airnode-nvd.fly.dev/api","routes":{"getCve":{"method":"GET","path":"/rest/json/cves/2.0","parameters":{"cveId":"query"}},"searchCves":{"method":"GET","path":"/rest/json/cves/2.0","parameters":{"keywordSearch":"query","keywordExactMatch":"query","cpeName":"query","isVulnerable":"query","pubStartDate":"query","pubEndDate":"query","lastModStartDate":"query","lastModEndDate":"query","cvssV3Severity":"query","cvssV3Metrics":"query","cvssV4Severity":"query","cweId":"query","hasKev":"query","hasCertAlerts":"query","hasOval":"query","noRejected":"query","sourceIdentifier":"query","virtualMatchString":"query","resultsPerPage":"query","startIndex":"query"}},"getCveHistory":{"method":"GET","path":"/rest/json/cvehistory/2.0","parameters":{"cveId":"query","changeStartDate":"query","changeEndDate":"query","eventName":"query","resultsPerPage":"query","startIndex":"query"}},"searchCpes":{"method":"GET","path":"/rest/json/cpes/2.0","parameters":{"cpeMatchString":"query","keywordSearch":"query","keywordExactMatch":"query","cpeNameId":"query","lastModStartDate":"query","lastModEndDate":"query","matchCriteriaId":"query","resultsPerPage":"query","startIndex":"query"}},"getCpeMatch":{"method":"GET","path":"/rest/json/cpematch/2.0","parameters":{"cveId":"query","matchCriteriaId":"query","matchStringSearch":"query","resultsPerPage":"query","startIndex":"query"}},"listSources":{"method":"GET","path":"/rest/json/source/2.0","parameters":{"sourceIdentifier":"query","lastModStartDate":"query","lastModEndDate":"query","resultsPerPage":"query","startIndex":"query"}}},"parameters":{"x-airnode-sign":"false skips the signature: no attestation headers, and the body streamed through with no size cap.","x-airnode-project":"alias:pointer, once per field to keep and at most 20 times, each pointer an RFC 6901 JSON Pointer into the upstream response that starts with /, is not repeated and is not inside another's value. The body becomes an object of those aliases in Unicode code point order, the signature covers it, and a pointer the response does not have is a 400."},"flow":"Send the request the provider documents, method, path, query and body as they are, to url followed by the route's path. The body comes back as the upstream sent it, with the upstream's status, and on success the attestation is in the X-Airnode-Address, X-Airnode-Request-Hash, X-Airnode-Timestamp and X-Airnode-Signature headers, with the operation served in X-Airnode-Operation. Every query parameter starting with x-airnode- is this Airnode's own, listed under parameters, and never reaches the upstream.","requestHash":"keccak256(utf8(JSON.stringify([\"passthrough\", method, path, queryEntries, headerEntries, body]))), where method is upper case, path is what was sent after url as a URL parser resolves it, the query entries are what was sent with the Airnode's own parameters removed, in the order sent, headerEntries are the headers that the routes declare as parameters and the request sent, each name in lower case and sorted by Unicode code point, with the values of a header sent more than once joined with \", \" in the order sent and the result trimmed of spaces and tabs, and body is the request body as sent, or an empty string. A request that carries a projection appends its entries, sorted by name in Unicode code point order, as a seventh element","dataBytes":"UTF-8 of the response body exactly as received, less a leading byte order mark, or of the projected object"}}}