
Dependabot alerts REST API now allows you to directly retrieve dependency versions within a repository
This page has been translated by machine translation. View original
Hi there! I'm Yuji Nishimura from the Operations Division!
Some of you may be fetching Dependabot alerts via the REST API and syncing them to vulnerability management tools or ticketing systems. Until now, alerts included the "range of vulnerable versions" but not the "version actually used in the repository." This meant you had to either read the lockfile separately or make an additional query using the GraphQL vulnerableRequirements field.
With the GitHub Changelog on October 20, 2026, version_requirement was added to the REST API response. In this article, I read the alerts from my own repositories, verified how values differ between lockfiles and manifests, and confirmed whether they match the GraphQL values. I also summarize some points to keep in mind when incorporating this into a sync process.
My verification covered 330 alerts (open, fixed, and dismissed) across 8 of my repositories on github.com, read using GitHub CLI's gh api.
What Changed This Time
- Scope: github.com and GitHub Enterprise Cloud with data residency (GitHub Enterprise Server support planned for 3.24)
- Added field:
dependency.version_requirementon each alert - Where it appears: Individual alerts, repository/organization/enterprise alert lists, and the
dependabot_alertwebhook payload - Official links: GitHub Changelog, REST API endpoints for Dependabot alerts
The value included is the last one recorded by Dependabot for that alert. The content of the value varies depending on where Dependabot detected the dependency. According to the Changelog:
| Detection source | Value in version_requirement |
|---|---|
| Lockfile | Resolved version |
| Manifest without lockfile | Version or version range declared in the manifest (e.g., ~1.8.1) |
| Dependency submission API | Resolved version submitted by the build tool |
| When no value is available | null |
The REST API OpenAPI definition also includes notes about empty strings, truncation at 64 bytes, and one value per alert. I'll cover these along with their implications for sync processing in the latter half.
Trying It Out on My Own Repositories
Environment
- macOS (arm64)
- GitHub CLI 2.102.0 (using
gh apiwith the built-in--jq). Logged in viagh auth loginwith an account that can view Dependabot alerts on the target repositories - Targets: 8 of my repositories with Dependabot alerts enabled and active alerts (Cargo.lock, Cargo.toml, package.json, package-lock.json, pnpm-lock.yaml, uv.lock, Gemfile.lock, packages.config)
All operations were read-only; no alert states were modified.
1. Verifying the Field on an Individual Alert
First, I fetched a single alert by specifying its alert number (22 in this case) and inspected dependency.
gh api repos/<owner>/<repo>/dependabot/alerts/22 --jq '.dependency'
{
"manifest_path": "wasm/mandelbrot/Cargo.toml",
"package": { "ecosystem": "rust", "name": "wee_alloc" },
"relationship": "unknown",
"scope": "runtime",
"version_requirement": ">= 0.4.5,< 0.5.0"
}
version_requirement appeared alongside manifest_path and relationship. Whether I set the request header X-GitHub-Api-Version to 2022-11-28 or 2026-03-10, the same value was returned. The OpenAPI definition also lists it for both API versions.
2. How Values Differ Between Lockfiles and Manifests
I prepared a script to list version_requirement alongside the vulnerable version range (security_vulnerability.vulnerable_version_range) and the first patched version (first_patched_version). Only open alerts are targeted.
#!/bin/bash
# Usage: alerts.sh <owner/repo> Lists dependencies and versions for open Dependabot alerts
gh api --paginate "repos/$1/dependabot/alerts?state=open&per_page=100" \
--jq '.[] | [.dependency.package.name, .dependency.manifest_path,
(.dependency.version_requirement // "null"),
.security_vulnerability.vulnerable_version_range,
(.security_vulnerability.first_patched_version.identifier // "-")] | @tsv'
I ran this on a Rust practice repository and extracted the rows originating from lockfiles and from Cargo.toml files without lockfiles, added a header row, and aligned the columns. The result is shown in the image below (the show.sh on screen is a small script that performs this extraction and formatting).

rand from Cargo.lock shows = 0.7.3, which matches the resolved version 0.7.3 written in the lockfile. wee_alloc from Cargo.toml (no lockfile) shows >= 0.4.5,< 0.5.0. The declaration in Cargo.toml is version = "0.4.5". Cargo treats this notation as a caret requirement (^0.4.5), so the value recorded is that requirement expanded into a range.
tokio appears on 3 rows because 3 separate advisories exist for the same = 0.2.25. The vulnerable range and patched version differ per alert, and you need to upgrade to the highest patched version (in this example, 1.38.2) to resolve all three.
In another case, a Node.js sample app without a lockfile showed ~1.8.1 and ~1.17.1 in version_requirement, exactly as declared in package.json.
3. Differences in Notation by Ecosystem
I fetched alerts across all 8 repositories without filtering by state (state=open,fixed,dismissed,auto_dismissed) and aggregated the values by manifest type. The notation varied by ecosystem.
| Manifest / lockfile | Example values observed |
|---|---|
| Cargo.lock, Gemfile.lock, uv.lock, packages.config | = 0.7.3, = 3.8.1 (prefixed with = ) |
| pnpm-lock.yaml, package-lock.json | 4.1.11, 1.49.1 (digits only) |
| package.json (no lockfile) | ~1.8.1, ~ 4.15.2 |
| Cargo.toml (no lockfile) | >= 0.4.5,< 0.5.0 |
NuGet's packages.config is a manifest, but since it declares versions as fixed values, it followed the same = x.y.z format as lockfiles.
For package.json, even the same package morgan showed ~ 1.8.1 in older alerts and ~1.8.1 in newer ones—differing in whether there is a space after the operator. Since the value is the last one recorded per alert, it appears that the notation from when the alert was recorded is preserved as-is. This variation in notation is not documented officially, so I'm sharing it here as a personal observation.
Fixed (fixed) and dismissed (dismissed) alerts also retained their values.
4. Comparing with GraphQL's vulnerableRequirements
I compared the results with GraphQL's vulnerableRequirements mentioned in the introduction. The repositories I compared had fewer than 100 alerts each, so a single page with first: 100 retrieved all of them on the GraphQL side. For repositories with more than 100 alerts, pagination would be needed on the GraphQL side as well.
gh api graphql -f query='
query {
repository(owner: "<owner>", name: "<repo>") {
vulnerabilityAlerts(first: 100) {
nodes { number vulnerableManifestPath vulnerableRequirements }
}
}
}' --jq '.data.repository.vulnerabilityAlerts.nodes[] | [.number, .vulnerableManifestPath, .vulnerableRequirements] | @tsv' | sort > gql.tsv
gh api --paginate 'repos/<owner>/<repo>/dependabot/alerts?per_page=100' \
--jq '.[] | [.number, .dependency.manifest_path, .dependency.version_requirement] | @tsv' | sort > rest.tsv
diff gql.tsv rest.tsv
All 41 alerts in the Rust repository matched exactly on number, manifest, and value, with diff producing no output. The 8 alerts in the Node.js sample app also matched, including the space in ~ 1.8.1. Across the 49 alerts from the 2 repositories I compared, I can get the same values from the REST API list alone without a separate GraphQL query.
Points to Keep in Mind When Incorporating into Sync Processing
The following is based on my personal observations and the OpenAPI definition documentation.
- The value is not necessarily a version number: Without a lockfile, you may get a version range like
~1.8.1or>= 0.4.5,< 0.5.0. For alerts detected from a lockfile or manifest, you can distinguish between a resolved version and a version range by checking whethermanifest_pathpoints to a lockfile - Notation differs by ecosystem: There are differences such as the presence or absence of
=and whitespace. Before performing comparison with a patched version, normalize by stripping=and whitespace, then pass to an ecosystem-specific version comparison library - The same value may appear across multiple alerts: Since each advisory generates a separate alert, when grouping by dependency, take the maximum patched version
- Distinguish between
nulland empty string: None appeared in the 330 alerts I tested, but according to the OpenAPI definition, the value can benullwhen absent, or an empty string for dependencies with no specified version - Truncated at 64 bytes: Long version ranges may be cut off midway, making them unreadable as valid ranges
- One value per alert: When multiple vulnerable versions correspond to a single alert, only one is recorded and it may not represent all vulnerable versions. If you need a complete picture of all versions, checking the lockfile or SBOM may still be necessary
Summary
With the addition of version_requirement to the Dependabot alerts REST API, you can now get the dependency version used in the repository (or the declared version range if there is no lockfile) directly from the alert list, without a separate GraphQL query. In my own repositories, alerts from lockfiles contained resolved versions, while those from manifests without lockfiles contained declared version ranges. The values also matched GraphQL's vulnerableRequirements.
That said, values may be version ranges, and notation differs by ecosystem. When incorporating this into a sync process, use manifest_path to determine whether the source is a lockfile, and strip = and whitespace before using the value in comparisons. I did not test the organization/enterprise list API or webhooks this time, so it would be worth verifying that the same values appear there before relying on them.
I hope this is helpful to someone.
Reference Links: