A production-ready Go client library for the NVD CVE API v2.0, providing comprehensive access to the National Vulnerability Database with automatic pagination, intelligent retry logic, and enterprise-grade features.
- Complete CVE API Coverage: Full support for all CVE API v2.0 parameters and filters
- CVE Change History API: Track and audit changes to CVE records over time
- Automatic Pagination: Transparently handles offset-based pagination for large result sets (up to 341,616+ CVEs)
- Intelligent Retry Logic: Exponential backoff for transient errors (429, 503, 5xx) with configurable limits
- Structured Logging: Production-ready integration with zap for observability
- Flexible Authentication: Optional API key support (5 req/30s without key, 50 req/30s with key)
- Multiple Config Methods: Environment variables, JSON config files, or programmatic setup
- Production-Ready Transport: Configurable timeouts, retries, custom headers, proxy support, TLS configuration
- Type-Safe API: Strongly-typed request/response models with comprehensive field coverage
- Robust Time Parsing: Custom time type handles NVD API's inconsistent timestamp formats (with/without timezone)
- Fluent Request Builder: Chainable methods for clean, readable code
- Error Handling Helpers: Type-checking functions for common error scenarios
go get github.com/deploymenttheory/go-sdk-cveRequest a free API key from NVD to get 50 requests per 30 seconds (vs 5 without a key).
package main
import (
"context"
"fmt"
"log"
"github.com/deploymenttheory/go-sdk-cve/nvd"
"github.com/deploymenttheory/go-sdk-cve/nvd/cves"
)
func main() {
// Set environment variable: export NVD_API_KEY="your-key-here"
client, err := nvd.NewClientFromEnv()
if err != nil {
log.Fatal(err)
}
// Search for CVEs by keyword
resp, _, err := client.CVEs.List(context.Background(), &cves.ListRequest{
KeywordSearch: "Microsoft Windows",
ResultsPerPage: 100,
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found %d CVEs\n", resp.TotalResults)
for _, vuln := range resp.Vulnerabilities {
fmt.Printf("- %s: %s\n", vuln.CVE.ID, vuln.CVE.Descriptions[0].Value)
}
}vuln, _, err := client.CVEs.GetByID(context.Background(), "CVE-2021-44228")
if err != nil {
log.Fatal(err)
}
fmt.Printf("CVE: %s\n", vuln.CVE.ID)
fmt.Printf("Published: %s\n", vuln.CVE.Published)
fmt.Printf("Status: %s\n", vuln.CVE.VulnStatus)
// Access CVSS metrics
if vuln.CVE.Metrics != nil && len(vuln.CVE.Metrics.CVSSMetricV31) > 0 {
metric := vuln.CVE.Metrics.CVSSMetricV31[0]
fmt.Printf("CVSS: %.1f (%s)\n", metric.CVSSData.BaseScore, metric.CVSSData.BaseSeverity)
}export NVD_API_KEY="your-api-key-here"
export NVD_BASE_URL="https://services.nvd.nist.gov"
export HIDE_SENSITIVE_DATA="false"cfg, err := nvd.LoadConfigFromFile("config.json")
if err != nil {
log.Fatal(err)
}
client, err := nvd.NewClient(cfg)Example config.json:
{
"api_key": "your-api-key-here",
"base_url": "https://services.nvd.nist.gov",
"hide_sensitive_data": false
}import (
"time"
"go.uber.org/zap"
"github.com/deploymenttheory/go-sdk-cve/nvd"
)
logger, _ := zap.NewProduction()
cfg := &nvd.Config{
APIKey: "your-api-key-here",
BaseURL: "https://services.nvd.nist.gov",
}
client, err := nvd.NewClient(
cfg,
nvd.WithTimeout(30*time.Second),
nvd.WithRetryCount(3),
nvd.WithLogger(logger),
)import "time"
startDate := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
endDate := time.Date(2024, 12, 31, 23, 59, 59, 0, time.UTC)
resp, _, err := client.CVEs.List(context.Background(), &cves.ListRequest{
PubStartDate: &startDate,
PubEndDate: &endDate,
ResultsPerPage: 2000,
})resp, _, err := client.CVEs.List(context.Background(), &cves.ListRequest{
CVSSV3Severity: "CRITICAL",
NoRejected: true,
})resp, _, err := client.CVEs.List(context.Background(), &cves.ListRequest{
CPEName: "cpe:2.3:o:microsoft:windows_10:1607:*:*:*:*:*:*:*",
})resp, _, err := client.CVEs.List(context.Background(), &cves.ListRequest{
HasKEV: true,
})resp, _, err := client.CVEs.List(context.Background(), &cves.ListRequest{
CWEID: "CWE-287", // Improper Authentication
})import "github.com/deploymenttheory/go-sdk-cve/nvd/cve_history"
startDate := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
endDate := time.Date(2024, 1, 31, 23, 59, 59, 0, time.UTC)
resp, _, err := client.CVEHistory.List(context.Background(), &cve_history.ListRequest{
ChangeStartDate: &startDate,
ChangeEndDate: &endDate,
})
for _, change := range resp.CVEChanges {
fmt.Printf("CVE: %s, Event: %s, Date: %s\n",
change.Change.CVEID,
change.Change.EventName,
change.Change.Created)
}resp, _, err := client.CVEHistory.List(context.Background(), &cve_history.ListRequest{
EventName: "Initial Analysis",
ChangeStartDate: &startDate,
ChangeEndDate: &endDate,
})client, err := nvd.NewClient(
cfg,
nvd.WithTimeout(60*time.Second),
nvd.WithRetryCount(5),
nvd.WithRetryWaitTime(3*time.Second),
nvd.WithRetryMaxWaitTime(60*time.Second),
nvd.WithTotalRetryDuration(5*time.Minute),
)client, err := nvd.NewClient(
cfg,
nvd.WithProxy("http://proxy.example.com:8080"),
)import "crypto/tls"
tlsConfig := &tls.Config{
MinVersion: tls.VersionTLS12,
}
client, err := nvd.NewClient(
cfg,
nvd.WithTLSClientConfig(tlsConfig),
)client, err := nvd.NewClient(
cfg,
nvd.WithGlobalHeader("X-Application-Name", "MyApp"),
nvd.WithGlobalHeaders(map[string]string{
"X-Custom-Header": "value",
}),
)The CVE API supports extensive filtering options:
- cpeName: Filter by CPE name (e.g.,
cpe:2.3:o:microsoft:windows_10:1607:*:*:*:*:*:*:*) - cveId: Get specific CVE by ID (e.g.,
CVE-2019-1010218) - cveTag: Filter by CVE tags (
disputed,unsupported-when-assigned,exclusively-hosted-service) - cvssV2Metrics / cvssV3Metrics / cvssV4Metrics: Filter by CVSS vector strings
- cvssV2Severity / cvssV3Severity / cvssV4Severity: Filter by severity (
LOW,MEDIUM,HIGH,CRITICAL) - cweId: Filter by CWE ID (e.g.,
CWE-287) - hasCertAlerts: Include only CVEs with US-CERT Technical Alerts
- hasCertNotes: Include only CVEs with CERT/CC Vulnerability Notes
- hasKev: Include only CVEs in CISA's Known Exploited Vulnerabilities Catalog
- hasOval: Include only CVEs with OVAL records
- isVulnerable: Filter CPE matches to only vulnerable configurations
- keywordSearch: Search CVE descriptions (multiple keywords act as AND)
- keywordExactMatch: Require exact phrase match for keyword search
- lastModStartDate / lastModEndDate: Filter by last modification date (120-day max range)
- pubStartDate / pubEndDate: Filter by publication date (120-day max range)
- kevStartDate / kevEndDate: Filter by KEV catalog addition date (120-day max range)
- noRejected: Exclude CVEs with REJECT/Rejected status
- sourceIdentifier: Filter by data source (e.g.,
cve@mitre.org) - virtualMatchString: Broader CPE filtering than cpeName
- versionStart / versionStartType / versionEnd / versionEndType: Filter CPE version ranges
- changeStartDate / changeEndDate: Filter by change date (120-day max range, both required)
- cveId: Get complete change history for specific CVE
- eventName: Filter by event type (see Event Types below)
CVE ReceivedInitial AnalysisReanalysisCVE ModifiedModified AnalysisCVE TranslatedVendor CommentCVE Source UpdateCPE Deprecation RemapCWE RemapReference Tag UpdateCVE RejectedCVE UnrejectedCVE CISA KEV Update
The NVD API enforces rate limits:
- Without API Key: 5 requests per 30 seconds
- With API Key: 50 requests per 30 seconds
To obtain an API key, request one from the NVD.
The SDK automatically handles rate limiting (429 errors) with intelligent retry logic:
- ✅ Respects
Retry-Afterheaders (30 seconds for NVD API) - ✅ Exponential backoff for other retryable errors
- ✅ Configurable retry attempts (default: 5)
- ✅ Logs retry attempts at WARN level
- ✅ Maximum retry duration to prevent infinite loops
See the Rate Limiting Guide for detailed information and best practices.
- Use Date Ranges: Request only CVEs modified since your last sync
- API Key: Always use an API key for production to get higher rate limits
- Add Delays: For batch operations, add 1-7 second delays between requests
- Pagination: Let the SDK handle pagination automatically with
List()methods - Error Handling: Check for rate limiting with
nvd.IsRateLimited(err) - Logging: Use structured logging in production with
WithLogger() - Retry Configuration: Adjust retry settings based on your use case
- Quick Start Guide
- API Reference
- Usage Guide
- Rate Limiting Guide
- Architecture
- Examples
- NVD CVE API Documentation
- GoDoc
Contributions are welcome. Please read our Contributing Guidelines before submitting pull requests.
This project is licensed under the MIT License — see the LICENSE file for details.
- Issues: GitHub Issues
- NVD API docs: nvd.nist.gov/developers
go-sdk-cve/
├── nvd/ # Main SDK package
│ ├── nvd.go # Client entry point
│ ├── with_options.go # Configuration options
│ ├── client/ # HTTP transport layer
│ │ ├── transport.go # Core HTTP client
│ │ ├── request_builder.go # Request construction
│ │ ├── pagination.go # Automatic pagination
│ │ ├── retry.go # Retry logic
│ │ ├── errors.go # Error handling
│ │ └── ...
│ ├── config/ # Configuration management
│ │ └── config.go # Config loading and validation
│ ├── constants/ # SDK constants
│ │ ├── endpoints.go # API endpoints
│ │ ├── mime.go # Content types
│ │ └── version.go # SDK version
│ ├── cves/ # CVE API service
│ │ ├── crud.go # API operations
│ │ ├── models.go # Request/response types
│ │ ├── enums.go # Constants and enums
│ │ └── helpers.go # Fluent API builders
│ ├── cve_history/ # CVE Change History API
│ │ ├── crud.go # API operations
│ │ ├── models.go # Request/response types
│ │ └── enums.go # Event type constants
│ └── shared/ # Shared utilities
│ └── environment/ # Environment variable helpers
├── examples/ # Working code examples
│ ├── cves/ # CVE API examples
│ │ ├── list_by_keyword/
│ │ ├── get_by_id/
│ │ ├── filter_by_severity/
│ │ ├── date_range_sync/
│ │ ├── kev_catalog/
│ │ └── fluent_api/
│ ├── cve_history/ # Change History examples
│ │ └── track_changes/
│ └── comprehensive/ # Full-featured example
├── docs/ # Documentation
│ ├── quick-start.md # Getting started guide
│ ├── api-reference.md # Complete API reference
│ └── usage-guide.md # Common patterns and best practices
├── go.mod # Go module definition
├── Makefile # Build and test targets
├── README.md # This file
└── CHANGELOG.md # Version history
The SDK follows a layered architecture inspired by the AWS SDK for Go:
- Transport Layer (
nvd/client): HTTP client with retry logic, pagination, error handling - Service Layer (
nvd/cves,nvd/cve_history): API-specific operations and business logic - Model Layer (
models.go): Type-safe request/response structures - Configuration Layer (
nvd/config): Flexible configuration management
- SSOT (Single Source of Truth): Each piece of data has one authoritative location
- DRY (Don't Repeat Yourself): Shared logic is abstracted into reusable components
- SOLID Principles: Clean interfaces and separation of concerns
- Fail-Fast: Invalid configurations and requests fail immediately with clear errors
This is a community SDK and is not affiliated with or endorsed by NIST or the National Vulnerability Database.