Check if nuget package exists using command line

The approach that I had used, which was based on some of the other answers, recently break, so I want to describe more fully how we go about discovering the URLs and doing the actual validation.

NuGet.org provides a service index that provides the list of base URLs as indexed by type. This service index is available at https://api.nuget.org/v3/index.json.

It returns data in the following structure (truncated for brevity):

PS> Invoke-RESTmethod -uri 'https://api.nuget.org/v3/index.json'

version resources
------- ---------
3.0.0   {@{@id=https://azuresearch-usnc.nuget.org/query; ... 

In other words, the service index provides a set of available API versions and a corresponding set of resources (i.e., endpoints/queryable APIs). Those resources correspond to URLs that have very specific capabilities and which should all be documented. at the time of this writing, we only have version 3.0.0 as an option, so let's examine those resources we can see what those endpoints look like, filtering the output to endpoints related to package registration and metadata:

PS> (Invoke-RESTmethod -uri 'https://api.nuget.org/v3/index.json' | `
    ? { $_.version -eq '3.0.0' }).resources | `
    ? { $_.'@type' -like '*Registration*' } | `
    select-object -Property '@id','@type'

@id                                                @type
---                                                -----
https://api.nuget.org/v3/registration4/            RegistrationsBaseUrl
https://api.nuget.org/v3/registration4/            RegistrationsBaseUrl/3.0.0-rc
https://api.nuget.org/v3/registration4/            RegistrationsBaseUrl/3.0.0-beta
https://api.nuget.org/v3/registration4-gz/         RegistrationsBaseUrl/3.4.0
https://api.nuget.org/v3/registration4-gz-semver2/ RegistrationsBaseUrl/3.6.0
https://api.nuget.org/v3/registration4-gz-semver2/ RegistrationsBaseUrl/Versioned

One useful property that I left off the output is that a comment property exists that provides a useful description of the endpoint. For example, for RegistrationsBaseUrl we have the following comment:

Base URL of Azure storage where NuGet package registration info is stored

From the above, we can see that our base URL will be one of the URLs above on the left. Checking the docs for @type RegistrationsBaseUrl:

These registrations are not compressed (meaning they use an implied Content-Encoding: identity). SemVer 2.0.0 packages are excluded from this hive.

For my needs, I want to include SemVer 2.0.0, so I want to use a different endpoint. Again checking the docs:

These registrations are compressed using Content-Encoding: gzip. SemVer 2.0.0 packages are included in this hive.

So, per the earlier query results I want the following url:

https://api.nuget.org/v3/registration4-gz-semver2/

In reading through the documentation we learn that we're interested in the Registration Page for the package that we're trying to query. The URL syntax will be:

$BASEURL/<PackageName>/<PackageVersion>.json

Using Newtonsoft.Json as a sample package and expanding:

https://api.nuget.org/v3/registration4-gz-semver2/newtonsoft.json/12.0.3.json

Now that we know how we get the URL, and the fact that it can change over time and should be dynamically retrieved, let's write some PowerShell that will determine whether a NuGet package has been published to a given source:

param (
  [Parameter(Mandatory=$true)] 
  [string]$Id,
  [Parameter(Mandatory=$true)] 
  [string]$Version,
  [Parameter(Mandatory=$false)] 
  [string]$source = "https://api.nuget.org/v3/index.json"
)

$resources = (invoke-restmethod -uri $source | ? { $_.version -eq '3.0.0' }).resources
$baseUrl = ($resources | ? { $_.'@type' -eq 'RegistrationsBaseUrl/3.6.0' }).'@id'

try
{
  $packageName = $Id.ToLowerInvariant()
  $packageVersion = $Version.ToLower()

  # Use supported HEAD method for performance
  Invoke-RestMethod -uri "$($baseUrl)$($packageName)/$($packageVersion).json" -Method HEAD > $null 2>&1
  # method didn't throw so the NuGet package exists
  $true
}
catch
{
  # method threw, so the NuGet package doesn't exist
  $false
}

Its use would look like the following:

PS>  Get-NuGetPackageExists.ps1 -Id Newtonsoft.Json -Version 10.0.2
True

I do not know if your private NuGet server implements the same server Api as nuget.org, but I will assume it does. https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource describes the API that can be used to check if a package is published and get its versions. Unlike nuget list command it performs exact match by the package Id.

For example, suppose you wish to know whether the package AjaxMin is published. Executing nuget list ajaxmin is not very helpful:

C:\> nuget.exe list ajaxmin
BundleTransformer.MicrosoftAjax 1.10.0
Bundler.NET 1.5.7
CodeSlice.Web.Baler.Extensions.AjaxMinifier 0.2.0
combres 2.3.0.4
combres.log4net 2.3.0.4
combres.mvc 2.3.0.4
Delegate.AjaxMinBuilder 1.1.2
DotLessMinification 0.42.1
AjaxMin 5.14.5506.26202
NUglify 1.5.1
NUglify 1.5.13
Pvc.Ajaxmin 0.0.2
RequestReduce 1.8.76
WebMarkupMin.Core 2.6.0
WebMarkupMin.MsAjax 2.6.0
Web.Optimization.Bundles.AjaxMin 0.0.8
Web.Optimization.Bundles.YUI 0.0.8
C:\>

Yes, you can filter the output, but I find it too much work. You can be more precise by using the server Api. Observe:

C:\> $url = 'https://api.nuget.org/v3/registration3-gz-semver2/ajaxmin/index.json'
C:\> $res = Invoke-RestMethod $url
C:\> $res.items.items.catalogEntry.version
4.12.4057.21792
4.13.4076.28499
4.19.4141.18463
4.30.4295.16112
4.36.4337.24224
4.37.4345.34101
4.39.4362.37207
4.40.4369.35534
4.41.4378.21434
4.42.4387.23950
4.43.4392.20083
4.44.4396.18853
4.45.4416.14249
4.46.4422.26284
4.47.4452.34008
4.48.4489.28432
4.49.4503.16524
4.50.4504.34801
4.51.4507.18296
4.52.4518.14738
4.53.4526.21623
4.54.4533.37029
4.55.4545.19776
4.56.4560.33404
4.58.4566.27257
4.59.4576.13504
4.60.4609.17023
4.61.4617.31171
4.62.4618.15628
4.63.4630.14654
4.64.4630.17932
4.66.4633.35991
4.67.4639.17289
4.68.4663.23906
4.69.4665.24361
4.70.4668.12892
4.71.4679.26350
4.72.4679.35523
4.73.4685.17669
4.74.4698.25434
4.75.4713.17606
4.76.4714.20550
4.77.4723.25304
4.78.4724.23869
4.80.4763.16598
4.81.4769.14860
4.82.4784.14537
4.83.4785.14876
4.84.4790.14417
4.85.4828.21154
4.86.4836.34222
4.89.4861.30057
4.90.4864.13402
4.91.4875.26882
4.92.4896.13361
4.93.4902.12739
4.94.4916.15482
4.95.4924.12392
4.96.4941.15389
4.97.4951.28483
5.0.5007.14585
5.1.5007.23730
5.2.5021.15814
5.3.5068.16463
5.4.5085.25629
5.5.5091.22839
5.6.5100.19204
5.7.5124.21499
5.8.5172.27710
5.9.5229.26438
5.10.5260.16959
5.11.5295.12309
5.12.5436.22734
5.13.5463.15282
5.14.5506.26202
C:\>

We get all the versions of the package. Now, if you want to check if a certain version known beforehand is published - you can:

C:\> $url = 'https://api.nuget.org/v3/registration3-gz-semver2/ajaxmin/5.14.5506.26202.json'
C:\> $res = Invoke-RestMethod $url
C:\> $res


@id            : https://api.nuget.org/v3/registration3-gz-semver2/ajaxmin/5.14.5506.26202.json
@type          : {Package, http://schema.nuget.org/catalog#Permalink}
catalogEntry   : https://api.nuget.org/v3/catalog0/data/2018.10.06.23.54.33/ajaxmin.5.14.5506.26202.json
listed         : True
packageContent : https://api.nuget.org/v3-flatcontainer/ajaxmin/5.14.5506.26202/ajaxmin.5.14.5506.26202.nupkg
published      : 2015-01-28T22:45:45.577+00:00
registration   : https://api.nuget.org/v3/registration3-gz-semver2/ajaxmin/index.json
@context       : @{@vocab=http://schema.nuget.org/schema#; xsd=http://www.w3.org/2001/XMLSchema#; catalogEntry=; registration=; packageContent=; published=}



C:\>

Now what happens if we ask for a version that does not exist? Here we go:

C:\> $url = 'https://api.nuget.org/v3/registration3-gz-semver2/ajaxmin/5.14.5506.26196.json'
C:\> $res = Invoke-RestMethod $url
Invoke-RestMethod : BlobNotFoundThe specified blob does not exist.
RequestId:cd1579e6-801e-007d-5eba-64ac9d000000
Time:2019-09-06T13:53:54.6832811Z
At line:1 char:8
+ $res = Invoke-RestMethod $url
+        ~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
C:\>

How about asking for a non existing package? Let us search for ajaxmi (no 'n'):

C:\> $url = 'https://api.nuget.org/v3/registration3-gz-semver2/ajaxmi/index.json'
C:\> $res = Invoke-RestMethod $url
Invoke-RestMethod : BlobNotFoundThe specified blob does not exist.
RequestId:23d96274-b01e-0018-3bba-641dc0000000
Time:2019-09-06T13:55:35.5288968Z
At line:1 char:8
+ $res = Invoke-RestMethod $url
+        ~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
C:\>

You can call this CMD script from PowerShell easy enough, with examples below it. You can just go by $LastExitCode to determine how to proceed, with 0 meaning you can publish:

check_nupkg.bat

@echo off
SetLocal EnableDelayedExpansion EnableExtensions
pushd "%~dp0"

set "pkg_name=%~1"
set "pkg_version=%~2"

call nuget list %pkg_name% -AllVersions -Prerelease|findstr /i /r /c:"^%pkg_name% %pkg_version%$" -N>nul 2>&1

if not "%errorlevel%"=="0" (
    echo This package can be published
    exit /b 0
) else (
    echo This package has already been published.
    exit /b 1
)

C:\stuff>.\check_nupkg.bat "lolspeak" "1.0.0"

This package has already been published.

C:\stuff>check_nupkg.bat "lolspeak" "11.0.0"

This package can be published