How OFACScreen Matches Names
If you are buying a screening tool you are buying its matching. Everything else is plumbing around it. This page describes what OFACScreen actually does when you type a name, including the parts that do not flatter us, because a vendor who will not name their method is asking you to take the most important part of the product on faith.
Everything here is testable in the free search, which costs nothing and needs no signup. If a claim on this page is wrong, the search box will show you.
The Short Version
- Every name, yours and every one on the lists, is romanized to plain ASCII letters and digits and lowercased. That normalized form is what gets compared.
- Comparison is Postgres trigram similarity: both strings are cut into overlapping three-character runs and scored on how many runs they share.
- Both columns are GIN indexed with
gin_trgm_ops, and the filter uses the trigram operator that index can answer, so a search reads candidates rather than all 70,000 entries. - Aliases are searched alongside primary names. An entry that matches only through an alias is scored on its best-scoring alias, and the result tells you which alias caught it.
- An exact match on the normalized name or on any alias scores 100%. A query whose words all appear in a candidate's words scores at least 85%.
- The default cut-off is 0.3. Batch runs and daily monitoring use 0.4. The REST API takes a
thresholdbetween 0.0 and 1.0 on each request. - There is no phonetic pass. No Soundex, no Metaphone, no NYSIIS.
Step One: Both Sides Are Romanized
Sanctions lists are published in a mixture of scripts. The EU Financial Sanctions File carries entries whose primary name is written in Cyrillic, Arabic, Hangul or Chinese characters, and the UN, Canadian and Swiss lists carry non-Latin spellings as aliases alongside their Latin ones. A screening tool that simply dropped the characters it could not read would reduce an Arabic query to whitespace and report the target clear, which is the worst answer a sanctions tool can give.
So every name is romanized before it is compared, on both sides. Punctuation is removed, diacritics are stripped, everything is lowercased, and runs of whitespace are collapsed. "PUTIN, Vladimir Vladimirovich" and "Владимир Путин" end up in the same alphabet, which is the only way a comparison between them can mean anything.
What romanization does not do is reproduce the exact spelling a given list chose. Transliteration schemes disagree with each other, and the trigram scoring downstream is what absorbs the residual difference. Here is how the scripts actually behave, which is worth knowing before you rely on any of them:
- Cyrillic and Greek. These romanize close to the spellings the lists use. Type "Владимир Путин" and you get the same answer as "Vladimir Putin".
- Hangul. Handled specially, see below.
- Kana. Hiragana and katakana are romanized in Hepburn. "シノダ ケンイチ" becomes "shinoda kenichi" and matches the SDN entry spelled that way. One caveat worth knowing: a run of kana becomes a single word, so a name written as one unbroken run scores lower than the same name written with a space or a separator between its parts.
- Arabic. Arabic script does not write short vowels, so romanizing it produces a consonant skeleton: "أيمن الظواهري" becomes "ymn ldhwhry". Both sides go through the same transformation, so this matches the Arabic-script names and aliases the lists publish, which the EU, UN, Canadian and Swiss lists carry in quantity. What it does not do is bridge to a Latin-only entry: a consonant skeleton scores very poorly against a fully vowelled Latin transliteration. In practice an Arabic-script query surfaces the right entry through the Arabic alias the list carries, and the entry is then displayed under its Latin name.
- Han characters. Romanized as Chinese. See the gap section below, because this one has a sharp edge.
Korean Gets Two Readings, Deliberately
Korean is the one script where a generic romanizer produces a dangerous answer, so it is worth explaining why we treat it differently.
Revised Romanization is the modern South Korean standard, and it is what an off-the-shelf library produces. It is not what the sanctions lists use. OFAC, the UN, the EU and the UK all write "KIM Jong Un", "RI Pyong Chol", "PAK Tong Sok". Revised Romanization writes those same three names "Gim Jeong Eun", "Ri Byeong Cheol" and "Bak Dong Seok". None of those spellings appears on any list we carry. A Hangul query romanized the modern way scores about 0.24 against the list spelling, which is below every threshold this product offers: a DPRK name would come back Clear.
So Hangul is romanized twice. The first reading follows the convention the lists themselves use, and that is the one used for ranking. The second is Revised Romanization, kept as an additional probe rather than discarded, because the lists are not consistent with themselves either: across the corpus the Korea programs carry names spelled both "Kim" and "Gim", both "Jong" and "Jeong", both "Pak" and "Bak". The minority spellings are real designated people.
You can check this in the free search. Type 김정은 and the top result is the OFAC SDN entry "KIM, Jong Un" at 100%.
Step Two: Trigram Similarity
Once both names are in the same alphabet, they are compared with Postgres pg_trgm. A trigram is a run of three consecutive characters. "putin" contains the trigrams that overlap across it, and similarity is the proportion of trigrams the two strings share. It is a character-level measure, which is exactly right for the failure mode sanctions screening actually has: transliteration drift and spelling variation, where the letters are mostly the same and mostly in the same order.
Two details matter for anyone evaluating this seriously:
- It is indexed. Both
name_normalizedcolumns, on entries and on aliases, carry a GIN index built withgin_trgm_ops, and the row filter is written with the trigram operator that index can serve. A plainsimilarity(a, b) >= xcomparison cannot use an index and forces a scan of every row, which is what makes some tools slow enough that people quietly stop screening. - The cut-off is a session setting, held per search. The trigram operator reads its cut-off from a Postgres configuration parameter rather than from the query text, so each search sets it inside its own transaction and it is released when the transaction ends. Different callers genuinely want different cut-offs, and one caller's setting must not leak onto the next request sharing that connection.
Aliases Are Searched Too
Sanctions lists carry alternate spellings, birth names, transliteration variants and business names as aliases, and a great many real matches arrive through them rather than through the primary name. So aliases are searched as their own indexed set, and an entry surfaces if either its primary name or any of its aliases clears the cut-off.
An entry appears once, no matter how many of its names matched. If it matched on its primary name, that is the score you see. If it matched only through aliases, it is scored on its best-scoring alias, and the result shows you which alias caught it, so you can see why the entry is in front of you.
The Exact-Match Boost
Trigram similarity alone under-scores some obviously correct answers, so two adjustments sit on top of the raw score:
- If your normalized query is identical to the entry's normalized name or to any of its normalized aliases, the score is 100%. For Korean, either reading counts, so a Hangul query that matches a list's minority spelling is reported as the confirmed match it is rather than hidden behind a medium-confidence one.
- If every word of your query, counting only words of three characters or more, appears among the candidate's words, the score is at least 85%. This is what puts "Vladimir Putin" at the top when the list spells the entry "PUTIN, Vladimir Vladimirovich".
Nothing else moves a score. There is no hand-curated list of famous names, no boosting by list, and no re-ranking by anything other than the score you are shown.
Thresholds: the Real Numbers
The score you see is the score used. Results are shown to you sorted by it, with a match level attached: high at 80% and above, medium from 50%, low below that.
- 0.3 is the default cut-off, used by the free search on /screen/, by the dashboard search, and by the MCP tool. It is deliberately loose. A screening tool that quietly hides weak matches is making your review decision for you, and the whole point of the score column is that you make it.
- 0.4 is used by batch CSV runs and by daily monitoring, both of which then treat 0.5 and above as a match worth flagging. These are unattended processes producing a queue for a human, so they run slightly tighter than an interactive search.
- Any value from 0.0 to 1.0 can be set per request on the REST API with the
thresholdparameter. That is the only place the cut-off is under your control; there is no threshold slider in the web interface, and this page would be lying if it implied otherwise.
A search needs at least two characters, and returns at most 500 matches. There is no industry-standard threshold number, and any vendor quoting one is guessing. Whatever cut-off you settle on, write down what it was and why, because an examiner will ask how sensitive your search was.
What We Do Not Do
This is the part most vendor pages leave out.
- We do not run a phonetic pass. No Soundex, no Metaphone, no NYSIIS, no double metaphone. Phonetic algorithms catch sound-alike names that share few letters, and they cost you a large volume of false positives on short names. We chose character-level similarity plus romanization instead, and the honest framing is that this is a trade-off rather than a strict improvement. If your risk profile is dominated by sound-alike English names, that is a real consideration.
- Korean written in hanja is a known gap. Han characters are romanized as Chinese, because that is right the overwhelming majority of the time. It is wrong for Korean names written in Chinese characters:
金正恩romanizes as "jin zheng en", not "kim jong un", and does not match the SDN entry. If you hold Korean names in hanja, convert them to Hangul or to the Latin spelling before screening. We would rather you knew this than discovered it. - We do not screen on-chain wallet addresses. This is name screening. OFAC does designate wallet addresses on the SDN List, and an address-level blockchain analytics tool is the right instrument for that.
- We do not match on date of birth, passport number or nationality. Those fields are shown on a match, from the source list, so you can rule a hit in or out. They are not part of the search itself.
- A score is not an identification. A 100% score means the strings are identical after romanization, and nothing more. Two different people share a name every day. Compare the date of birth, the locations and the ID numbers on the match before you act on it. See How to Handle an OFAC Match.
Test It Yourself
None of this requires our word for it, an account, or a card. Open the free search and try:
김정은, Hangul, against a list that spells it "KIM, Jong Un".Владимир Путин, Cyrillic, against Latin-script entries on four lists.シノダ ケンイチ, katakana, against "SHINODA, Kenichi".Ayman al-Zawahiriand thenAyman Zawahiri, to see hyphenation and a dropped particle handled.金正恩, hanja, to see the gap described above with your own eyes.
If you want the result of one of those searches as a document, the sample report shows what that looks like, and any search can be turned into one for $9.99 without an account.
Related Reading
- How Fuzzy Matching Works in Sanctions Screening, the general concepts rather than our implementation.
- Choosing OFAC Screening Software, including what to ask a vendor about their matching.
- What an Examiner Asks For, and Which of It We Give You.
Start Screening Against OFAC Today
14-day free trial. No credit card required. Screen against OFAC SDN, Non-SDN, BIS, and more.
Start Free Trial