NuGet Server API

This section documents MSBuild 18.9, the .NET SDK 10.0.402 (with its dotnet CLI) and NuGet 7.9, as published in the MSBuild, .NET and NuGet documentation on learn.microsoft.com, which is the reference these pages are written and verified against. Material about .NET 11 / NuGet 8.0 is still in preview and is always flagged as such — never presented as baseline.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The C# Reference bibliography lists the reference material consulted while preparing these pages.

Every NuGet client speaks the same HTTP protocol to every feed. Knowing its shape helps when debugging a private feed, writing a mirror or a script, or reading a restore log full of URLs. This page explores the V3 protocol with curl against nuget.org and a local BaGetter 1.6.5, on 2026-10-23. Hosting a feed is covered on Private Feeds.

The service index

A source URL is the address of a service index: a JSON document listing resources, each with a type and a URL. Clients look up the resource type they need and never construct other URLs themselves.

curl -s https://api.nuget.org/v3/index.json | jq -r '.version, (.resources | length)'
3.0.0
40

Some of nuget.org’s resources (one entry per type; most types appear several times, with versions such as /3.0.0-rc, or for redundant hosts):

SearchQueryService                            https://azuresearch-usnc.nuget.org/query
SearchAutocompleteService                     https://azuresearch-usnc.nuget.org/autocomplete
RegistrationsBaseUrl                          https://api.nuget.org/v3/registration5-semver1/
PackageBaseAddress/3.0.0                      https://api.nuget.org/v3-flatcontainer/
LegacyGallery                                 https://www.nuget.org/api/v2
PackagePublish/2.0.0                          https://www.nuget.org/api/v2/package
SymbolPackagePublish/4.9.0                    https://www.nuget.org/api/v2/symbolpackage
RepositorySignatures/4.7.0                    https://api.nuget.org/v3-index/repository-signatures/4.7.0/index.json
VulnerabilityInfo/6.7.0                       https://api.nuget.org/v3/vulnerabilities/index.json
Catalog/3.0.0                                 https://api.nuget.org/v3/catalog0/index.json
ReadmeUriTemplate/6.13.0                      https://api.nuget.org/v3-flatcontainer/{lower_id}/{lower_version}/readme

(The listing was produced with a short Python filter instead of jq.) The version suffix of a type is the protocol version of that resource, often the NuGet client version that introduced it; a client picks the newest type it understands. BaGetter’s index is much smaller — publish, symbol publish, search, autocomplete, registrations and package base address:

PackagePublish/2.0.0 http://localhost:5555/api/v2/package
SymbolPackagePublish/4.9.0 http://localhost:5555/api/v2/symbol
SearchQueryService http://localhost:5555/v3/search
RegistrationsBaseUrl http://localhost:5555/v3/registration/
PackageBaseAddress/3.0.0 http://localhost:5555/v3/package/
SearchAutocompleteService http://localhost:5555/v3/autocomplete

That is enough for restore, push and search; without VulnerabilityInfo it cannot feed NuGetAudit, which is why a private-only configuration needs auditSources (Auditing Packages for Vulnerabilities).

The resources restore uses

Package base address ("flat container") — the simplest resource and the one restore uses most: a version list per lower-cased ID, and the .nupkg itself. These are the GET lines of a restore log (How Package Installation Works):

curl -s https://api.nuget.org/v3-flatcontainer/humanizer.core/index.json
curl -s -o /dev/null -w '%{http_code} %{content_type}\n' https://api.nuget.org/v3-flatcontainer/humanizer.core/2.14.1/humanizer.core.2.14.1.nupkg
curl -s -o /dev/null -w '%{http_code}\n' https://api.nuget.org/v3-flatcontainer/humanizer.core/9.9.9/humanizer.core.9.9.9.nupkg
{"versions":["2.0.0-beta02-0092","2.0.0","2.0.1", … ,"3.0.1","3.0.8","3.0.10"]}   (32 versions)
200 application/octet-stream
404

Registrations — per-package metadata (dependencies per framework, listing state, deprecation, vulnerabilities) in pages; the registration5-gz-semver2 variant is gzip-compressed and includes SemVer 2.0.0 versions. Its index for humanizer.core had one page covering 2.0.0-beta02-0092 to 3.0.10.

Search and autocomplete — what dotnet package search and the Visual Studio Browse tab call:

curl -s 'https://azuresearch-usnc.nuget.org/query?q=packageid:Humanizer.Core&take=1'

returned one hit, Humanizer.Core 3.0.10, with "verified": true (a reserved ID prefix).

Vulnerability information

VulnerabilityInfo/6.7.0 points to an index of two files — a periodically rebuilt base and an update with everything since:

curl -s --compressed https://api.nuget.org/v3/vulnerabilities/index.json
[{"@name":"base","@id":"https://api.nuget.org/v3-vulnerabilities/2026.10.19.23.33.13/vulnerability.base.json","@updated":"2026-10-19T23:33:13.4608684Z","comment":"The base data for vulnerability update periodically"},
 {"@name":"update","@id":"https://api.nuget.org/v3-vulnerabilities/2026.10.19.23.33.13/2026.10.23.11.33.39/vulnerability.update.json","@updated":"2026-10-23T11:33:39.4634622Z","comment":"The patch data for the vulnerability. Contains all the vulnerabilities since base was last updated."}]

The response is served with Content-Encoding: gzip regardless of the request’s Accept-Encoding; without --compressed, curl saved the raw gzip bytes (505 bytes uncompressed). Clients download these two files once and check every package locally — the audit sends no package IDs to the server.

Publishing, status codes and limits

  • Publish — PUT of the .nupkg to the PackagePublish URL with the API key in the X-NuGet-ApiKey header (dotnet nuget push); DELETE to unlist. Symbol packages go to SymbolPackagePublish. nuget.org returns 409 Conflict for an existing version (Publishing to nuget.org).

  • Status codes — 200/201/202 success, 404 not found (a missing version, as above), 401/403 authentication, 409 conflict, 429 rate limited. For any 5xx or network error the official client retries three times.

  • Authentication — reads are anonymous on nuget.org; private feeds use basic authentication or tokens through credential providers (Authenticated Feeds and Credential Providers).

  • Rate limits — nuget.org’s V3 search is not rate-limited; the V1/V2 search and package endpoints are, per IP address (429 with "Rate limit is exceeded. Try again in N seconds."), and push and unlist have their own limits.

V2, the OData protocol at https://www.nuget.org/api/v2, still answers for old clients (nuget.exe 2.x and some tools) but is deprecated; new feeds and scripts should use V3 only.

Common mistakes

  • Hard-coding v3-flatcontainer URLs — read them from the service index; private feeds use other paths.

  • Upper-case IDs or versions in flat-container URLs — they are lower-cased.

  • Scripting against V2 — deprecated and rate-limited.

  • Fetching the vulnerability files without decompressing — they are gzip-encoded.