License Field Inconsistencies Across npm, PyPI, and crates.io
Description and use
License field inconsistencies across npm, PyPI, and crates.io make it hard to automatically determine a dependency’s license. The author examined how each registry stores license data, finding that npm provides a clear SPDX identifier, PyPI uses multiple partially populated fields, and crates.io’s sparse index lacks license information, requiring a separate REST API call.
Problem
Determining a dependency’s license using only the registry’s API is unreliable because each registry exposes license information differently.
Outcome
The study revealed that npm consistently provides a single SPDX license field, PyPI offers three partially populated fields with the new license_expression being most reliable, and crates.io’s sparse index lacks license data entirely, requiring a separate REST call.
Workflow
- 1. Query npm:
GET https://registry.npmjs.org/{pkg}/latestand read the top‑levellicensefield. - 2. Query PyPI:
GET https://pypi.org/pypi/{pkg}/jsonand examineinfo.license,info.license_expression, andinfo.classifiersforLicense ::entries. - 3. Query crates.io: use the sparse index
https://index.crates.io/{path}to get version data (no license field) and, if needed, the separate REST endpointhttps://crates.io/api/v1/crates/{pkg}for license info. - 4. Normalize the retrieved data into a consistent SPDX identifier or full license text.
- 5. Store the normalized license in a SBOM or compliance report.
- 6. If any field is missing, fall back to the next available field or flag the package for manual review.
Evidence
npm answered cleanly. The other two did not, and not in the way I expected.
PyPI’s GET … doesn’t have one license field. It has three: info.license, info.license_expression, and info.classifiers.
cargo doesn’t talk to crates.io’s website. It talks to a sparse index … No license key.
- Record date: 2026-09-09T16:15:27.310Z