Sanctions

Match

POST /sanctions/match screens a person or company against sanctions and PEP data and returns a score per candidate. Request, response, batch of 100, and errors.

POST /sanctions/match

match takes one subject — a person or a company with everything you know about it — and returns the records that could be the same subject, each with a score from 0 to 1 and a match flag.

This is the endpoint for automatic screening. Use search when a person reads a list.

Full field list: matchSanctions in the reference.

The request

Prop

Type

Company, Organization and LegalEntity read the same group of records, so a company found as an Organization is returned for all three.

An array in properties holds up to 20 values, and each value up to 300 characters.

The properties that count

Everything you send is kept, but only these keys change the score:

KeyExampleEffect on the score
name["Vladimir Puttin"]The base of the score. Send every spelling you have.
birthDate["1952-10-07"]Same date, same year or a conflict.
citizenship, country["ru"]An overlap or a conflict. Use two-letter codes.
gender["male"]A conflict costs a little.
idNumber, taxNumber, innCode, registrationNumber, leiCode, swiftBic, imoNumber["7323006644"]An equal identifier makes the result a match. A conflicting one of the same kind pushes it down.

Read Reading a score for the exact numbers.

Send every field you have

A name alone gives you long lists to review. A name with a birth date and a country gives a clean decision. An identifier gives near certainty. The fields you leave out are never held against the candidate: an absent field is not compared.

A worked example

The subject has a typo in the surname, and a birth date and a citizenship.

curl -s -X POST "https://api.linkinlegal.com/v1/sanctions/match" \
  -H "Authorization: Bearer $LINKINLEGAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "schema": "Person",
    "properties": {
      "name": ["Vladimir Puttin"],
      "birthDate": ["1952-10-07"],
      "citizenship": ["ru"]
    },
    "threshold": 0.8,
    "limit": 2
  }'
{
  "results": [
    {
      "id": "Q7747",
      "caption": "Vladimir Putin",
      "schema": "Person",
      "score": 1,
      "match": true,
      "features": {
        "name": 0.9806,
        "birthDate": 0.1,
        "country": 0.05,
        "identifier": null,
        "gender": null
      },
      "datasets": ["eu_fsf", "us_ofac_sdn", "gb_fcdo_sanctions"],
      "target": true,
      "properties": {
        "name": ["Vladimir Vladimirovich Putin"],
        "birthDate": ["1952-10-07"],
        "country": ["ru"]
      }
    },
    {
      "id": "usgsa-941a678fce498dfde69459519fccb211a970b95a",
      "caption": "Vladimir Vladimirovich PUTIN",
      "schema": "Person",
      "score": 0.9652,
      "match": true,
      "features": {
        "name": 0.9152,
        "birthDate": null,
        "country": 0.05,
        "identifier": null,
        "gender": null
      },
      "datasets": ["us_sam_exclusions"],
      "target": true,
      "properties": {
        "name": ["Vladimir Vladimirovich PUTIN"],
        "country": ["ru"]
      }
    }
  ]
}

The typo cost almost nothing: the name alone scored 0.9806. The same birth date added 0.10 and the same country added 0.05, so the first result is at the maximum of 1. The second record carries no birth date, so features.birthDate is null and nothing was added or taken away.

The response

FieldTypeWhat it is
idstringThe id of the candidate record.
captionstringThe display name of the candidate.
schemastringThe kind of candidate.
scorenumber0 to 1, four decimals.
matchbooleantrue when score is at or above your threshold.
featuresobjectThe parts the score is built from: name, birthDate, country, identifier, gender. A null means the field was not compared.
datasetsarrayThe source lists of the candidate.
targetbooleanAlways true: only listed entities are candidates.
propertiesobjectThe properties of the candidate, so you can show the evidence to a reviewer.

results is sorted by score, highest first, and it is cut at limit. It also holds candidates below your threshold, with match: false. That is deliberate: a reviewer wants to see the near misses. Read match, not the length of results.

An empty results array means nothing came near the subject.

Screen many subjects in one call

POST /sanctions/match/batch

Send 1 to 100 subjects under queries. You choose the key of each item; it comes back on the answer.

curl -s -X POST "https://api.linkinlegal.com/v1/sanctions/match/batch" \
  -H "Authorization: Bearer $LINKINLEGAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "queries": {
      "cust-1": {
        "schema": "Person",
        "properties": { "name": ["Marina Gulina"] },
        "limit": 1
      },
      "cust-2": {
        "schema": "Company",
        "properties": {
          "name": ["Wrong Name LLC"],
          "innCode": ["7323006644"]
        },
        "limit": 1
      }
    }
  }'
{
  "responses": {
    "cust-1": {
      "results": [
        {
          "id": "ru-inn-381205591080",
          "caption": "МАРИНА КОНСТАНТИНОВНА ГУЛИНА",
          "schema": "Person",
          "score": 0.9333,
          "match": true,
          "features": {
            "name": 0.9333,
            "birthDate": null,
            "country": null,
            "identifier": null,
            "gender": null
          },
          "datasets": ["ann_graph_topics", "ext_ru_egrul"],
          "target": true,
          "properties": { "name": ["МАРИНА КОНСТАНТИНОВНА ГУЛИНА"] }
        }
      ]
    },
    "cust-2": {
      "results": [
        {
          "id": "ru-inn-7323006644",
          "caption": "АКЦИОНЕРНОЕ ОБЩЕСТВО \"СПЕКТР-АВИА\"",
          "schema": "Company",
          "score": 0.95,
          "match": true,
          "features": {
            "name": 0,
            "birthDate": null,
            "country": null,
            "identifier": 1,
            "gender": null
          },
          "datasets": ["ann_graph_topics", "ext_ru_egrul"],
          "target": true,
          "properties": {
            "name": ["АКЦИОНЕРНОЕ ОБЩЕСТВО \"СПЕКТР-АВИА\""],
            "innCode": ["7323006644"]
          }
        }
      ]
    }
  }
}

Two things to know:

  • Every item follows the rules of POST /sanctions/match, and every item can carry its own threshold, limit, include and datasets.
  • A batch call costs one request against your rate limit, whether it holds one subject or 100.

A bad item does not fail the batch

The call stays 200. The bad item gets an error object in place of its results:

{
  "responses": {
    "cust-1": { "results": [] },
    "cust-3": {
      "error": {
        "code": "invalid_request",
        "message": "Invalid request fields: properties."
      }
    }
  }
}

The item code is invalid_request (the item was wrong) or internal_error (the item failed on our side; send it again). Walk every key of responses and check for error first.

Errors

StatusCodeCauseWhat to do
400invalid_requestschema is not one of the six values, properties.name is empty, threshold is out of 0–1, limit is out of 1–25, or a batch holds 0 or more than 100 items.Fix the body.
400unknown_parameterA field name that does not exist in the body.Check it against the reference.
503index_unavailableThe match index is not ready.Retry in a few minutes.

When match is not available

match needs its name index. While that index is rebuilt or not yet ready, the endpoint answers 503 index_unavailable and the message asks you to try again in a few minutes.

For a batch, this is the one error that fails the whole call, and not each of the 100 items. Retry the whole batch.

search and the entity endpoints do not use this index and keep working.

Practical advice

  • One subject per call, or a batch. Do not loop 100 single calls: one batch is one request against your limit, and it is faster.
  • Use one stable key per subject in a batch (your customer id). Then you can write the answers straight back to your records.
  • Send every spelling of a name you hold, in properties.name. The best one counts; extra names never lower the score.
  • Store the id and the score of every hit with your decision. When you rescreen, you can then show what changed.
  • Keep the default threshold of 0.8 until you have measured on your own data. Read Thresholds.

Last updated on

On this page