The .NET client for the Open Source Initiative License API — list, search and filter the canonical catalog of OSI Approved Licenses® from C#, F# or any other .NET language, including the human-readable license text.
A lightweight, multi-targeted (.NET 10 / netstandard2.0) library built on the official OSI API. It fetches the OSI license catalog, supports filtering by SPDX ID, name, keyword, and steward, and extracts human-readable license text from the OSI HTML pages. No caching layer — every call goes to the network; callers that want caching add their own decorator.
- List all OSI Approved Licenses®, streamed as
IAsyncEnumerable - Look up a single license by its OSI ID (e.g.
mit,gpl-3-0) - Filter by SPDX ID, including
*wildcards (GPL*,*-2.0) - Search licenses by name
- Filter by classification keyword via a strongly typed enum (e.g.
PopularStrongCommunity) - Filter by steward organization (e.g.
eclipse-foundation) - Full license metadata: version, submission and approval dates, stewards, keywords, links
- Human-readable license text for every license — something the API payload itself does not contain
- First-class dependency injection support (
IHttpClientFactory,ILogger), or usable standalone with a singlenew OsiClient() - Runs on .NET 10 and on every
netstandard2.0-compatible runtime (.NET Framework, .NET Core, Mono, Unity)
dotnet add package OpenSourceInitiative.LicenseApiOr as a package reference:
<PackageReference Include="OpenSourceInitiative.LicenseApi" Version="3.*" />services.AddOsiLicensesClient();
// With options
services.AddOsiLicensesClient(options =>
{
options.BaseAddress = new Uri("https://..."); // default: OSI API
});Resolve IOsiClient from the container:
var client = sp.GetRequiredService<IOsiClient>();
// Stream all licenses
await foreach (var license in client.GetAllLicensesAsyncEnumerable())
Console.WriteLine(license?.Name);
// Filter
var gplFamily = await client.GetBySpdxIdAsync("GPL*");
var popular = await client.GetByKeywordAsync(OsiLicenseKeyword.PopularStrongCommunity);await using var client = new OsiClient();
var mit = await client.GetByOsiIdAsync("mit");
Console.WriteLine(mit?.LicenseText);The primary interface for all license queries. Lives at OpenSourceInitiative.LicenseApi.Interfaces.
| Method | Description |
|---|---|
GetAllLicensesAsyncEnumerable(token) |
Streams the full catalog as IAsyncEnumerable<OsiLicense?>. |
GetByOsiIdAsync(id, token) |
Fetches a single license by its OSI ID (e.g. "mit"). |
GetBySpdxIdAsync(id, token) |
Filters by SPDX ID; supports * wildcards ("GPL*", "*-2.0"). |
GetByNameAsync(name, token) |
Filters by human-readable name. |
GetByKeywordAsync(keyword, token) |
Filters by OsiLicenseKeyword enum value. |
GetByStewardAsync(steward, token) |
Filters by steward organization slug. |
Implements IDisposable and IAsyncDisposable.
Input handling:
id/name/steward/keywordarguments are always treated as opaque values and escaped before being combined with the configuredBaseAddress— a caller passing a full URL, a//hostreference, or..segments through one of these methods cannot redirect the request to a different host. Don't rely on this as input validation, though: garbage in still means "license not found" or aJsonException, not a meaningful error.
public sealed class — the concrete HTTP implementation of IOsiClient. Registered as a typed IHttpClientFactory client named "OsiClient" when using DI.
Constructor: OsiClient(ILogger<OsiClient>? logger, OsiClientOptions? options, HttpClient? httpClient) — all parameters optional. When httpClient is null, the client owns and disposes the inner HttpClient; when provided externally it is not disposed.
License text is fetched automatically per license via a two-step strategy:
- OSI HTML page — extracts the node with CSS class
license-content. - Steward HTML URL — last resort, only used if the steward URL doesn't already point at a
.txtfile.
If both steps fail (network error, unreachable page, markup without a license-content node), LicenseText is left as an empty string rather than throwing — a scraping failure on a single license does not fail the whole call.
public sealed class — configures the DI registration. Lives at OpenSourceInitiative.LicenseApi.Options.
| Property | Type | Default | Description |
|---|---|---|---|
BaseAddress |
Uri |
https://opensource.org/api/ |
OSI API base URL. |
PrimaryHandlerFactory |
Func<HttpMessageHandler>? |
null |
Injects a custom primary handler (useful for testing). |
UserAgent |
IList<ProductInfoHeaderValue> |
Assembly name + version | Added to every request. |
HttpClientHandler |
HttpClientHandler |
AllowAutoRedirect = true |
Used when no external HttpClient is supplied. |
public sealed record — represents one OSI license entry. Lives at OpenSourceInitiative.LicenseApi.Models.
| Property | Type | Notes |
|---|---|---|
Id |
string |
OSI-internal identifier (e.g. "mit"). |
Name |
string |
Human-readable name. |
SpdxId |
string? |
SPDX identifier (e.g. "MIT"). |
Version |
string? |
Optional version string. |
SubmissionDate |
DateTime? |
Parsed from yyyyMMdd via CustomFormatDateTimeConverter. |
ApprovalDate |
DateTime? |
Parsed from yyyyMMdd. |
Approved |
bool |
OSI approval status. |
Keywords |
IReadOnlyCollection<OsiLicenseKeyword> |
Deserialized via OsiLicenseKeywordsConverter; unknown tokens are ignored. |
Stewards |
IReadOnlyCollection<string> |
Steward organization slugs. |
Links |
OsiLicenseLinks |
HAL-style _links object. |
LicenseText |
string? |
Extracted plain text — not part of the API payload, populated by the client after fetching. Empty string if scraping failed. Mutable, so you can set it yourself when constructing an OsiLicense by hand (e.g. in tests). |
OsiLicenseExtensions adds license.GetLicenseText(httpClient, token) and license.GetAndSetLicenseText(httpClient, token) for re-fetching or backfilling the text on an existing instance.
public enum — OSI classification keywords. Lives at OpenSourceInitiative.LicenseApi.Enums.
Values: PopularStrongCommunity, International, SpecialPurpose, NonReusable, Superseded, VoluntarilyRetired, RedundantWithMorePopular, OtherMiscellaneous, Uncategorized.
Serializes to/from the OSI API string tokens (e.g. popular-strong-community) via OsiLicenseKeywordsConverter.
public static class — the single registration entry point. Lives at OpenSourceInitiative.LicenseApi.Extensions.
AddOsiLicensesClient(this IServiceCollection, Action<OsiClientOptions>?) — registers a named IHttpClientFactory client ("OsiClient") and IOsiClient → OsiClient as transient.
public abstract/sealed class — base and derived exception types. Live at OpenSourceInitiative.LicenseApi.Exceptions.
OsiInitializationException is thrown by the obsolete OsiLicensesClient.InitializeAsync when the catalog fetch fails.
public class JsonConverter<DateTime?> — handles the yyyyMMdd date format used by the OSI API for submission and approval dates. Registered via [JsonConverter] attributes on OsiLicense. Null and empty strings deserialize to null; an unrecognized format throws JsonException.
[Obsolete] — wraps IOsiClient with an eager-loading, snapshot-based API kept for backward compatibility. Use IOsiClient directly for new code. Unlike IOsiClient, its SearchAsync/Search assume every entry returned by the API has a non-null Id/Name; a malformed catalog entry missing either field will throw a NullReferenceException here rather than being filtered out.
| TFM | Notes |
|---|---|
net10.0 |
Full feature set. Uses ValueTask.CompletedTask, MediaTypeNames, ReadAsStreamAsync(token), C# 14 extension blocks. |
netstandard2.0 |
Compatible subset. #if !NETSTANDARD2_0 guards cover API surface differences. System.Text.Json pulled as a NuGet dependency. |
This library talks to the public, free-to-use API that the Open Source Initiative serves from https://opensource.org/api.
- Announcement: Introducing the new API for OSI Approved Licenses®
- API documentation and discussion: OSI Discuss thread
- License catalog: opensource.org/licenses
Issues and pull requests are welcome — see CONTRIBUTING.md and SUPPORT.md. Changes are listed in the GitHub releases.
Licensed under the MIT License — itself an OSI Approved License®.
Copyright © 2025 Oliver Schantz and Contributors.
This is an independent community project. It is not affiliated with, sponsored by, or endorsed by the Open Source Initiative. "Open Source Initiative" and "OSI Approved License" are trademarks of the Open Source Initiative; they are used here only to describe the API this library consumes.