Canada SEMA Sanctions Delta avatar

Canada SEMA Sanctions Delta

Under maintenance

Pricing

$10.00 / 1,000 sanctions listing changeds

Go to Apify Store
Canada SEMA Sanctions Delta

Canada SEMA Sanctions Delta

Under maintenance

Watches the official Canadian Consolidated Autonomous Sanctions List (SEMA) by country/regime and reports only real changes: new designations and confirmed de-listings. Low, bursty cadence - free when nothing changes.

Pricing

$10.00 / 1,000 sanctions listing changeds

Rating

0.0

(0)

Developer

Radu Furtuna

Radu Furtuna

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

9 days ago

Last modified

Share

Watches the official Canadian Consolidated Autonomous Sanctions List (Global Affairs Canada, Special Economic Measures Act — SEMA) by country/regime and reports only genuine changes since your last check: a new designation, or a confirmed de-listing. Free when nothing changes — you pay only for delivered events.

Built for compliance/AML/sanctions-screening teams who need to know when someone in their book of business gets newly designated — or is removed from the list — without re-downloading and diffing a ~2.9 MB XML file themselves.

Read this before you rely on the signal — volume is weak, not strong

  • Measured event volume is low. A direct count of DateOfListing-DateDinscription in the live file over the last 30 days (checked 2026-09-14) found 8 new designations across the entire list, all 19 countries/regimes combined. Some 30-day windows will have zero. This is a discovery monitor for a low-and-bursty signal, not a high-volume feed — an earlier internal estimate of "26/month" from a prior check could not be reproduced against the live file today (same file, same size, same Last-Modified as that check) and is not repeated here; treat the 8/30-days figure as the honest current baseline, not a promise of steady flow. Do not subscribe expecting daily activity.
  • The source path has already moved once. The original URL this actor was built against (.../sanctions/sema-lmes.xml under an older base path) returned 404 on a later check; the current path was re-found via the source's own index page. If Global Affairs Canada moves the file again, every watch on this actor will start failing with sema_snapshot_incomplete (or a permanent fetch error) until the actor is updated — this is a real, disclosed operational risk of this particular source, not a theoretical one.
  • The file carries no status field at all. The list contains only currently active designations — there is no "revoked"/"active" flag and no reason-for-removal field. A de-listed name simply stops appearing in the next full download. This actor's entire signal is therefore presence/absence of a record between two snapshots, not a stated reason for the change.
  • The source has no own stable id. Identity is a 6-part composite key built from the record's own content: country + schedule/annex + last name + given name + entity/ship name + date of birth (or ship build date). This key is verified unique across all 5,698 live records (checked 2026-09-14). The Item-NumeroDarticle field is deliberately not part of this key — it is a position within a single schedule/annex, not a stable identifier, and can be renumbered when neighbouring entries are added or removed at a future republish.
  • Because there is no id, country is part of a record's identity, not a separate attribute. If Global Affairs Canada ever changes how a listing's own Country-Pays is spelled, that shows up as an ordinary (two-strike) disappearance under the old country and a fresh baseline-free appearance under the new one — not as some special "still in the file, just left your filter" event. There is no left_filter status in this actor's output for that reason (unlike some other actors in this family whose filter field is not part of the record's identity).
  • De-listing (disappeared) detection needs two consecutive complete downloads without the record (two-strike) — a single missing check only produces an unpaid removal_candidate row.
  • If this run's XML download looks truncated, malformed, or suspiciously small (coverage.semaSnapshotIncompleteCount > 0), no baseline is initialized and no de-listing detection runs at all for the affected watches — a partial snapshot never gets silently treated as "nothing changed" or "everyone was de-listed." Nothing is billed in that case.
  • This is an informational monitor of a public sanctions list, not a real-time compliance-screening service and not a substitute for checking directly with Global Affairs Canada (international.gc.ca/world-monde/international_relations-relations_internationales/sanctions) before making a business or compliance decision.

How it works

  1. First run per watch (monitorId + watchId) records a private baseline of every record currently matching your country filter — no rows, no charge. A baseline is only recorded from a complete download (see integrity checks below).
  2. Every later run re-downloads the full list and classifies each record in your filter:
    • appeared — a new record matching your filter appeared (new designation, or a name returning after a confirmed prior de-listing);
    • removal_candidate — a record you'd seen before is missing from this run's download for the first time in a row. Delivered as an informational row with billed: false — not charged, and not proof of anything (a single missing point is indistinguishable from a transient download hiccup);
    • disappeared — the same record is missing for the second consecutive complete download. Billed: the designation was very likely removed from the list.
  3. Only appeared/disappeared are billed, one PPE event each (sanctions-listing-changed). removal_candidate is always informational, never billed.
  4. Attribute changes on a record that stays listed and stays inside your filter are never reported as an event. This actor is a discovery/membership monitor, not an attribute-change tracker: the source itself carries no revision history for a listing's non-identity fields (aliases, ship IMO number, etc.). Only presence/absence relative to your filter is ever reported.

Download integrity (why a partial file can never become a false signal)

The list is one XML file, no pagination — there is no way to fetch "the next page" if a download is truncated, so this actor checks completeness independently, five ways, before trusting a download for baseline or de-listing purposes at all: the server's Content-Length header must be present and numeric; the actually-downloaded byte count must match it exactly; the XML must parse as well-formed and complete (a ParseError — the typical symptom of a stream cut off mid-tag — is never silently tolerated); the final record count must clear a floor (currently 3,000 — the live file has 5,698); and the file is downloaded twice, over two independent connections, and the two results must match byte-for-byte (see below). Any failure discards the whole snapshot for every watch in the run; none of these are about how many records changed, only about whether the download itself can be trusted.

Known limitation: the double-download check is a weaker substitute for an independent completeness marker, not an equivalent. Unlike some sibling actors in this family whose source publishes through a catalog wrapper (e.g. a CKAN package_show call that can report a resource's expected size before downloading it), international.gc.ca serves this file as a plain static XML with no independent, third-party record of what a complete download should look like — no catalog entry, and the file's own human-readable landing page is not reliable either (its displayed date was observed to disagree with the file's own Last-Modified by six days during construction). To reduce (not eliminate) the risk of a truncated file passing every check unnoticed, this actor downloads the same URL twice, each over its own separate connection (a fresh requests.Session() per attempt, no connection reuse between the two), and requires the two full response bodies to be byte-for-byte identical in addition to each individually passing all four checks above. If they disagree, the run treats the snapshot as untrustworthy and uses neither of the two downloads. This closes the specific failure mode where a truncated response is internally self-consistent (well-formed XML, correct Content-Length for its own — shorter — body) and would otherwise slip past the first four checks. It does not close the case where the same intermediary (a CDN edge, or a corporate/network proxy sitting between wherever this actor runs and international.gc.ca) truncates both independent requests identically — in that scenario the two downloads would still match each other while both being wrong, and this check would not catch it. A live DNS/TLS check at construction time (single A record, no CDN-typical headers, certificate issued to Shared Services Canada) found no evidence of an edge CDN in front of this source, which lowers — but by itself does not prove — the likelihood of this residual scenario.

Input

  • monitorId — name of this monitor's durable history (a-z0-9-, up to 40 chars). Re-run with the same monitorId to continue the same history; a new value starts a fresh baseline.
  • watches — 1-20 objects {"watchId", "country"}. country is matched as an exact string (after trimming leading/trailing whitespace) against the source's own bilingual Country-Pays field, or the special value ALL for the whole list with no filter. This field is not a closed enum — Canada adds new sanctions regimes from time to time (the live file already carries non-country entries such as a corrupt-foreign-officials-justice regulation) — use the exact string as the source publishes it. Once a watchId is used, its country is locked — reusing the id with a different filter fails the run instead of silently mixing histories. Add new watchIds any time under the same monitorId.
  • notifyOn / webhookUrl — optional HTTPS webhook receiving a JSON digest of billed changes. The webhook is only sent if a conservative worst-case time budget for the request itself still fits before the run's deadline; if it does not, the webhook is skipped entirely rather than risking a late/contradictory delivery, and the run honestly reports run_wall_timeout.

Output

  • Dataset — every row this run produced (watchId, compositeKeyFingerprint, status, country, lastName/givenName or entityOrShip, dateOfBirthOrShipBuild, schedule, itemNumber, dateOfListing, aliases, shipImoNumber, titleOrShipType, eventId, billed). status is one of appeared/disappeared/removal_candidate. compositeKeyFingerprint is a SHA-256 hash of the record's 6-part identity key (see above) — used internally for claim/event deduplication and shown here so you can join rows belonging to the same underlying record across runs. A durable named dataset holds the append-only history of everything ever delivered under this monitorId; the default (run) dataset mirrors what this run delivered plus a run_summary row.
    • billed on a removal_candidate row is always false — never charged, by design.
    • billed on an appeared/disappeared row is written as null. The row is saved to the dataset before its charge is attempted (we never charge before delivery is confirmed — see "Guarantee" below), so the final charge outcome isn't known yet at write time. To find out whether one specific row was actually charged, match its eventId against this run's Pay-Per-Event charged-events ledger in the Apify Console. For the run-level total, use recordsDelivered/recordsBilled in coverage and the unbilled count in digest.
  • Key-value store — coverage (per-watch status/reason/matchedCount/candidateCount, requested/ attempted/succeeded/failed, records delivered/billed, total list records seen this run; semaSnapshotIncompleteCount > 0 means the download was rejected as incomplete for the affected watches — see "Honest limits") and digest (a short human-readable summary, which also carries this partial-coverage warning even when no billable change occurred).

Guarantee

At-most-once delivery and billing, not exactly-once. A crash right after a right-to-bill is granted can lose that one event (it will not be redelivered), but you will never be charged twice for the same underlying change. See ROADMAP.md for the full claim protocol and its boundaries.

Durable dataset vs. default dataset

The durable named dataset is a journal of delivery attempts across every run of this monitorId — it is not itself proof of payment (a row can in rare crash scenarios be written without a confirmed charge). The default (per-run) dataset mirrors this run's rows plus one run_summary record and is what you see in the Apify Console run view. Reconcile billing against coverage, not row count alone.