A typed Go client for the pkg.go.dev API (the "Go Pkgsite API",
https://pkg.go.dev/v1beta): search packages and symbols, read documentation (whole package or a
single symbol), list versions, importers, known vulnerabilities and a module's dependencies.
The public API lives at the module root (package pkggodev): context-first methods,
functional options, clean typed results (no ogen codegen leakage) and auto-paginating
iterators. It wraps an ogen-generated client kept under
internal/api.
Optional response fields are typed as mo.Option[T] (e.g.
pkg.Synopsis is mo.Option[string], module.Size is mo.Option[int64]): the type tells absent
apart from a zero value, with no leaked codegen wrappers. Read them with .OrEmpty(), .Get(),
.MustGet() or .IsAbsent(). Required fields stay plain (pkg.Path is a string). When marshalled
back to JSON, absent options are omitted (omitzero).
Tip
Looking for a CLI or MCP instead of a Go library? Use samber/godig — the pkg.go.dev CLI powered by this client.
go get github.com/samber/go-pkggodev-clientThis library is v0 and follows SemVer. It is compatible with Go >= 1.25 (uses iter.Seq2).
import pkggodev "github.com/samber/go-pkggodev-client"
c, err := pkggodev.New(pkggodev.WithUserAgent("my-app/1.0"))
if err != nil {
panic(err)
}
// Single object.
pkg, _ := c.Package(ctx, "github.com/samber/lo")
fmt.Println(pkg.Path, pkg.Synopsis.OrEmpty()) // required string + optional mo.Option[string]
// A single symbol's documentation (token-efficient, no full package blob).
sym, err := c.Symbol(ctx, "github.com/samber/lo", "Map", pkggodev.WithExamples())
if errors.Is(err, pkggodev.ErrSymbolNotFound) {
// symbol does not exist in the package
}
fmt.Println(sym.Kind, sym.Signature) // "Function", "func Map[...](...) ..."
fmt.Println(sym.Synopsis.OrEmpty()) // first sentence of the doc
// One page.
page, _ := c.Versions(ctx, "github.com/samber/lo", pkggodev.WithLimit(10))
fmt.Println(page.Total, page.NextToken.OrEmpty())
// All results, auto-paginated.
for v, err := range c.AllVersions(ctx, "github.com/samber/lo") {
if err != nil {
break
}
fmt.Println(v.Version, v.CommitTime)
}
// A module's dependencies, parsed from the go.mod on the Go module proxy.
deps, _ := c.Dependencies(ctx, "github.com/samber/do/v2")
for _, d := range deps.Requires {
fmt.Println(d.Path, d.Version.OrEmpty(), d.Indirect) // github.com/samber/go-type-to-string v1.8.0 false
}
// Module download size (Content-Length of the proxy zip), opt-in via WithSize.
m, _ := c.Module(ctx, "github.com/samber/do/v2", pkggodev.WithSize())
fmt.Println(m.GoVersion.OrEmpty(), m.Size.OrEmpty()) // "1.18" 65031 (bytes)GoDoc: https://pkg.go.dev/github.com/samber/go-pkggodev-client
func New(opts ...ClientOption) (*Client, error)WithBaseURL(url)— override the API base URL.WithVulnBaseURL(url)— override the Go vulnerability database base URL used byVulns(defaults tohttps://vuln.go.dev).WithHTTPClient(*http.Client)— custom timeouts / transport.WithUserAgent(string)— set theUser-Agentheader.WithGoproxy(string)— override the module proxy list used byMajorVersions,DependenciesandModule/VersionswithWithSize(same syntax as theGOPROXYenv var; honored by default, defaulting tohttps://proxy.golang.org).
All take context.Context first and return clean, typed values:
| Method | Returns |
|---|---|
Search(ctx, opts...) |
*Page[SearchResult] |
Package(ctx, path, opts...) |
*Package |
ImportedBy(ctx, path, opts...) |
*ImportedByResult |
Packages(ctx, path, opts...) |
*PackagesResult |
Module(ctx, path, opts...) |
*Module |
Versions(ctx, path, opts...) |
*Page[ModuleVersion] |
Symbols(ctx, path, opts...) |
*Page[SymbolInfo] |
Symbol(ctx, path, symbol, opts...) |
*Symbol |
Vulns(ctx, path, opts...) |
[]Vulnerability |
MajorVersions(ctx, modulePath, opts...) |
*Page[MajorVersion] |
Dependencies(ctx, modulePath, opts...) |
*DependenciesResult |
Symbols lists the package symbols as lightweight SymbolInfo values (name, kind, synopsis,
parent). Symbol returns the full documentation of a single symbol (func, type, method,
var or const) instead of the whole package doc blob — handy to keep token usage low. symbol
is the exported identifier ("Map") or "Type.Method" ("Either.ForEach"); matching is
case-sensitive. The doc is derived client-side from the package documentation (always fetched as
Markdown, so WithDoc is ignored here) and the method returns ErrSymbolNotFound when the symbol
is absent. Pass WithExamples to include runnable examples.
Vulns is sourced directly from the Go vulnerability database in
OSV format, so each Vulnerability carries far more than a
summary: CVE/GHSA Aliases, affected version Ranges, the affected Packages and their Symbols,
References, review status and the pkg.go.dev/vuln URL. path may be a module path (exact match)
or a package path (matched to its owning module); standard-library imports such as crypto/x509
resolve to the stdlib pseudo-module.
The result is scoped to the queried module, so there is no single fixedVersion field: a module
can be fixed across several disjoint intervals, so the fix lives per-interval in Ranges[].Fixed.
for _, v := range vulns {
fmt.Println(v.ID, v.Aliases) // GO-2021-0113 [CVE-2021-38561 GHSA-ppp9-7jff-5vj2]
for _, r := range v.Ranges {
fmt.Printf(" introduced %s, fixed %s\n", r.Introduced.OrEmpty(), r.Fixed.OrEmpty())
}
}Pass WithVersion with a concrete semver to keep only the vulnerabilities actually affecting that
version (read the covering fix from Ranges); WithLimit caps the result. The database is fetched
whole rather than paginated, so Vulns returns a plain slice.
In Go, major versions beyond v1 live as separate modules (path, path/v2, path/v3…) and
can be non-contiguous (e.g. v1, v2, v4, v6). pkg.go.dev does not yet expose a MajorVersions
endpoint (golang/go#76718), so MajorVersions derives
the answer from the Go module proxy — honoring GOPROXY (see WithGoproxy). It lists the
tagged versions of the base path (which yields v0, v1 and any +incompatible majors that share it)
and probes path/vN for higher majors. gopkg.in/pkg.vN paths are supported too.
page, _ := c.MajorVersions(ctx, "github.com/samber/do")
for _, mv := range page.Items {
fmt.Println(mv.Major, mv.ModulePath, mv.Version, mv.IsLatest)
// v2 github.com/samber/do/v2 v2.0.0 true
// v1 github.com/samber/do v1.6.0 false
}Each MajorVersion carries its own ModulePath, the Major ("v2"), the latest Version in that
major, and IsLatest for the highest major. Results are sorted newest-major-first. The module proxy
has no pagination cursor, so the result is a single page. WithExcludePseudo drops majors whose
latest version is a pseudo-version (reflecting ExcludePseudo from the proposal); WithLimit caps
the number of returned majors (the proposal's Max); WithFilter matches each major's module path.
MajorVersions returns ErrProxyDisabled when GOPROXY is off/direct-only and
ErrInvalidModulePath for an unparsable path.
pkg.go.dev does not expose a dependencies endpoint, so Dependencies reads and parses the module's
go.mod straight from the Go module proxy (honoring GOPROXY, see WithGoproxy). It returns the
require directives (each with its version and whether it is // indirect), plus any replace and
exclude directives and the module's go directive.
deps, _ := c.Dependencies(ctx, "github.com/samber/do/v2")
fmt.Println(deps.Version, deps.GoVersion.OrEmpty()) // resolved version + go directive, e.g. "v2.0.0" "1.18"
for _, d := range deps.Requires {
fmt.Println(d.Path, d.Version.OrEmpty(), d.Indirect)
}WithVersion selects the version; when unset (or "latest") the proxy's latest version is used and
Version reports the concrete version the go.mod was read at. Dependencies returns
ErrProxyDisabled when GOPROXY is off/direct-only, ErrInvalidModulePath for an unparsable
path, and ErrModuleNotFound when the module version is unknown to every proxy.
Module always reports the go directive of the module's go.mod in Module.GoVersion (read from the
already-returned go.mod contents, no extra request). Download sizes are opt-in via WithSize: a size
is the Content-Length of the module zip on the Go module proxy, fetched with a HEAD request (the
archive is never downloaded). It populates Module.Size (one extra request) and, on Versions /
AllVersions, ModuleVersion.Size for each listed version (one concurrent request per version).
m, _ := c.Module(ctx, "github.com/samber/do/v2", pkggodev.WithSize())
fmt.Println(m.GoVersion.OrEmpty(), m.Size.OrEmpty()) // "1.18" 65031 (bytes)
// Size of every release, fetched concurrently.
for v, err := range c.AllVersions(ctx, "github.com/samber/do/v2", pkggodev.WithSize()) {
if err != nil {
break
}
fmt.Println(v.Version, v.Size.OrEmpty())
}Module and Versions return ErrProxyDisabled when WithSize is set but GOPROXY is
off/direct-only.
WithVersion, WithModule, WithLimit, WithToken, WithFilter, WithGOOS, WithGOARCH,
WithDoc, WithQuery, WithSymbol, WithExamples, WithImports, WithLicenses, WithReadme,
WithSize, WithExcludePseudo. Each method ignores options that do not apply to it.
Each listing endpoint has an All… variant returning a Go 1.25 iter.Seq2[T, error] that lazily
follows NextToken across pages:
AllSearch, AllVersions, AllSymbols, AllPackages, AllImportedBy. (Vulns is not
paginated — it already returns the full slice — so there is no AllVulns.)
for v, err := range c.AllVersions(ctx, "github.com/samber/lo") {
if err != nil {
return err
}
fmt.Println(v.Version, v.CommitTime)
}WithLimit controls the page size; WithToken sets the starting cursor.
# Install dev dependencies
make tools
# Run tests
make test
# Lint
make lintGive a ⭐️ if this project helped you!
Copyright © 2026 Samuel Berthe.
This project is MIT licensed.