Skip to main content

LicenseID - A Portable SPDX License ID matcher

PyPI - Version GitHub License OpenSSF Best Practices OpenSSF Scorecard DOI

Get the SPDX License ID from license text.

A portable license ID matcher with command line interface and Python API. No database daemon or server needed.

Used as a license detection engine for Pitloom software bill of materilas generator.

Features

  • Hybrid matching pipeline:
    • Tier 0.5 (Marker detection): Detects SPDX-License-Identifier tags and structured markers (name fields, headings). An exact SPDX tag returns immediately with full confidence.
    • Tier 0 (Shortcut): Fast path for short inputs (names, IDs, brief expressions). Includes:
      • Case-insensitive exact ID match.
      • Prose-context disambiguation for bare deprecated IDs (e.g. "GPL-2.0 or later version" → GPL-2.0-or-later).
      • Conservative -only fallback when no granting context is present.
    • Tier 1 (Recall): Candidate retrieval using SQLite FTS5 trigram index, capped at the first 100 query words for consistent performance. Comment prefixes (//, #, *, ;) are stripped before querying.
    • Tier 2 (Precision): Adaptive ranking with RapidFuzz. Sliding-window alignment for fragments; coverage-aware scoring to prefer the tightest match. Marker confidence boosts applied only when confidence ≥ 0.85.
    • Tier 3 (Validation): Optional final validation via tools-java.
  • Deprecated ID normalisation:
    • GPL-2.0+ → GPL-2.0-or-later (SPDX + operator, unambiguous).
    • Apache-2+ → Apache-2.0+ (abbreviated base canonicalised, + retained).
    • Bare deprecated IDs (e.g. GPL-2.0) resolved conservatively to -only when no surrounding context is available.
  • SPDX License Expression support (via py-spdx-license):
    • Structural normalisation of AND/OR expressions: duplicate operands collapse (MIT AND MIT → MIT) and operands are put in a consistent order, making equivalent expressions compare equal.
    • WITH <exception> expressions are validated and matched against the SPDX exception list, not just the license list — e.g. licenseid match --id "MIT WITH Font-exception-2.0" resolves correctly even though it isn't a single license row.
  • Unix philosophy: Parseable, line-delimited CLI output.

Installation

Install with pipx:

pipx install licenseid

Or using uv:

uv tool install licenseid

Usage

1. Update the license database

Before matching, you need to build the local license index:

licenseid update

Advanced update options:

  • --version <version>: Download a specific SPDX License List version (e.g., 3.28.0).
  • --force: Force update even if the local database is already at the target version.
  • --no-cache: Bypass the local cache for downloads.

2. Identify a license

Identify license text from a file, an ID, or a string:

# From a file (smart detection)
licenseid match LICENSE.txt

# From an ID (smart detection)
licenseid match MIT

# From a string (smart detection / piped)
echo "MIT License..." | licenseid match

# Explicit ID lookup (fastest, skips similarity check)
licenseid match --id MIT

Common options:

  • --db <path>: Use a custom database path (global option). Supports SQLite URIs for in-memory databases (e.g., file:test?mode=memory&cache=shared).
  • --id <id>: Explicitly treat input as an SPDX License ID (bypasses file/text matching).
  • --bold: Print only the top license ID (no other info).
  • --diff: Show a word-by-word diff between the input and the best-matching candidate.
  • --json: Output results in JSON format.

The system uses a composite score (similarity + coverage bonus/penalty + optional popularity weight + marker confidence boost) to prefer the tightest match. For example, it distinguishes a short permissive licence from a superset that shares the same preamble.

3. Cache management

licenseid maintains a local cache of remote data to save bandwidth.

  • licenses.json: Cached for 45 days.
  • popularity.csv: Cached for 75 days.
  • SPDX data tarballs are versioned and never expire.

To clear the cache manually:

licenseid --clear-cache

4. Output formats

Default (Unix-friendly):

LICENSE_ID=Apache-2.0 SIMILARITY=0.9850 COVERAGE=1.0000

ID only:

licenseid match LICENSE.txt --bold

Example output:

Apache-2.0

JSON:

licenseid match LICENSE.txt --json

Example output:

[
  {
    "license_id": "Apache-2.0",
    "score": 0.985,
    "similarity": 0.985,
    "coverage": 1.0,
    "is_spdx": true,
    "is_osi_approved": true
  }
]

Diff (visual comparison):

licenseid match LICENSE.txt --diff

Example output:

LICENSE_ID=Apache-2.0 SIMILARITY=0.9980 COVERAGE=0.9975

WORD DIFF:
--- DATABASE
+++ INPUT
@@ -1601,8 +1601,4 @@
 language
 governing
 permissions
-and
-limitations
-under
-the
-license
+se

5. Exit codes

The CLI follows standard Unix exit code conventions, making it suitable for use in scripts and CI/CD pipelines.

Exit Code Meaning Scenarios
0 Success Confident match found; predicate is TRUE; database updated or already up-to-date.
1 Logic Failure No matching license found; predicate is FALSE; network error.
2 Usage Error Missing subcommand; missing input text/file; invalid parameters.

6. License predicates (for CI/CD)

Predicate commands are designed for shell scripting. They print true/false and exit with 0 (for true) or 1 (for false).

Command Description
is-spdx True if the license is in the SPDX License List.
is-open True if the license is OSI-approved OR FSF-libre.
is-free Alias for is-open.
is-osi True if the license is OSI-approved.
is-fsf True if the license is FSF-libre.

Example usage in a script:

# Check by ID
if licenseid is-osi MIT; then
  echo "This is an OSI-approved license."
fi

# Check by File
licenseid is-open LICENSE.txt || echo "Warning: Not an open source license"

# Check by Text (via stdin)
echo "MIT License..." | licenseid is-fsf && echo "FSF Libre!"

Python API

You can use licenseid directly in your Python projects:

from licenseid.matcher import AggregatedLicenseMatcher

# Initialize with default database
matcher = AggregatedLicenseMatcher()

# 1. Match by Raw Text (Positional or Keyword)
# Programmatic API is explicit: positional 'text' is always treated as text.
results = matcher.match("Permission is hereby granted...")
results = matcher.match(text="Custom license text...")

# 2. Match by SPDX License ID (Explicit)
# This performs a fast database lookup and returns full metadata.
results = matcher.match(license_id="MIT")

# 3. Match by File Path (Explicit)
results = matcher.match(file_path="LICENSE.txt")

# 4. Predicates
# Supports keyword arguments for precise control.
if matcher.is_osi(license_id="MIT"):
    print("OSI Approved!")

if matcher.is_open(file_path="LICENSE.txt"):
    print("Open Source!")

if matcher.is_spdx(text="Creative Commons Zero v1.0 Universal"):
    print("SPDX Match Found!")

Example JSON output:

[
  {
    "license_id": "MIT",
    "score": 1.01,
    "similarity": 1.0,
    "coverage": 0.0
  }
]

Development

Running tests

Regular test suite:

pytest

Run benchmarks and accuracy tests (expensive):

pytest --run-benchmark

Configuration

  • SPDX_TOOLS_JAR: Path to the tools-java jar for Tier 3 validation.

License

Apache-2.0

Citation

If you use this software, please cite it as follows:

Suriyawongkul, A. (2026). LicenseID - A Portable SPDX License ID Matcher (Version 0.3.5) [Computer software]. https://doi.org/10.5281/zenodo.19881009

BibTeX:

@software{Suriyawongkul_LicenseID_-_A_2026,
    author = {Suriyawongkul, Arthit},
    doi = {10.5281/zenodo.19881009},
    license = {Apache-2.0},
    month = aug,
    title = {{LicenseID - A Portable SPDX License ID Matcher}},
    url = {https://github.com/bact/licenseid.git},
    version = {0.3.5},
    year = {2026}
}

Metadata

Release files for licenseid 0.3.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for licenseid 0.3.6
File Size Uploaded
licenseid-0.3.6.tar.gz 158.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for licenseid 0.3.6
File Interpreter ABI Platform
licenseid-0.3.6-py3-none-any.whl Python 3 none any Details

Total release size: 226.9 kB

Release files / licenseid-0.3.6.tar.gz

Download URL licenseid-0.3.6.tar.gz
Size 158.7 kB
Tags Source
SHA-256 checksum
How to use checksums
3007da5ff4a75106a1be4152f868c63d739da779427c92f0f2969312579c5762
BLAKE2b-256 checksum
How to use checksums
6be0b102ba7f60a0a52ad4b0bde5d90fbc61abbc6ca78ebcf76aef6297887450
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 19, 2026.

Transparency log

Release files / licenseid-0.3.6-py3-none-any.whl

Download URL licenseid-0.3.6-py3-none-any.whl
Size 68.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
263333344fccb5a379ed164b5440e31e83e50bb587bf3b447ecf9305c88a6fa6
BLAKE2b-256 checksum
How to use checksums
432a26e0fa1011036fc25720105e39725b6629e24f0761a20dbd293a6a09c08b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 19, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.7

2 release files

This release

0.3.6 This release

2 release files

0.3.5

2 release files

0.3.4

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page