yente is an open source data match-making API. The service provides several HTTP endpoints to search, retrieve or match FollowTheMoney entities, including people, companies or vessels that are subject to international sanctions.
The yente API is built to provide access to OpenSanctions data, and can also be used to search and match other data, such as company registries or custom watchlists.
While yente is the open source core code base for the OpenSanctions API, it can also be run on-premises as a KYC appliance so that no customer data leaves your infrastructure.
- yente documentation - install, configure and use the service.
This deployment runs yente on a single server using Docker Compose with a multi-replica architecture and external cron-based reindexing. All infrastructure configuration is self-contained in this repository.
┌──────────────┐
│ Traefik │
│ (reverse │
│ proxy) │
└──────┬───────┘
│
┌───────────┴───────────┐
│ │
┌───────▼───────┐ ┌─────────▼───────┐
│ app │ │ app │
│ (replica 1) │ │ (replica 2) │
│ YENTE_AUTO_ │ │ YENTE_AUTO_ │
│ REINDEX=false│ │ REINDEX=false │
└───────┬───────┘ └────────┬────────┘
│ │
└──────────┬───────────┘
│
┌──────────▼──────────┐
│ index │
│ (Elasticsearch) │
│ port 9200 │
└─────────────────────┘
▲
(depends_on: healthy)
│
┌──────────┴──────────┐
│ cron-scheduler │
│ panubo/cron:1.4 │
│ reads crontab.txt │
└──────────┬──────────┘
│
│ docker compose run --rm
│
┌──────────▼──────────┐
│ reindexer │
│ (one-shot job) │
│ yente reindex │
└─────────────────────┘
| Service | Image | Replicas | Purpose |
|---|---|---|---|
traefik |
traefik:v3 |
1 | TLS termination, routing to app |
index |
elasticsearch:8.19.13 |
1 | Search index, 8GB JVM heap |
app |
yente:5.3.0 |
2 | API serving /search, /match |
reindexer |
yente:5.3.0 |
1 (profile) | Runs yente reindex, exits |
cron-scheduler |
panubo/cron:1.4 |
1 | Schedules reindexer via cron |
Multi-replica app (replicas: 2): Two yente instances behind a load balancer provide resilience. If one fails, the other continues serving. Under normal operation, requests are distributed across both.
External cron instead of auto-reindex: With multiple app replicas, YENTE_AUTO_REINDEX=true causes all replicas to clash, each trying to reindex simultaneously. Setting YENTE_AUTO_REINDEX=false disables the built-in scheduler, and the cron-scheduler container triggers reindexing externally — the standard pattern recommended by the yente project for multi-instance deployments.
Zero-downtime reindexing: Reindex is non-blocking because Elasticsearch uses index aliases. While reindexer builds a new index, the existing index continues serving API requests via the alias. The alias is atomically swapped once the new index is ready — no gap in service.
panubo/cron: Chosen over alternatives because it's actively maintained since 2016, runs jobs as non-root with configurable GID, logs to stdout/stderr (integrated with docker compose logs), and supports dynamic crontab reloading without restart.
Environment variables are set in .env:
| Variable | Value | Purpose |
|---|---|---|
YENTE_INDEX_TYPE |
elasticsearch |
Search backend type |
YENTE_INDEX_URL |
http://index:9200 |
Internal ES connection |
YENTE_MANIFEST |
/app/manifests/commercial.yml |
Dataset manifest |
OPENSANCTIONS_DELIVERY_TOKEN |
<redacted> |
OpenSanctions data access token |
YENTE_AUTO_REINDEX |
false |
Disabled — reindex via cron |
Reindexing runs twice daily at 00:00 and 12:00 UTC, defined in crontab.txt:
0 0,12 * * * cd /app && docker compose run --rm reindexer >> /proc/1/fd/1 2>&1
This schedule is version-controlled. To change the frequency, edit crontab.txt and restart the scheduler:
docker compose restart cron-scheduler# Start everything including cron
docker compose up -d
# Verify all services are healthy
docker compose ps
# Manually trigger a reindex (for immediate updates)
docker compose --profile reindex run --rm reindexer
# View real-time logs
docker compose logs -f cron-scheduler
docker compose logs -f reindexer
# Restart cron with updated schedule
docker compose restart cron-scheduler| Pro | Con |
|---|---|
| Zero-downtime reindex — ES aliases enable atomic index swap | Additional container — cron-scheduler adds minimal overhead (~5MB) |
| Resilient — 2 app replicas handle failure gracefully | Docker socket mount — cron container requires socket access to orchestrate reindexer |
| External cron survives app restarts — reindex schedule is independent of app lifecycle | UTC-only scheduling — cron container runs in UTC; DST/timezone shifts not supported |
Schedule is version-controlled — crontab.txt lives in repo |
No built-in alerting — reindex failures are only visible via logs |
Portable — all config lives in docker-compose.yml and .env |
Pre-initial index required — fresh deployment is unavailable until first reindex completes |
Efficient — delta/incremental updates by default (YENTE_DELTA_UPDATES=true) |
Shared ES resource contention — heavy reindex load may slightly elevate API latency during the ~5–15min window |
Reindex failing:
# Check the reindexer exit code and logs
docker compose --profile reindex run --rm reindexer
docker compose logs reindexerCron not firing:
# Verify the container is running
docker compose ps cron-scheduler
# Check scheduler logs
docker compose logs cron-scheduler
# Force a reload of the crontab
docker compose exec cron-scheduler sh -c "kill -HUP \$(cat /tmp/go-crond.pid)"Elasticsearch not healthy:
# Check ES health directly
docker compose exec index curl -s http://localhost:9200/_cluster/health
# View ES logs
docker compose logs indexAPI returning stale data:
# Force an immediate reindex
docker compose --profile reindex run --rm reindexer --force
# Verify the alias points to the latest index
docker compose exec index curl -s http://localhost:9200/_cat/aliases/yenteyente is implemented in asynchronous, typed Python using the FastAPI framework. We're happy to see any bug fixes, improvements or extensions from the community. To set up a local development environment, use uv:
git clone https://github.com/opensanctions/yente.git
cd yente
# Install runtime and development dependencies
uv sync
# Install pre-commit hooks with useful checks
prek install
# Activate the virtual environment
source .venv/bin/activateThis will install a broad range of dependencies, including numpy, scikit-learn and pyicu, which are binary packages that may require a local build environment. For pyicu in particular, refer to the package documentation.
Once you've set the YENTE_INDEX_URL environment variable to point to a running instance of ElasticSearch or OpenSearch, you can run the web server like this:
yente servebump2version --verbose minor # or patch
git push && git push --tags
You'll need a local piece of software to receive and display traces. Jaeger is widely used and has an easy-to-use Docker image. Paste the command to start the all-in-one container from the Jaeger documentation and find the web interface at http://localhost:16686.
Then, just run yente like this:
opentelemetry-instrument --exporter_otlp_traces_endpoint="http://localhost:4317" yente serveyente is licensed according to the MIT license terms documented in LICENSE. Using the service in a commercial context may require a data license for OpenSanctions data.