PyPI Commands
Overview
The netapi pypi commands provide CLI access to the Python Package Index (PyPI) JSON API. All commands support -f json for jq piping.
-
JSON API (
pypi.org/pypi/{package}/json) — package metadata, versions, downloads -
Simple API (
pypi.org/simple/{package}/) — lightweight version listing (HTML)
Prerequisites
No authentication required. The JSON API is public, free, and has no documented rate limits.
# No tokens or keys needed
netapi pypi info requests
Commands (8 total)
| Command | Description |
|---|---|
|
Package metadata (name, version, summary, license, author) |
|
List all released versions |
|
List dependencies (requires_dist) |
|
Download URLs for latest release |
|
Release history with dates |
|
Search packages (via PyPI search or warehouse) |
|
Compare two packages side-by-side |
|
Raw API access |
JSON Output
All commands support -f json for machine-readable output:
netapi pypi info requests -f json
netapi pypi versions django -f json
netapi pypi deps flask -f json
jq Patterns
Exploring Structure
# Top-level keys
netapi pypi info requests -f json | jq '.info | keys'
# Full info object
netapi pypi info requests -f json | jq '.info'
Package Information
# Summary card
netapi pypi info requests -f json | jq '{name: .info.name, version: .info.version, summary: .info.summary, license: .info.license}'
# Author and project URLs
netapi pypi info flask -f json | jq '{author: .info.author, home_page: .info.home_page, project_urls: .info.project_urls}'
# Python version requirement
netapi pypi info django -f json | jq -r '.info.requires_python'
# Package classifiers (filtered)
netapi pypi info requests -f json | jq -r '.info.classifiers[] | select(startswith("Programming Language"))'
Version Listing
# All versions
netapi pypi versions requests -f json | jq '.'
# Latest 10 versions
netapi pypi versions django -f json | jq '.[-10:]'
# Major versions only (X.Y format)
netapi pypi versions django -f json | jq '[.[] | select(test("^[0-9]+\\.[0-9]+$"))] | sort | .[-10:]'
Raw API Access
# Any package
netapi pypi api requests | jq '.'
# Specific version
netapi pypi api requests/2.28.0 | jq '.info.version'
Useful jq Recipes
Dependency Tree (One Level)
netapi pypi deps flask -f json | jq -r '.[] | select(contains("extra") | not) | split(" ")[0]' \
| xargs -I{} netapi pypi info "{}" -f json | jq '{name: .info.name, version: .info.version}'
Export to CSV
for pkg in requests flask django fastapi; do
netapi pypi info "$pkg" -f json | jq -r '[.info.name, .info.version, .info.license] | @csv'
done
Check for Updates
# Compare installed vs latest
pip list --format=json | jq -r '.[].name' | head -10 | while read pkg; do
latest=$(netapi pypi info "$pkg" -f json 2>/dev/null | jq -r '.info.version // "unknown"')
installed=$(pip show "$pkg" 2>/dev/null | awk '/^Version:/ {print $2}')
[[ "$installed" != "$latest" ]] && echo "$pkg: $installed → $latest"
done
curl Equivalents (Works Now)
These raw curl + jq patterns work today without the netapi CLI. No authentication required.
Package Metadata
# Full package info
curl -s https://pypi.org/pypi/requests/json \
| jq '{name: .info.name, version: .info.version, summary: .info.summary, license: .info.license, home_page: .info.home_page}'
# Author and URLs
curl -s https://pypi.org/pypi/flask/json \
| jq '{author: .info.author, urls: .info.project_urls}'
# Python version requirement
curl -s https://pypi.org/pypi/django/json | jq -r '.info.requires_python'
# Package description (first 300 chars)
curl -s https://pypi.org/pypi/fastapi/json | jq -r '.info.summary'
Version Listing
# All versions (keys of releases object)
curl -s https://pypi.org/pypi/requests/json | jq '[.releases | keys[]]'
# Latest 10 versions
curl -s https://pypi.org/pypi/django/json | jq '[.releases | keys[]] | sort | .[-10:]'
# Major versions only
curl -s https://pypi.org/pypi/django/json \
| jq '[.releases | keys[] | select(test("^[0-9]+\\.[0-9]+$"))] | sort | .[-10:]'
# Specific version metadata
curl -s https://pypi.org/pypi/requests/2.28.0/json \
| jq '{version: .info.version, requires_python: .info.requires_python}'
Dependencies
# All dependencies
curl -s https://pypi.org/pypi/flask/json | jq '.info.requires_dist'
# Required only (no extras)
curl -s https://pypi.org/pypi/flask/json \
| jq -r '.info.requires_dist[] | select(contains("extra") | not)'
# Just package names (strip version specifiers)
curl -s https://pypi.org/pypi/flask/json \
| jq -r '.info.requires_dist[] | select(contains("extra") | not) | split(" ")[0]'
# Deps for a specific extra
curl -s https://pypi.org/pypi/flask/json \
| jq -r '.info.requires_dist[] | select(contains("async"))'
Package Comparison
# Compare two HTTP libraries
for pkg in requests httpx; do
curl -s "https://pypi.org/pypi/${pkg}/json" \
| jq --arg p "$pkg" '{name: $p, version: .info.version, summary: .info.summary}'
done | jq -s '.'
# Compare web frameworks
for pkg in flask django fastapi; do
curl -s "https://pypi.org/pypi/${pkg}/json" \
| jq --arg p "$pkg" '{name: $p, version: .info.version, python: .info.requires_python, license: .info.license}'
done | jq -s '.'
Download URLs
# Latest release download URLs
curl -s https://pypi.org/pypi/requests/json \
| jq '.urls[] | {filename, packagetype, size: .size, url: .url}'
# Wheel only
curl -s https://pypi.org/pypi/requests/json \
| jq '.urls[] | select(.packagetype == "bdist_wheel") | {filename, size, python_requires: .requires_python}'
# Source distribution only
curl -s https://pypi.org/pypi/requests/json \
| jq '.urls[] | select(.packagetype == "sdist") | .url'
Release History
# Release dates (latest 10 versions)
curl -s https://pypi.org/pypi/requests/json \
| jq '[.releases | to_entries | .[] | select(.value | length > 0) | {version: .key, date: .value[0].upload_time[0:10]}] | sort_by(.date) | .[-10:]'
Dependency Tree (One Level Deep)
# Get Flask's deps, then look up each one
curl -s https://pypi.org/pypi/flask/json \
| jq -r '.info.requires_dist[] | select(contains("extra") | not) | split(" ")[0]' \
| xargs -I{} sh -c 'curl -s "https://pypi.org/pypi/{}/json" | jq "{name: .info.name, version: .info.version}"'
Security: Check Package Hashes
# SHA256 digests for verification
curl -s https://pypi.org/pypi/requests/json \
| jq '.urls[] | {filename, sha256: .digests.sha256}'
Bulk Package Info
# Audit your requirements.txt
cat requirements.txt | grep -v '^#' | awk -F'[=<>]' '{print $1}' | while read pkg; do
curl -s "https://pypi.org/pypi/${pkg}/json" 2>/dev/null \
| jq -r --arg p "$pkg" '"\($p): \(.info.version // "not found")"'
done
# Export package inventory to CSV
for pkg in requests flask django celery redis pydantic; do
curl -s "https://pypi.org/pypi/${pkg}/json" \
| jq -r '[.info.name, .info.version, .info.license, .info.requires_python // "any"] | @csv'
done