# Counterparty Risk Rollup — Sanctions, Courts, Registry, Hiring (`zinin/counterparty-risk-rollup`) Actor

One call, one row per counterparty: sanctions screening (OFAC + EU), legal-entity registry (GLEIF), litigation history (US/UK/PL) and hiring signal (Greenhouse/Lever/Ashby) with a transparent risk score. Keyless, no login.

- **URL**: https://apify.com/zinin/counterparty-risk-rollup.md
- **Developed by:** [Tim Zinin](https://apify.com/zinin) (community)
- **Categories:** Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Counterparty Risk Rollup — Sanctions, Courts, Registry, Hiring

Screen a counterparty in one call: OFAC + EU sanctions match, GLEIF legal-entity
registry (LEI, status, jurisdiction), litigation history (US/UK/Poland) and a hiring
signal (Greenhouse/Lever/Ashby) — all in a single dataset row per company, with a
transparent, documented risk score. No login, no API keys, keyless official sources only.

### What you get

- One row per counterparty combining up to four independent checks — pick any subset
  (unless the run stops early, e.g. your spend cap is reached — see **Pricing** below).
- Sanctions screening against the live OFAC SDN (primary names **and** aliases) and EU
  Consolidated lists.
- Legal-entity identity via GLEIF: LEI, registration status, jurisdiction, registered
  address, with fuzzy-match protection so a subsidiary is never confused with its parent.
- Litigation history from US federal PACER dockets (party-name search), UK case law, or
  Poland's SAOS judgment index — one jurisdiction picked per run.
- Hiring signal from public Greenhouse/Lever/Ashby job boards — open-role count as a
  growth/expansion read.
- A fully documented risk score: every weight is spelled out below, not a black box.
- Partial results are labeled partial — `checksRun` / `checksFailed` say exactly what
  ran and what didn't, and why.
- Runs on Apify: schedule it, monitor it, call it from the API, export to JSON/CSV/Excel
  or push straight into your own pipeline.

### How to run it

1. Click **Try for free** — no card needed on the free plan.
2. Paste one or more company legal names (or an LEI) into **Companies**.
3. Pick which checks to run — **Sanctions** and **Registry** are on by default; add
   **Litigation** and **Hiring** if you want the full picture.
4. Hit **Start** and pull the results from the dataset (UI, API or webhook).

### Pricing

Pay-per-event: **$0.005 per GB of memory at run start + $0.05 per counterparty row** — the
actual numbers on this Actor's own Pricing tab, check there for what's in effect on any
given run. Apify charges `apify-actor-start` once per GB of allocated memory (minimum one),
and this Actor's default allocation is 4 GB — enough to hold the OFAC/EU sanctions lists in
memory alongside the GLEIF/litigation/hiring lookups — so **a default run starts at $0.020**.
Run it with less memory and the start costs proportionally less; the per-row price does not
change. You're billed once per company, the moment at least one
of its requested checks completes — not per check, and not per HTTP call. A company where
every requested check failed (source outage, etc.) is returned for free with `found:false`
and an `error` explaining why. 20 unique companies in one run = **$1.020** at most (see
**Input** below for what "unique" means here).

Every "free" claim above (error rows, the notice row below) holds only as long as this
Actor's platform pricing carries no price for the SDK's own automatic per-dataset-write
event — this Actor checks that itself at every run start (see below) rather than take it
on faith. If that promise were ever broken by a pricing misconfiguration, this Actor does
not fall back to billing the explanation as a "free" row either — it writes NOTHING to the
dataset at all (an explanatory row would itself be billed under that same broken pricing)
and the run ends as **FAILED**, with the reason in its status message and log instead of a
dataset row.

**If a run stops early for any other reason** — your run-level spend cap was reached, a
result failed to deliver mid-run, or (rare) the budget check itself failed and this Actor
stopped rather than guess — you will NOT get "one row per company" for the whole list: the
companies already delivered keep their rows, every company after that point gets none, and
one extra free explanatory row (`found:false`, `error` set) is appended so the dataset
always says why it's short, instead of silently ending with fewer rows than you asked for.
Raise your spend cap to get the full result set if that was the cause — the row's `error`
field tells you which of these it actually was.

### Input

| Field | Required | What it does |
|---|---|---|
| `companies` | yes | Company legal names (or an LEI), up to 20. One row comes out per *unique* name (exact-string duplicates in your list are billed and counted once, not once per repetition), unless the run stops early — see **Pricing**. |
| `checks` | no | Which of `sanctions`, `registry`, `litigation`, `hiring` to run. Default: `sanctions`, `registry`. |
| `jurisdictionHint` | no | 2-letter country code. Narrows the registry lookup and picks the litigation source: `GB`/`UK` → UK case law, `PL` → Poland SAOS, anything else → US CourtListener (default). |
| `maxConcurrency` | no | How many companies to process in parallel. Default 3. |

```json
{
  "companies": ["Siemens AG"],
  "checks": ["sanctions", "registry"]
}
```

### Output

Real row from an actual run of this Actor's logic against live OFAC/EU/GLEIF data:

```json
{
  "company": "Siemens AG",
  "found": true,
  "riskScore": 0,
  "riskLevel": "low",
  "sanctionsHit": false,
  "sanctionsMatches": [],
  "registry": {
    "found": true,
    "lei": "W38RGI023J3WT1HWRP32",
    "legalName": "Siemens Aktiengesellschaft",
    "entityStatus": "ACTIVE",
    "registrationStatus": "ISSUED",
    "jurisdiction": "DE",
    "registeredAddress": "Werner-von-Siemens-Str. 1, München, DE-BY, DE, 80333",
    "matchConfidence": 1,
    "matchBasis": "name",
    "candidates": []
  },
  "litigation": null,
  "hiring": null,
  "checksRun": ["sanctions", "registry"],
  "checksFailed": [],
  "summary": "Siemens AG — risk low (0). no sanctions match; LEI W38RGI023J3WT1HWRP32 (DE, entity ACTIVE/registration ISSUED).",
  "checkedAt": "2026-07-28T00:00:00.000Z"
}
```

| Field | Meaning |
|---|---|
| `found` | `true` once at least one requested check completed for this company. |
| `riskScore` / `riskLevel` | 0-100 score and `low`/`medium`/`high` band — see formula below. `null` if no risk-relevant check (sanctions/registry/litigation) ran. |
| `sanctionsHit` / `sanctionsMatches` | Whether the name matched OFAC SDN or EU Consolidated, and the evidence (list, matched name, alias type, program, score). |
| `registry` | `found`, `lei`, `legalName`, `entityStatus`, `registrationStatus`, `jurisdiction`, `registeredAddress`, plus match confidence/basis. `found:false` with `candidates` means GLEIF held more than one plausible entity and none was confident enough to call. |
| `litigation` | `jurisdiction` actually queried, `caseCount`, `partial`, up to 5 `topCases` (name/court/date/number/url), `sourceNote`. `caseCount:0` is a valid clean-history result. `partial:true` (UK only, single-page fetch) means more cases exist beyond what this leg fetched — `caseCount` is a lower bound there, not the full history; `sourceNote` says so too. |
| `hiring` | `provider`, `openRoles`, `boards` (with sample role titles). Absent (and listed in `checksFailed`) when no board is found on any of the three tracked ATS — see FAQ. |
| `checksRun` / `checksFailed` | Which checks completed, and which failed with a reason. A company can be `found:true` with some checks failed — that's a partial result, labeled as such. |

GLEIF tracks the registry entity's status in **two independent fields**, and this Actor
surfaces both rather than picking one: `entityStatus` (is the legal entity itself still
active, as opposed to dissolved/merged out of existence?) and `registrationStatus` (is its
LEI *registration* current — `ISSUED` — or has it lapsed/been retired/annulled/duplicated,
independent of whether the company itself still exists?). A company can be fully
operational (`entityStatus: ACTIVE`) while its LEI registration has quietly gone stale
(`registrationStatus: LAPSED`) — verified live against a real GLEIF record — which is
itself a mild transparency signal (see the risk formula below).

### How the risk score is built

Every weight is documented — nothing here is a black box:

| Signal | Weight | Condition |
|---|---|---|
| Sanctions match | **+60** | Any OFAC/EU name hit. On its own this already crosses the HIGH threshold below. |
| No confident registry match | +10 | The registry check ran, but GLEIF held no single confident LEI record for the name. |
| Registry entity inactive | +10 | An LEI was found, but the entity's `entityStatus` is not `ACTIVE` and/or its `registrationStatus` is not `ISSUED` (lapsed, retired, merged, annulled, duplicate). |
| Some litigation | +5 | 1-5 matched cases in the checked jurisdiction. |
| Heavy litigation | +15 | More than 5 matched cases (replaces the +5 above, not additive on top of it). |

`riskScore` is the sum, capped at 100. `riskLevel` is `high` at 60+, `medium` at 20-59,
`low` below that. Hiring never affects the score — it's a growth signal, not a risk one.
If you only select `hiring`, `riskScore`/`riskLevel` come back `null`, because nothing
risk-relevant ran — a company is never labeled "low risk" on the strength of a check that
never touched sanctions, registry or litigation.

### Other tools we built

Need one piece of this in more depth, or a different angle on the same counterparty?

| Actor | What it does |
|---|---|
| [sanctions-screening](https://apify.com/zinin/sanctions-screening) | The full OFAC + EU screen on its own — every match, every alias, no row limit. |
| [company-registry-enricher](https://apify.com/zinin/company-registry-enricher) | Deeper registry lookups — LEI, UK Companies House directors and SIC codes. |
| [litigation-check](https://apify.com/zinin/litigation-check) | Full litigation history across all three jurisdictions at once, more cases per row. |
| [company-hiring-radar](https://apify.com/zinin/company-hiring-radar) | The full hiring picture — every open role, function breakdown, remote share, what's new. |
| [new-company-detector](https://apify.com/zinin/new-company-detector) | Freshly incorporated entities worth screening before you ever hear the name from a client. |

### FAQ / Limitations

**Is this a compliance sign-off?** No. It's a screening signal to prioritize manual
review, the same way our standalone `sanctions-screening` and `litigation-check` actors
are. A clean row is not a legal clearance; a hit needs human verification against the
official source before you act on it.

**Why did the hiring check fail for a company I know is hiring?** The hiring leg guesses
an ATS token from the company name (e.g. "Acme Robotics, Inc." → `acmerobotics`) — it
doesn't look the company up anywhere first. Most companies, especially large or
enterprise ones, don't run Greenhouse, Lever or Ashby at all, so a miss is reported as a
failed check (`checksFailed`), never as a finding about the company. A short or common
company name can also collide with an unrelated company that happens to use the same
token — before treating a hit as a signal, check `hiring.provider` + `hiring.token`
against that provider's own public board (e.g. `boards.greenhouse.io/<token>`) and eyeball
`hiring.boards[].sampleTitles` for roles that plausibly belong to this company.

**What this is NOT.** Not a KYC/AML system of record, not a substitute for a licensed
sanctions-screening vendor for regulated onboarding, and not a legal opinion on
litigation exposure. Registry ambiguity (`registry.found:false` with `candidates`) means
GLEIF holds more than one plausible match — resolve it by hand with the full legal name,
a country hint, or the LEI itself.

**A source outage is never sold as a clean result.** If OFAC/GLEIF/CourtListener/the ATS
boards are unreachable, or answer with something that doesn't match their normal response
shape, that check is reported as **failed** (`checksFailed`, free) — never as a quiet
"nothing found". The sanctions check specifically requires the OFAC SDN list, its ALT.CSV
aliases, and the EU Consolidated list to ALL load and pass a basic plausibility check; any
gap fails the whole sanctions check for that run rather than screening on an incomplete
list and calling it clean.

**Same base name, different legal form ≠ the same company.** "Acme AG" and "Acme GmbH" can
be two unrelated entities (a parent and an unrelated subsidiary/shell trading under the
same name) — this Actor treats a legal-form disagreement as a reason to stay `ambiguous`
rather than confidently pick one, even when the base name matches exactly.

**One jurisdiction at a time for litigation.** The litigation check queries exactly one
jurisdiction per run (US by default; `jurisdictionHint` picks UK or Poland). Need all
three for one company? Use `litigation-check` directly.

**UK litigation fetches exactly one page.** US and Poland return an authoritative total
count straight from their own APIs (`count`/`totalResults`), no matter how many actual
cases exist. The UK leg (single keyless HTTP call, by design) does not follow pagination —
when the feed itself says more results exist beyond this page, `litigation.partial` comes
back `true` and `caseCount` is a lower bound, not the company's full UK case count.

Found a gap or need this tuned to your own case — issues on this Actor's page.

### Other tools we built

#### Need the rest of the picture?

These run on the same account, take the same shape of input and bill the same way, so they slot into an existing pipeline without new plumbing.

| Actor | What it does |
|---|---|
| [AI Answer Change Alert](https://apify.com/zinin/ai-answer-change-alert) | Track whether an AI assistant's answer to a query you care about changed since last time — new sources… |
| [AI Crawler Access Checker](https://apify.com/zinin/ai-crawler-access-checker) | Check which AI crawlers (GPTBot, ClaudeBot, PerplexityBot, Google-Extended & more) can access your website |
| [AI Overview Citation Tracker](https://apify.com/zinin/ai-overview-tracker) | For each query that matters, see which sources and domains AI assistants cite in their answer — grounded… |
| [B2B Lead Enricher](https://apify.com/zinin/b2b-lead-enricher) | Turn a list of company websites into sales-qualified lead cards: detected tech stack, a rough revenue… |
| [Chotot Vietnam Listings Scraper](https://apify.com/zinin/chotot-listings) | Pull live classified ad listings (real estate, vehicles, electronics, jobs and more) straight from… |

# Actor input Schema

## `companies` (type: `array`):

Company legal names to assess, one per row (a Legal Entity Identifier is also accepted for the registry check). One dataset row comes out per company, combining every check you selected below.

## `checks` (type: `array`):

Which of the four checks to run per company. Sanctions and registry are fast (shared cached list + one API call); litigation and hiring each add one more API call per company.

## `jurisdictionHint` (type: `string`):

Optional ISO country code, e.g. "GB", "DE", "PL". Narrows the registry lookup to that country and picks which litigation source is queried (GB/UK -> UK case law, PL -> Poland SAOS, anything else -> US CourtListener, the default). "UK" is accepted as a synonym for "GB" everywhere — it is normalized to the ISO code "GB" before it reaches the registry (GLEIF) lookup, since GLEIF itself only recognizes "GB".

## `maxConcurrency` (type: `integer`):

How many companies to assess in parallel.

## Actor input object example

```json
{
  "companies": [
    "Siemens AG"
  ],
  "checks": [
    "sanctions",
    "registry"
  ],
  "jurisdictionHint": "GB",
  "maxConcurrency": 3
}
```

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "companies": [
        "Siemens AG"
    ],
    "checks": [
        "sanctions",
        "registry"
    ],
    "jurisdictionHint": "GB"
};

// Run the Actor and wait for it to finish
const run = await client.actor("zinin/counterparty-risk-rollup").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "companies": ["Siemens AG"],
    "checks": [
        "sanctions",
        "registry",
    ],
    "jurisdictionHint": "GB",
}

# Run the Actor and wait for it to finish
run = client.actor("zinin/counterparty-risk-rollup").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "companies": [
    "Siemens AG"
  ],
  "checks": [
    "sanctions",
    "registry"
  ],
  "jurisdictionHint": "GB"
}' |
apify call zinin/counterparty-risk-rollup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=zinin/counterparty-risk-rollup",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/cI2GI8G4wjegtAOtd/builds/lvh1DI2jS19sab8H9/openapi.json
