Tadawul Disclosure Monitor — Saudi Exchange
Pricing
Pay per usage
Tadawul Disclosure Monitor — Saudi Exchange
Saudi Tadawul Disclosure Monitor is an Apify Actor that collects company announcements from the Saudi Exchange (Tadawul) and returns them as structured data. It supports filtering announcements by company name, ticker, start date, end date, and maximum results.
Pricing
Pay per usage
Rating
0.0
(0)
Developer
Mohamed Youssef
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
14 days ago
Last modified
Categories
Share
An Apify Actor that monitors official corporate disclosures published by the Saudi Exchange (Tadawul) — Issuer & Financial Advisor Announcements.
It normalizes results into a clean, consistent schema, remembers what it has already returned so scheduled runs only surface new disclosures, and is built with an adapter architecture so more markets could be added later without touching the core logic (this build is intentionally scoped to Saudi Arabia only).
Honesty note about reliability. Tadawul renders its disclosure list client-side with JavaScript. This Actor prefers Tadawul's own structured JSON announcement API when it's reachable, and falls back to a real, headless Chromium browser (via Crawlee's
PlaywrightCrawler) to page through the on-page list when more results are needed. It has not been run against the live site from inside this development environment (no outbound network access here), so treat the first run on Apify as a validation run — check the log output, and see "If scraping stops finding results" below if Tadawul has changed its markup/API since this was built.
What it does
- The adapter requests Tadawul's announcement API first, then falls back to a headless-browser pass over the announcement list page if more results are still needed.
- Every disclosure is normalized into one unified schema (see below).
- Disclosures are deduplicated against everything this Actor has returned before, using a persistent key-value store — so a daily scheduled run only returns what's new.
- New (or, if
onlyNewis off, all matching) disclosures are pushed to the Actor's Dataset.
Input
| Field | Type | Description |
|---|---|---|
company | string | Optional company name filter, e.g. "Saudi Aramco" |
ticker | string | Optional ticker/symbol filter — Tadawul identifies companies by numeric code, e.g. "2222" |
Date | string | Optional lower date bound, DD-MM-YYYY |
maxResults | integer | Max disclosures to return (default 100) |
onlyNew | boolean | If true (default), only disclosures not seen in a previous run are returned. If false, everything found in the date range is returned, including previously-seen items. |
proxyConfiguration | object | Apify Proxy settings. Recommended if Tadawul starts blocking Apify's default IPs. |
Example — everything:
{"maxResults": 100,"onlyNew": true}
Example — a single company:
{"company": "Saudi Aramco","ticker": "2222","maxResults": 50,"onlyNew": false}
A ready-to-use test input is in examples/input-tadawul-test.json — paste it into the Actor's Input tab (or the "Start" JSON editor).
Validation
- If no
companyortickeris given, the Actor simply collects general disclosures for the date range.
Output (unified schema)
Every row in the Dataset has this shape:
{"country": "Saudi Arabia","market": "Tadawul","company_name": "Saudi Aramco","ticker": "2222","disclosure_title": "Saudi Aramco (2222) - Board of Directors' Decisions","disclosure_date": "24-8-2026","disclosure_time": "16:20","disclosure_url": "https://www.saudiexchange.sa/.../announcement/123456","source_data": {}}
Any field the source doesn't provide is null — nothing is invented. Anything extra the source exposes that doesn't map cleanly to the unified schema is preserved under source_data instead of being discarded.
Deduplication
The Actor keeps a persistent "seen" list in a named Apify Key-Value Store (SEEN-DISCLOSURES-TADAWUL), which — unlike the default run-scoped store — survives between runs of the same Actor on Apify. This is what makes onlyNew meaningful across scheduled runs.
The unique ID used for deduplication is chosen in this priority order (per disclosure):
- the official disclosure ID (Tadawul's
anId) - the disclosure's own URL
- its document URL
- a SHA-256 hash of its core fields (company, ticker, title, type, date, time), as a last resort
See src/utils/deduplication.js.
Daily monitoring / scheduling
- In the Apify Console, open this Actor → Schedules → Create new schedule.
- Pick a cron expression, e.g.
0 8 * * *for every day at 8:00 AM (Actor's/container's timezone — set your desired timezone on the schedule itself). - Attach the input you want (typically
onlyNew: true). - Each run will then only output disclosures that weren't seen in any previous run.
Viewing results
- Dataset tab on the run: table/JSON/CSV/Excel view of every disclosure pushed in that run (use the Overview view for the key columns).
- Storage → Key-value stores:
SEEN-DISCLOSURES-TADAWULholds the internal "already seen" ledger (not meant for direct consumption, but useful for debugging deduplication). - Downstream automation (n8n, Telegram, WhatsApp, email, further AI processing) can read the Dataset via the Apify API or an Apify integration.
Local development
npm install# Windows/macOS/Linux with Chromium already available via `playwright install` if neededapify run # or: node src/main.js, after setting an input in storage/key_value_stores/default/INPUT.json
On the Apify platform, just create a new Actor from this source (or push with the Apify CLI: apify push) — the Dockerfile uses Apify's official Playwright/Chrome base image, so no manual browser installation is needed.
Project structure
.actor/actor.json Actor metadata + Dataset view definitionINPUT_SCHEMA.json Input form shown in the Apify Consolesrc/main.js Entry point: validation, orchestration, summary loggingadapters/base.js Abstract MarketAdapter contracttadawul.js Tadawul-specific scraping logicutils/normalizer.js Maps the adapter's raw output to the unified schemadeduplication.js Persistent "seen disclosures" trackingdates.js Date/time parsing helpersexamples/input-tadawul-test.jsonDockerfilepackage.jsonREADME.md (this file)
Adding another market later (e.g. EGX, ADX, DFM, LSE)
- Create
src/adapters/<market>.jsexporting a class that extendsMarketAdapter(seesrc/adapters/base.js) and implementsasync collect(), returning an array ofRawDisclosureobjects. - Register it and its
{ country, market }pair insrc/main.js.
Nothing else needs to change — normalizeDisclosure(), deduplication, dataset pushing, and logging are all market-agnostic.
If scraping stops finding results
Tadawul is a JavaScript-rendered site and its exact markup/API can change over time. If a run logs collected 0 disclosures:
- Open the run's log and check for the
WARNING/ERRORlines from the adapter. - Try enabling Apify Proxy (residential) in the input — Tadawul may rate-limit or geo-block data-center IPs.
- If the site's structure changed, the fix is localized to src/adapters/tadawul.js.
Notes on the Apify Store listing
This Actor does not make guarantees like "100% uptime" or "always works" — like any scraper of a third-party website, it depends on the source site's markup/API staying reasonably stable and being reachable from Apify's infrastructure. Please respect Tadawul's terms of use; this Actor only reads publicly published disclosure pages and applies reasonable rate limiting (see defaultCrawlerOptions() in src/adapters/base.js).