Sanctions.Net
0.1.1
dotnet add package Sanctions.Net --version 0.1.1
NuGet\Install-Package Sanctions.Net -Version 0.1.1
<PackageReference Include="Sanctions.Net" Version="0.1.1" />
<PackageVersion Include="Sanctions.Net" Version="0.1.1" />
<PackageReference Include="Sanctions.Net" />
paket add Sanctions.Net --version 0.1.1
#r "nuget: Sanctions.Net, 0.1.1"
#:package Sanctions.Net@0.1.1
#addin nuget:?package=Sanctions.Net&version=0.1.1
#tool nuget:?package=Sanctions.Net&version=0.1.1
Sanctions.NET
Sanctions name-screening for .NET against the OFAC, UN and EU consolidated lists. Zero dependencies, offline-capable, built for fintech compliance teams.
There is no good open-source sanctions screening library in .NET. Compliance engineers either pay for a vendor platform or hand-roll CSV downloads and string comparisons inside their payment services. Sanctions.NET packages the boring, correctness-critical parts: tolerant feed parsers, a tested normalization pipeline and composable fuzzy matchers, behind a four-line API.
Quickstart
using Sanctions.Net;
var screener = await SanctionsScreener.CreateAsync(); // downloads the lists
ScreeningResult result = screener.Screen("Ayman al-Zawahiri");
if (result.IsMatch)
{
foreach (var match in result.Matches) // ordered by score, best first
{
Console.WriteLine($"{match.Score:F1} {match.ListSource} {match.ListEntryId} {match.MatchedName}");
}
}
Each ScreeningMatch carries ListSource, MatchedName, Score (0-100), EntityType, ListEntryId and Aliases.
List sources
| List | Format | Default URL | Update cadence |
|---|---|---|---|
| OFAC SDN | CSV | https://sanctionslistservice.ofac.treas.gov/api/PublicationPreview/exports/SDN.CSV |
As designations are made, often several times a week |
| OFAC SDN alternate names | CSV | https://sanctionslistservice.ofac.treas.gov/api/PublicationPreview/exports/ALT.CSV |
Published alongside SDN.CSV |
| OFAC Consolidated (non-SDN) | CSV | https://sanctionslistservice.ofac.treas.gov/api/PublicationPreview/exports/CONS_PRIM.CSV |
Same cadence as SDN actions |
| OFAC Consolidated alternate names | CSV | https://sanctionslistservice.ofac.treas.gov/api/PublicationPreview/exports/CONS_ALT.CSV |
Published alongside CONS_PRIM.CSV |
| UN Security Council Consolidated | XML | https://scsanctions.un.org/resources/xml/en/consolidated.xml |
When sanctions committees list or delist |
| EU Consolidated Financial Sanctions | XML | https://webgate.ec.europa.eu/fsd/fsf/public/files/xmlFullSanctionsList_1_1/content?token=... |
Regenerated whenever EU restrictive measures change |
OFAC URLs point at the official Sanctions List Service (the retired treasury.gov/ofac/downloads paths only work through a courtesy redirect). The OFAC providers download both the primary file and the alternate names (ALT) file and join the alternate names onto each entity by entity number, so aliases come from the canonical ALT feed, not just the a.k.a. fragments in the remarks column.
The UN URL currently serves the legacy consolidated schema (the feed redirects to consolidatedLegacyByPRN.xml); the parser targets that schema. The EU default URL uses the DG FISMA public shared token, verified working against the live feed. If your organisation is registered with DG FISMA, plug in your own tokenised URL:
var options = new ScreeningOptions
{
EuConsolidatedUrl = "https://webgate.ec.europa.eu/fsd/fsf/public/files/xmlFullSanctionsList_1_1/content?token=YOUR-TOKEN",
};
Every feed URL is overridable the same way (OfacSdnUrl, OfacSdnAltUrl, OfacConsolidatedUrl, OfacConsolidatedAltUrl, UnConsolidatedUrl), which also makes internal mirrors easy.
Parsers are tolerant at row level: malformed rows are skipped, never thrown on, and the counts are surfaced per list through screener.ParseReports. Failures fail loudly instead of silently screening nothing: a feed that cannot be downloaded, is larger than the 512 MB safety cap, or parses to zero entities throws a typed exception (SanctionsListDownloadException, EmptySanctionsListException) at construction or refresh time. Exception messages never include URL query strings, so EU tokens stay out of logs and error trackers.
Matchers
Scoring is the best result across the enabled matchers, per name variant (primary name and every alias).
| Matcher | Catches | Score |
|---|---|---|
ExactNormalizedMatcher |
Casing, diacritics, punctuation and spacing differences (NÚÑEZ, José vs Nunez Jose) |
100 or 0 |
TokenSortMatcher |
Reordered names (AL-ZAWAHIRI AYMAN vs Ayman al-Zawahiri) |
100 or 0 |
JaroWinklerMatcher |
Misspellings and transliteration drift (Zawahiry vs Zawahiri), also on the token-sorted form |
0-100 |
All three run by default. Compose your own set:
using Sanctions.Net;
using Sanctions.Net.Matching;
var options = new ScreeningOptions
{
Matchers = [new ExactNormalizedMatcher(), new TokenSortMatcher()],
Threshold = 90,
};
Normalization (shared by every matcher) lowercases, folds diacritics and combining marks, removes apostrophes, converts remaining punctuation to spaces and collapses whitespace. Invisible and format characters (zero-width spaces, joiners, the BOM, soft hyphen, word joiner, bidirectional controls and blank-rendering fillers such as the Hangul fillers) are stripped rather than treated as separators, so an evader cannot split a name with hidden characters to slip past exact or token-sort matching. Non-Latin scripts (Arabic, Cyrillic, CJK) pass through and match exactly against original-script aliases carried on the lists.
Known limitation: only Basic Multilingual Plane invisible characters are stripped. Astral-plane (supplementary) invisible code points such as the Unicode tag characters (U+E0000..U+E007F) and the variation-selector supplement (U+E0100..U+E01EF) are not yet handled; codepoint-aware normalization is planned for 0.2.
Offline / air-gapped mode
Screen from files you distribute yourself, with no network access:
var screener = await SanctionsScreener.CreateFromFilesAsync(new Dictionary<ListSource, string>
{
[ListSource.OfacSdn] = "/data/sdn.csv",
[ListSource.UnConsolidated] = "/data/un-consolidated.xml",
[ListSource.EuConsolidated] = "/data/eu-consolidated.xml",
});
Offline mode takes one file per list, so OFAC aliases come from the remarks column only; the ALT join currently applies to downloaded mode. If you need full alias coverage offline, mirror the feeds internally and point the URL overrides at your mirror.
await screener.UpdateAsync() refreshes from the original source in both modes (re-downloads, or re-reads the files) and swaps the index atomically, so in-flight Screen calls are never disrupted.
HttpClient is injected via ScreeningOptions.HttpClient, so IHttpClientFactory-managed clients work naturally. Lists are downloaded once at startup and held in memory; screening itself never touches the network.
Threshold guidance
The default threshold is 85 (ScreeningDefaults.DefaultThreshold).
- 85 is a sensible onboarding/KYC default: it catches one-character misspellings and transliteration variants of multi-token names.
- 90-95 suits high-volume payment screening where analysts review every hit and false positives are costly.
- 100 means exact-after-normalization only (including reordering via token sort). Use it only when you accept missing misspellings.
- Short queries deserve care: Jaro-Winkler rewards shared prefixes, so a single-token query can score in the high 80s against short list names. If you screen single tokens, consider a higher threshold or an exact/token-sort-only matcher set.
- Query length is not capped by the library. If you screen user-supplied input in a server context, bound the input length at your API boundary before calling
Screen.
Screen both directions of doubt: a false negative is a regulatory breach, a false positive is an analyst review. Tune toward the cheaper failure.
Disclaimer
Sanctions.NET is a screening aid, not legal or compliance advice. Matching a name is not a sanctions determination, and not matching is not clearance. Screening programs need qualified compliance oversight, list-update monitoring and documented review procedures.
Roadmap
- Fuzzy date-of-birth matching to cut false positives on common names
- PEP (politically exposed persons) feed support
- Delta updates with ETag / If-Modified-Since instead of full re-downloads
- Phonetic matchers (Soundex/Metaphone) and candidate indexing/blocking for large-scale throughput
- Codepoint-aware Jaro-Winkler and full-width (FormKD) folding for CJK inputs
- ALT-file support in offline mode and
IAsyncDisposable
License
MIT, copyright Israel Iyonsi.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.