← All posts

Using the pkg.go.dev API in Go dependency review

The pkg.go.dev API gives Go teams a stable way to pull package metadata into CI, dependency review, and SBOM workflows. The value comes from using it for module identity, license checks, and review triggers, then joining it with native Go tooling.

The Go team’s post, “Introducing the pkg.go.dev API,” points to a simple fact with large practical value: pkg.go.dev now exposes package metadata through an API. For teams who build in Go, this matters well beyond documentation search. It gives your tooling a stable way to inspect modules, versions, licenses, and documentation signals without scraping HTML.

If you run a Go codebase in production, your dependency process lives or dies on metadata quality. You need to know which modules you import, which versions you pin, which licenses enter your tree, and which packages have weak maintenance signals. An API for package data shortens the path between discovery and policy. It also reduces one class of brittle internal tooling, the scraper that breaks when a site layout changes.

The useful question is not whether an API exists. It is how you fold it into engineering controls. Below are the checks worth building first, the signals worth storing, and the failure modes worth expecting.

Treat package metadata as an input to policy

A dependency review process often starts too late. A developer adds a module, the build passes, and review happens weeks later during audit or incident response. The pkg.go.dev API supports a better path. Pull package metadata at import time, or at least during CI, and make it part of your policy layer.

Focus on a small set of fields first.

  • Module path
  • Tagged versions
  • License metadata
  • Published documentation status
  • Repository links
  • Imported-by and imports context, where available

These fields help answer basic operational questions.

  • Are you importing the module you think you are
  • Did the dependency move across module paths
  • Are you pinned to a pseudo-version instead of a tagged release
  • Does the package expose enough documentation for a reviewer to assess intended use
  • Does the declared license fit your distribution model

The main mistake here is over-scoring weak signals. Documentation presence does not prove maintenance quality. A missing license field does not prove a bad package. Treat metadata as a review trigger, not an automatic verdict.

A practical pattern is to assign each module a review state.

  • Approved
  • Approved with constraints
  • Needs human review
  • Blocked pending license or source validation

Then feed pkg.go.dev API results into that state machine. This keeps your rules simple and auditable.

Verify module identity, not package name alone

Go’s import model is clear, but many internal dashboards still flatten dependencies into package names. This creates blind spots. In Go, identity sits with the full module path and version. Similar names across repositories, forks, or vanity import paths create confusion fast.

Use the API to normalize what you store.

  • Full module path
  • Resolved version
  • Source repository URL
  • Redistributable license indicator
  • Subdirectory package path, if the package is not at module root

This matters when a project changes ownership, splits into multiple modules, or republishes from a vanity domain. Your build still compiles, but your records drift. When records drift, patching and audit drift with them.

A common place this goes wrong is transitive dependency tracking. Teams review direct imports, then stop. But many incidents and license surprises enter through transitive modules. If your software bill of materials pipeline already walks go list -deps -json, enrich the output with pkg.go.dev metadata and persist both direct and transitive identities.

For example, you might store a compact record like this.

{
  "package": "golang.org/x/crypto/chacha20poly1305",
  "module": "golang.org/x/crypto",
  "version": "v0.24.0",
  "license": "BSD-3-Clause",
  "docs": true,
  "review_state": "approved"
}

This is not complex data. The value comes from storing it consistently.

Use the API to tighten CI checks

The fastest win is CI enrichment. When a pull request changes go.mod or go.sum, query package metadata and attach a short report to the build. Keep the report small. Engineers read concise output. Auditors need a durable artifact.

A useful CI check includes these steps.

  1. Diff go.mod and identify new or changed modules.
  2. Resolve concrete versions with go list -m -json all.
  3. Query pkg.go.dev API metadata for each changed module or key package.
  4. Compare results against internal policy.
  5. Fail only on hard requirements. Route softer signals to human review.

Hard requirements usually include:

  • Missing approved license where policy requires one
  • Module path mismatch against an allowlist entry
  • Version pinned to an unapproved fork
  • Dependency sourced from an internal denylist

Softer signals usually include:

  • No tagged release
  • Sparse documentation
  • Recent path migration
  • Large new transitive tree

Where teams fail is making CI noisy. If every new dependency trips five non-blocking warnings, developers stop reading. Keep the automated verdict narrow. Emit a separate dependency note for context.

Another failure mode is querying live metadata on every build without caching. API-backed checks belong behind a cache with a time-to-live. Dependency metadata does not change often enough to justify repeated live calls in every pipeline stage.

Combine pkg.go.dev data with native Go tooling

The API is useful, but it is only one layer. Go already gives you strong local inspection tools. The best results come from joining them.

Use native tooling for ground truth about what your build resolves.

  • go list -m -json all for module graph state
  • go mod graph for dependency edges
  • go version -m <binary> for embedded module versions in built artifacts
  • govulncheck for reachable vulnerability analysis
  • go env GOMODCACHE and checksum database behavior for fetch provenance controls

Use pkg.go.dev API data for ecosystem context.

  • Human-facing package and module metadata
  • License and documentation signals
  • Stable links to package docs and repository pages

This split matters because docs metadata is not your build authority. Your build authority is the resolved module graph and checksums. If the two disagree, trust the build graph first and investigate why. In practice, mismatches show up around replaced modules, local forks, vanity paths, and stale internal records.

A good internal report joins these two views.

module: example.com/acme/lib
resolved: v1.8.3
replace: none
packages imported: 12
license: MIT
docs present: yes
vuln status: no reachable findings
review state: approved with constraints
notes: package crypto helper limited to internal service use

This gives engineers one page with both operational and policy context.

Watch for ecosystem edge cases

Any metadata pipeline picks up edge cases. Go has a few recurring ones.

First, multi-module repositories. One repository often contains several modules with different release patterns. If you track only repository URL, you lose version accuracy. Always key review data by module path and version.

Second, pseudo-versions. These are useful in development and emergency pinning, but they weaken review clarity. A pseudo-version is not wrong, but it deserves attention because it ties your build to a specific commit outside a semantic tag. If you allow them, record the reason and owner.

Third, replace directives. They are essential in local development and controlled forks. They also create drift between source review, CI, and production if unmanaged. Surface every non-local replace in review output.

Fourth, license ambiguity. A metadata field is a convenience, not legal analysis. If the API reports no redistributable license or conflicting files exist upstream, route the module for legal or compliance review.

Fifth, package-level versus module-level assumptions. A safe module does not mean every package in it fits your use case. The package you import matters. Cryptography helpers, code generation tools, and cgo wrappers often need deeper review than utility packages.

A small ruleset catches most of this.

  • Block unknown non-local replace directives
  • Flag pseudo-versions for owner sign-off
  • Store module path plus version as the primary key
  • Re-check license metadata on version change
  • Review high-risk package classes, such as crypto, serialization, auth, and cgo, with a human in the loop

What to watch next

The pkg.go.dev API is one step toward better machine-readable ecosystem data for Go. The next thing to watch is standardization across dependency facts, package metadata, vulnerability context, and artifact provenance. Teams get the most value when these signals land in one internal inventory, not four separate dashboards.

If you maintain a large Go estate, start small. Add API-backed dependency notes to CI. Cache results. Store module path, version, license, and review state. Then join that with govulncheck and your SBOM pipeline. The gain is not more data. The gain is fewer blind spots and faster reviews.