# OpenSanctions: a sanctions and PEP dataset you have to run yourself

> OpenSanctions aggregates sanctions lists, politically exposed persons and related entities into one database, and the code that builds it, zavod, ships as a Docker image you run locally. The data pipeline is open, but the licences are split, so the data itself is not the free part.

**opensanctions/opensanctions** — An open database of international sanctions data, persons of interest and politically exposed persons

- Repository: https://github.com/opensanctions/opensanctions
- Website: https://www.opensanctions.org
- Stars: 851 · Forks: 196
- Language: Python
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/opensanctions-opensanctions

## OpenSanctions pulls scattered sanctions lists into one dataset

The project describes itself as an open source database of sanctions data, politically exposed persons and related entities, published through the website at opensanctions.org with a datasets index, a FAQ and a licensing page. The subject matter is due diligence, and the repository topics name the same set of audiences: due-diligence, investigations, journalism and money-laundering work, plus a scraper topic that hints at where the data comes from.

The problem being solved is not a lack of sanctions lists. Public lists from different authorities and programmes already exist. The problem is that they arrive in incompatible formats, cover overlapping people under different spellings, and mix entities with sanctions entries. OpenSanctions crawls those sources, puts everything into one schema, and offers the result as a dataset.

That framing has a consequence for adoption. This is a data engineering project that happens to publish a database. Adopting the data means adopting the pipeline, because the output is a product of the crawlers rather than a checked-in file you can simply download.

## Parsing, cleaning, deduplication and export are four separate jobs

The README lists four stages that define the codebase: parsing of raw source data, cleaning and standardization of data structures, deduplication to maintain data integrity, and exporting into a variety of output formats. That list is the architecture. Crawlers produce records, the records are normalized, the same person appearing in several lists is collapsed, and the merged set is written out in whatever form a consumer needs.

The schema underneath is Follow the Money, described in the README as a JSON focused anti-corruption data model, and it is used as the schema for all crawlers. Because every crawler targets the same model, sources that describe the same kind of entity end up structurally comparable, which is what makes deduplication across source lists possible at all. FtM data is then optionally exposed to simplified formats like CSV, so a consumer who wants a spreadsheet gets one without the pipeline having a second data model.

The ordering matters. Deduplication sits after cleaning, so the merge operates on standardized records rather than on raw text, and it happens before export, so every output format inherits the same merges. A consumer who re-does matching on raw source text instead ends up with a different answer than the one the dataset gives.

## zavod holds the crawlers and Docker is the documented easy path

The Python package that does the work lives in the zavod/ directory, and the documentation for it is hosted separately at zavod.opensanctions.org. The README is explicit that the easiest way to run zavod is Docker, and that changing the code means setting up a development environment in a local Python virtual environment, with both routes pointed at the install documentation.

The Dockerfile shows how the two pieces fit. It builds on ubuntu:26.04, creates a virtual environment at /venv, puts that environment on PATH, copies the zavod directory to /opensanctions/zavod and installs it in editable mode, then sets the working directory to /opensanctions. The runtime stage adds postgresql-client, poppler-utils and libleveldb1d, which tells you the pipeline expects to talk to a PostgreSQL database and to work with PDF text and LevelDB stores.

The directory layout supports the same reading. meta/ holds dataset definitions, datasets/ holds output, analysis/ holds analysis code, contrib/ holds supporting files such as the openssl.cnf that the image points at, and plans/ holds planning documents.

## Running the pipeline with docker compose and the Makefile

The compose file defines a single service named app, built from the local Dockerfile, with the published image as its tag, a working hostname and a tmpfs mounted at /tmp. Two bind mounts matter: one keeps your data on the host and the other keeps your working copy of the code live inside the container, so edits take effect without rebuilding the image.

```yaml
services:
  app:
    build: .
    image: ghcr.io/opensanctions/opensanctions:latest
    hostname: work
    volumes:
      - "./data:/opensanctions/data"
      - "./opensanctions:/opensanctions/opensanctions"
    tmpfs:
      - "/tmp"
```

The Makefile wraps the common operations. Its workdir target creates the data directory that the first mount expects, and its run target builds the image and executes the opensanctions run command inside a throwaway container. The targets are copied here as they appear in the file, so you can run them by hand.

```bash
workdir:
	mkdir -p data/
```

Because the compose command is detected at run time, the Makefile uses docker-compose when that binary exists and falls back to docker compose, so the same targets work on either setup. The clean target removes data/datasets along with build output and the mypy and pytest caches, which is the destructive step to be careful with before a long crawl has been harvested.

## Deduplication depends on a human review step

Not every crawler output is treated as finished. Crawlers that use Data Reviews require human review of extracted data, and the repository points to ui/README.md for how to run the review UI locally. The UI directory sits at the top level alongside zavod/, and there is also an init-test-db.sql file and a docker-compose.dev.yml, so the review interface has its own development setup rather than being folded into the ETL image.

This is the part of the design that deserves attention from an adopter. A crawl that scrapes structured pages can usually be trusted to a degree, but a crawl that reads a PDF, parses a fuzzy name field or has to guess which rows mean the same person produces records that a human should look at before they are merged and exported. Treating that review step as optional would corrupt exactly the deduplication stage the project presents as a core function.

The practical limit is that a review UI is a queue. It works when the volume of borderline records stays manageable for the people available, and it stops working when a new source produces more uncertain records than the team can read. The README does not document how the review queue is prioritised or how many people are expected to work through it.

## nomenklatura and yente offer a route other than a flat export

Two sibling projects are listed under associated repositories, and they describe a clearly different approach from downloading a dataset and grepping it. nomenklatura is a framework for storing data statements with full data lineage and for integrating entity data from multiple sources, and it also handles the enrichment function that links OpenSanctions to external databases like OpenCorporates. yente is an API for entity matching and searching.

The difference is where the matching happens. The dataset route hands you a merged file, and you match it yourself with your own tooling, against your own data, and you are responsible for the result. The nomenklatura and yente route keeps the statements and their provenance in a store, and pushes the matching to a search API that returns candidates rather than a single authoritative identity. Lineage is retained either way, but in the second case you get candidate sets to adjudicate instead of a fixed merge.

The trade-off is operational weight. The dataset path needs a crawl and a copy of the files. The service path needs two more components running, plus whatever you build to adjudicate candidates. For a one-off investigation, the file is usually the cheaper answer.

## No tagged releases, a latest image tag and a strict line on agent submissions

The repository has no GitHub releases, so there is no version to pin and no changelog to read at a release page. The last push was on 2026-10-20, which shows the work is current, but the compose file builds from the local Dockerfile and refers to the image tag ghcr.io/opensanctions/opensanctions:latest. A pipeline built on that tag is not reproducible: the same compose file yields different code on different days, and recording what you actually ran means capturing an image digest yourself.

The README also sets out a position on AI tools. The stated view is that contributions should carry meaningful human effort, judgement and context, because reviewing pull requests is work for maintainers. The wording is direct about the consequence: agent generated pull requests that exist to improve a contributor profile are rejected, and the accounts are banned. The project says outright that it has no definite policy, yet it treats unsupervised submissions as a category to be refused.

For a team that wants to contribute upstream, this is the practical constraint on how you engage. An automated patch that opens an issue or a pull request without a person behind it will not be merged, however correct it is.

## MIT covers the code while the data carries non-commercial terms

The licensing split is the part that most affects a decision. The README states that the code inside the repository is licensed under the MIT License, that data files produced by OpenSanctions are licensed under CC BY-NC 4.0, and that some files under datasets/ are unmodified copies of third party files that the project cannot license at all. COPYRIGHT.md records which of those applies to what, the repository carries a LICENSES/ directory and a REUSE.toml file, and a licensing page on the website covers commercial use.

The consequence for an adopter is that the permissive licence applies to the code you would run, not to the output you would sell. MIT is fine for a service you host, and CC BY-NC 4.0 is a non-commercial licence on the data files themselves, with a layer of third party copies underneath that carries no open licence at all. Which specific files land in that last category is decided by COPYRIGHT.md and the source list, not by the code licence.

This is not legal advice, and the point here is a practical one. If your use is commercial, or if you intend to redistribute the datasets inside a product, the licence check is on the data side, and reading COPYRIGHT.md is the first step, not the LICENSE file.

## Conclusion

OpenSanctions fits research, journalism and investigative teams who want the full crawl pipeline and are willing to host it, and it does not fit products that need a licensed, versioned, commercially usable feed, because the data files are released under CC BY-NC 4.0 and some files under datasets/ cannot be licensed at all. Before you build on it, read COPYRIGHT.md to see which licence covers which file, confirm that non-commercial terms suit your use, and read the crawl output rather than trusting a match blindly. The last push was on 2026-10-20, and the compose file tracks the image tag latest, so pin a digest before anything depends on it.

## FAQ

### How do I run OpenSanctions locally?

The README states that the easiest way to run zavod is Docker, with install details in the zavod documentation. The repository ships a docker-compose.yml and a Makefile whose run target builds the image and executes opensanctions run, and whose workdir target creates the data directory the compose file mounts.

### What data model does OpenSanctions use?

Crawlers write to the Follow the Money schema, described as a JSON focused anti-corruption data model, and that data is then optionally exposed in simplified formats such as CSV. The four stages in the codebase are parsing, cleaning and standardization, deduplication, and export.

### Can I use the OpenSanctions data commercially?

The code in the repository is under the MIT License, but the data files produced by the project are licensed under CC BY-NC 4.0, and some files under datasets/ are unmodified third party copies that cannot be licensed at all. The README points to COPYRIGHT.md for which licence covers which file and to the website licensing page for commercial use.

### Does OpenSanctions publish tagged releases?

The repository has no GitHub releases, and the last push was on 2026-10-20. The compose file refers to the image ghcr.io/opensanctions/opensanctions:latest, so the tag moves as the code changes.

## Sources

- [Issues](https://github.com/opensanctions/opensanctions/issues)
- [License: MIT](https://github.com/opensanctions/opensanctions/blob/main/LICENSE)
- [opensanctions/opensanctions on GitHub](https://github.com/opensanctions/opensanctions)
- [Project website](https://www.opensanctions.org)
- [README](https://github.com/opensanctions/opensanctions/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/opensanctions-opensanctions
