# NuGet Package Scraper: .NET Packages & Frameworks (`arman-bd/nuget-package-scraper`) Actor

Extract NuGet registration data: versions, dependency groups by target framework, licence, authors, deprecation and vulnerability notices. No key, no proxy.

- **URL**: https://apify.com/arman-bd/nuget-package-scraper.md
- **Developed by:** [Arman Hossain](https://apify.com/arman-bd) (community)
- **Categories:** Developer tools, Automation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.37 / 1,000 package scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## NuGet Package Scraper: .NET Packages & Frameworks

![NuGet Package Scraper: Every version, per-framework dependency groups, licence, deprecation notices and vulnerability advisories for any NuGet package](https://api.apify.com/v2/key-value-stores/ZQOcNAOHrIgTacAmy/records/nuget-package-scraper.jpg)

Reads registration data straight from the official nuget.org V3 API: every version, dependency groups broken out per target framework, licence expression, authors, publish dates, deprecation notices with their replacement package, and vulnerability advisories.

It talks to `api.nuget.org` over plain HTTP, so there's no credentials, no login, no proxy and no browser. A latest-version lookup costs two requests per package however many hundreds of versions that package has.

**Agent skill: [SKILL.md](https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/nuget-package-scraper.md)**

```
https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/nuget-package-scraper.md
```

### What you get

| Field | What it holds |
|---|---|
| `id`, `latestVersion`, `url` | Package ID as NuGet cases it, the resolved latest version, and the nuget.org page |
| `description`, `summary`, `title`, `tags` | Descriptive metadata from the catalogue entry |
| `authors` | Author list, split from NuGet's comma-separated string |
| `licenseExpression`, `licenseUrl`, `projectUrl`, `iconUrl` | SPDX licence expression and project links |
| `dependencyGroups` | `[{ targetFramework, dependencies: [{ id, range }] }]` for the latest version |
| `targetFrameworks` | Every TFM the latest version declares a dependency group for |
| `dependencyCount` | Total dependencies across all returned groups |
| `isDeprecated`, `deprecationReasons`, `deprecationMessage`, `alternatePackage` | Deprecation state and the package NuGet recommends instead |
| `vulnerabilities` | `[{ advisoryUrl, severity }]` where nuget.org has advisories |
| `isListed`, `published` | Whether the latest version is listed, and when it was published |
| `totalVersions` | Every version ever pushed, prerelease included |
| `versions` | Per-version array with publish date, listed flag, deprecation flag and TFMs (opt-in) |
| `packageContent` | Direct `.nupkg` URL |
| `scrapedAt` | Run timestamp |

`RUN_SUMMARY` in the key-value store holds per-run counts, the flags you used, and any package that failed.

### Use cases

- **Dependency audits.** Licence expression, deprecation state and advisories arrive in one row per package.
- **Framework compatibility.** `targetFramework: "net8.0"` shows exactly what a package needs on that TFM.
- **Replacing dead packages.** `alternatePackage` is NuGet's own recommendation, not a guess.
- **Microsoft-stack tracking.** Publish dates and version counts across a portfolio of packages.
- **SBOM pipelines.** Flat JSON that joins cleanly against your `.csproj` package references.

### Quick start

Two packages, latest stable only:

```json
{
 "packages": ["Newtonsoft.Json", "Serilog"]
}
```

What a package needs on .NET 8 specifically:

```json
{
 "packages": ["Serilog", "Dapper", "Microsoft.Extensions.Logging"],
 "targetFramework": "net8.0"
}
```

Full history including prereleases, for a release-cadence study:

```json
{
 "packages": ["https://www.nuget.org/packages/Serilog"],
 "includeAllVersions": true,
 "includePrerelease": true
}
```

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `packages` | array | required | Package IDs, case-insensitive, or full nuget.org package URLs. Duplicates are removed. |
| `includeAllVersions` | boolean | `false` | Adds the `versions` array. Walks every registration page, so Serilog costs ten extra requests. |
| `includePrerelease` | boolean | `false` | Lets prereleases win `latestVersion` and keeps them in `versions`. |
| `targetFramework` | string | `""` | Case-insensitive exact match on a TFM, such as `net8.0`, `netstandard2.0` or `.NETFramework4.7.1`. |

`totalVersions` is always the true total even with `includeAllVersions` off, because NuGet publishes a per-page leaf count in the registration index. The `targetFramework` filter narrows `dependencyGroups`, `targetFrameworks` and `dependencyCount`; a package with no group for that TFM comes back with all three empty, which is itself the answer to "does this support net8.0?".

### Output example

A real record for `Serilog`, dependency groups truncated to three:

```json
{
 "id": "Serilog",
 "latestVersion": "4.4.0",
 "description": "Simple .NET logging with fully-structured events",
 "summary": null,
 "title": null,
 "authors": ["Serilog Contributors"],
 "licenseExpression": "Apache-2.0",
 "licenseUrl": "https://www.nuget.org/packages/Serilog/4.4.0/license",
 "projectUrl": "https://serilog.net/",
 "iconUrl": "https://api.nuget.org/v3-flatcontainer/serilog/4.4.0/icon",
 "tags": ["serilog", "logging", "semantic", "structured"],
 "dependencyGroups": [
 { "targetFramework": "net10.0", "dependencies": [] },
 {
 "targetFramework": ".NETFramework4.6.2",
 "dependencies": [
 { "id": "System.Diagnostics.DiagnosticSource", "range": "[8.0.1, )" },
 { "id": "System.Threading.Channels", "range": "[8.0.0, )" },
 { "id": "System.ValueTuple", "range": "[4.5.0, )" }
 ]
 }
 ],
 "targetFrameworks": ["net10.0", ".NETFramework4.6.2", ".NETFramework4.7.1", "net6.0", "net8.0", "net9.0", ".NETStandard2.0"],
 "dependencyCount": 7,
 "isDeprecated": false,
 "deprecationReasons": [],
 "deprecationMessage": null,
 "alternatePackage": null,
 "vulnerabilities": [],
 "isListed": true,
 "published": "2026-07-10T03:35:24.047+00:00",
 "totalVersions": 600,
 "versions": [],
 "packageContent": "https://api.nuget.org/v3-flatcontainer/serilog/4.4.0/serilog.4.4.0.nupkg",
 "url": "https://www.nuget.org/packages/Serilog",
 "scrapedAt": "2026-08-06T11:45:24.979Z"
}
```

A deprecated package fills in the fields that matter for an audit. `WindowsAzure.Storage` returns `isDeprecated: true`, `deprecationReasons: ["Legacy"]`, the full maintainer message, and `alternatePackage: "Azure.Storage.Common"`.

### Naming a package

| What you have | What to pass |
|---|---|
| `Newtonsoft.Json` | `Newtonsoft.Json` |
| `newtonsoft.json` | either one, since the registration path is lowercased for you |
| `https://www.nuget.org/packages/Dapper` | either the URL or `Dapper` |

The `id` field in the output uses NuGet's own casing, not yours, so it joins cleanly against `.csproj` references.

### API example

```bash
curl -X POST "https://api.apify.com/v2/acts/arman-bd~nuget-package-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
 "packages": ["Newtonsoft.Json", "Serilog"],
 "targetFramework": "net8.0"
 }'
```

### JavaScript example

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('arman-bd/nuget-package-scraper').call({
 packages: ['Newtonsoft.Json', 'Serilog', 'Dapper'],
 targetFramework: 'net8.0',
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const p of items) {
 const flag = p.isDeprecated ? ` (DEPRECATED, use ${p.alternatePackage})` : '';
 console.log(`${p.id}@${p.latestVersion} [${p.licenseExpression}]${flag}`);
}
```

### Limits and behaviour

- This uses `registration5-gz-semver2`, not `registration5-semver1`. The older feed omits `deprecation` and `vulnerabilities` entirely. The gzipped semver2 feed is the `RegistrationsBaseUrl/3.6.0` resource advertised in nuget.org's own service index, and it is the only one that can answer an audit question.
- Registration pages are not always inlined. Small packages inline their version leaves in the index, while large ones return only page stubs with an `@id` to follow, and Serilog has ten such pages. The Actor detects both and fetches only what it needs.
- Latest-version lookups are cheap. With `includeAllVersions` off, the Actor walks pages newest first and stops at the first page containing a matching version, which is usually one index plus one page whatever the package size.
- Versions are sorted with a real SemVer 2 comparator, so `4.4.0` outranks `4.10.0-beta` and a prerelease never outranks its own release.
- A missing package won't kill the run. nuget.org serves registrations from blob storage, so an unknown ID is an XML 404; it lands in `RUN_SUMMARY.failures` and the run continues. The Actor errors out only if every package failed.
- Transient errors get retried. 429s and 5xx responses get three attempts with linear backoff. A 404 is fatal for that package.
- Public data only. No authentication, no private feeds, no personal data.

### FAQ

**Do I need a proxy?** No. Proxy configuration is not required to run this Actor.

**Do I need a NuGet account or API key?** No. You supply no credentials.

**Are package IDs case-sensitive?** Not for input, because the registration path is lowercased for you. The output `id` carries NuGet's canonical casing.

**Why does a package show no dependency groups?** Either it declares none, or your `targetFramework` filter matched nothing. Drop the filter and check `targetFrameworks` to see what it actually supports.

**Why is `vulnerabilities` usually empty?** NuGet attaches advisories to specific affected versions, and most current releases have none. The field reflects what the registration feed carries for the version returned; it is never inferred.

**Can I read private or self-hosted feeds?** No. This Actor targets nuget.org only.

**Can I plug it into something else?** Yes. Apify API, the client libraries, webhooks, scheduled runs, dataset exports to JSON, CSV or Excel, or MCP. The output is structured JSON.

# Actor input Schema

## `packages` (type: `array`):

NuGet package IDs, case-insensitive. Full nuget.org package URLs are accepted and the ID is extracted automatically.

## `includeAllVersions` (type: `boolean`):

Add a `versions` array covering every published release. This walks all registration pages, so a package like Serilog costs ten extra requests instead of one. Off by default.

## `includePrerelease` (type: `boolean`):

Treat prerelease builds as candidates for `latestVersion` and keep them in the version history. Off by default, so you get the newest stable release.

## `targetFramework` (type: `string`):

Keep only dependency groups whose target framework matches this string, case-insensitively, for example 'net8.0' or 'netstandard2.0'. Leave empty to return every group.

## Actor input object example

```json
{
  "packages": [
    "Microsoft.Extensions.Logging",
    "https://www.nuget.org/packages/Dapper"
  ],
  "includeAllVersions": false,
  "includePrerelease": false,
  "targetFramework": "net8.0"
}
```

# Actor output Schema

## `items` (type: `string`):

Every record the run produced.

## `runsummary` (type: `string`):

The RUN_SUMMARY record from the run's key-value store.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "packages": [
        "Newtonsoft.Json",
        "Serilog"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("arman-bd/nuget-package-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "packages": [
        "Newtonsoft.Json",
        "Serilog",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("arman-bd/nuget-package-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "packages": [
    "Newtonsoft.Json",
    "Serilog"
  ]
}' |
apify call arman-bd/nuget-package-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,arman-bd/nuget-package-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/xI0c58SFUtIkX1adw/builds/zYOP95b7ixHo50wwt/openapi.json
