<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: SM</title>
    <description>The latest articles on DEV Community by SM (@malmon).</description>
    <link>https://dev.to/malmon</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3957073%2F49b8d322-c075-4b49-bc48-dc644c9bb2e1.png</url>
      <title>DEV Community: SM</title>
      <link>https://dev.to/malmon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/malmon"/>
    <language>en</language>
    <item>
      <title>Adding Commodity-to-Equity Linkage to a Macro Stress Monitor</title>
      <dc:creator>SM</dc:creator>
      <pubDate>Sun, 14 Jun 2026 09:12:31 +0000</pubDate>
      <link>https://dev.to/malmon/adding-commodity-to-equity-linkage-to-a-macro-stress-monitor-4lcg</link>
      <guid>https://dev.to/malmon/adding-commodity-to-equity-linkage-to-a-macro-stress-monitor-4lcg</guid>
      <description>&lt;p&gt;I recently launched a rules-based macro stress monitor for African and LatAm economies on Apify. It pulls from central bank APIs, the World Bank, IMF IFS, and the Pink Sheet, and returns structured JSON covering FX momentum, inflation, commodity terms-of-trade, reserves, and structural vulnerability for each country.&lt;/p&gt;

&lt;p&gt;The commodity signal already told me &lt;em&gt;which countries&lt;/em&gt; were benefiting from or getting hurt by commodity price moves. What it didn't tell me was &lt;em&gt;who&lt;/em&gt; — which listed companies sit in the direct path of that move.&lt;/p&gt;

&lt;p&gt;So I built a second layer: &lt;code&gt;companySignals&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem Worth Solving
&lt;/h2&gt;

&lt;p&gt;The stress monitor already computed something like this for Colombia:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"commodityCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CRUDE_OIL_AVG"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"commodityName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Crude oil"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"yoyPct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;60.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencePct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exportImpactPct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;18.02&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oil up 60% YoY, 30% of Colombia's export basket, +18pp terms-of-trade impact. The country-level signal was clear. But "Colombia benefits from oil prices" tells you nothing actionable about where that benefit flows at the company level.&lt;/p&gt;

&lt;p&gt;The obvious answer is Ecopetrol. But what about Chile and copper? Peru has both copper &lt;em&gt;and&lt;/em&gt; gold firing simultaneously — which companies have exposure to both, and how do you represent that without duplicating entries?&lt;/p&gt;

&lt;p&gt;Those were the design questions.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Data Model
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Static reference file
&lt;/h3&gt;

&lt;p&gt;The company data is a static JSON reference file — not live-scraped. Sourced from Wikipedia sector listings, stock exchange sector pages (JSE, BVL, B3, BVC, NGX, GSE), and company investor relations pages. Researched once, committed to the repo, updated manually when something changes.&lt;/p&gt;

&lt;p&gt;Structure per pair:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"commodityCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"COPPER"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"companies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Antofagasta plc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ticker"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ANTO"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LSE: ANTO"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"exposureTier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"primary"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"note"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Chilean copper pure-play (Luksic group) operating Los Pelambres, Centinela, Antucoya and Zaldivar."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BHP Group Limited"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ticker"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BHP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ASX &amp;amp; NYSE &amp;amp; LSE: BHP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"exposureTier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"significant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"note"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Operates Escondida, the world's largest copper mine, plus Spence in northern Chile."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dataYear"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JSE/LSE sector listings + Wikipedia"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;exposureTier&lt;/code&gt; is per-commodity, not per-company. That distinction matters — a company can be &lt;code&gt;primary&lt;/code&gt; on one commodity and &lt;code&gt;significant&lt;/code&gt; on another. Buenaventura in Peru is the clearest example.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Aggregation Problem: Buenaventura
&lt;/h2&gt;

&lt;p&gt;Peru's commodity tailwind flags are firing on both copper (+42% YoY, +13.5pp impact) and gold (+39% YoY, +8.1pp impact) simultaneously. Buenaventura appears in both reference lists — as &lt;code&gt;primary&lt;/code&gt; for gold and &lt;code&gt;significant&lt;/code&gt; for copper.&lt;/p&gt;

&lt;p&gt;A flat schema would return two separate Buenaventura entries, which is misleading. The aggregated schema groups by company and nests commodity signals:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Compania de Minas Buenaventura"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ticker"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BVN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NYSE: BVN; BVL: BUENAVC1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"signalCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"commoditySignals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"commodityCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"COPPER"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"commodityName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Copper"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"exposureTier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"significant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"note"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Copper exposure via ~19% stake in Cerro Verde and El Brocal mine."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"yoyPct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;42.07&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"exportImpactPct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;13.46&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"direction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tailwind"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"commodityCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GOLD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"commodityName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Gold"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"exposureTier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"primary"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"note"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Peru's largest publicly traded precious-metals miner."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"yoyPct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;38.61&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"exportImpactPct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;8.11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"direction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tailwind"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;signalCount&lt;/code&gt; is the sortable/filterable field — "show me companies touched by more than one of this week's commodity moves" is now a one-line filter.&lt;/p&gt;




&lt;h2&gt;
  
  
  Trigger Logic
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;companySignals&lt;/code&gt; only populates when a &lt;code&gt;commodityShock&lt;/code&gt; or &lt;code&gt;commodity_tailwind&lt;/code&gt; compound flag fires for that (country, commodityCode) pair. On quiet weeks when no flag fires, the field is absent — it doesn't try to manufacture a story.&lt;/p&gt;

&lt;p&gt;The flag check runs against the compound flags already computed by the stress monitor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"compoundFlags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"commodity_tailwind:Copper+13.5pp,Gold+8.1pp"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the flag fires, look up the (country, commodityCode) pair in the reference file. If a match exists, attach the companies. If not, omit the field silently. No errors, no empty arrays.&lt;/p&gt;

&lt;p&gt;The direction field (&lt;code&gt;tailwind&lt;/code&gt; or &lt;code&gt;headwind&lt;/code&gt;) is derived automatically from the price change sign and whether the country is a net exporter of that commodity.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Output Actually Found
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The interesting case is not "company benefits from tailwind."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's obvious — Ecopetrol benefits from oil prices. Anyone following Colombian equities already knows this.&lt;/p&gt;

&lt;p&gt;The interesting case is the &lt;em&gt;gap&lt;/em&gt; between the country-level macro signal and the company-level reality.&lt;/p&gt;

&lt;p&gt;Colombia has a commodity tailwind and the calmest acute stress reading in the region (1.1/100). But Colombia also has the highest structural vulnerability in the dataset (74.6/100), with debt service consuming 43% of export earnings and the IMF Flexible Credit Line cancelled in October 2025.&lt;/p&gt;

&lt;p&gt;Ecopetrol is majority state-owned. S&amp;amp;P downgraded Ecopetrol's credit rating &lt;em&gt;alongside&lt;/em&gt; Colombia's sovereign rating in April 2026 — explicitly because the rating is capped by the sovereign due to Ecopetrol's importance to government revenue.&lt;/p&gt;

&lt;p&gt;Meanwhile, Citigroup downgraded Ecopetrol in June 2026 citing production decline. A union launched a 24-hour strike. The stock moved on presidential election news.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;companySignals&lt;/code&gt; correctly flags Ecopetrol as exposed to Colombia's oil tailwind. But the real story is that the company-level factors (production, labor, politics) are currently dominating over the commodity price move. The macro signal is a &lt;em&gt;research hook&lt;/em&gt;, not a standalone buy/sell signal.&lt;/p&gt;

&lt;p&gt;That's why the output includes a &lt;code&gt;direction&lt;/code&gt; field and an &lt;code&gt;exposureTier&lt;/code&gt; but deliberately does not include a price target, a recommendation, or a "benefit score." The design is intentionally a starting point for research, not a conclusion.&lt;/p&gt;




&lt;h2&gt;
  
  
  Coverage So Far
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;LatAm (7 countries, 9 pairs):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Peru — Copper, Gold&lt;/li&gt;
&lt;li&gt;Chile — Copper&lt;/li&gt;
&lt;li&gt;Colombia — Crude Oil&lt;/li&gt;
&lt;li&gt;Brazil — Crude Oil, Soybeans&lt;/li&gt;
&lt;li&gt;Argentina — Soybeans&lt;/li&gt;
&lt;li&gt;Uruguay — Beef, Soybeans&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Africa (8 pairs, in progress):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;South Africa — Platinum, Gold&lt;/li&gt;
&lt;li&gt;Ghana — Gold, Crude Oil&lt;/li&gt;
&lt;li&gt;Nigeria — Crude Oil&lt;/li&gt;
&lt;li&gt;Zambia — Copper&lt;/li&gt;
&lt;li&gt;Tanzania — Gold&lt;/li&gt;
&lt;li&gt;Côte d'Ivoire — Cocoa (first headwind test case)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;35+ companies across both regions, all with real verifiable tickers, at least one major/well-known name per pair as a credibility anchor.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;The static reference file is the right call for v1 — no API dependency, no scraping, predictable output. The gap is that it only covers pairs I've researched manually. The actor returns nothing (rather than erroring) for unflagged or unresearched pairs, which is the honest behaviour — better to omit than to fabricate.&lt;/p&gt;

&lt;p&gt;The next analytical layer is peer-relative z-scores: instead of comparing each country only to its own history, compare it to regional peers on the same run. Colombia's structural vulnerability of 74.6 is striking in isolation; it's even more striking when Chile — riding the &lt;em&gt;same&lt;/em&gt; commodity tailwind — sits at 5.5. That cross-country comparison is pure derivation from data already in the dataset.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Actor links:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://apify.com/malmon/african-economic-stress-monitor" rel="noopener noreferrer"&gt;African Economic Stress Monitor — Apify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://apify.com/malmon/latam-economic-stress-monitor" rel="noopener noreferrer"&gt;LatAm Economic Stress Monitor — Apify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rapidapi.com/malmon-malmon-default/api/african-economic-stress-monitor1" rel="noopener noreferrer"&gt;RapidAPI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://malmonde.substack.com" rel="noopener noreferrer"&gt;Methodology and findings — Substack&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>analytics</category>
      <category>api</category>
      <category>datascience</category>
      <category>showdev</category>
    </item>
    <item>
      <title>What I Built the Day Apify Launched MCP Connectors</title>
      <dc:creator>SM</dc:creator>
      <pubDate>Thu, 11 Jun 2026 13:37:12 +0000</pubDate>
      <link>https://dev.to/malmon/what-i-built-the-day-apify-launched-mcp-connectors-5deh</link>
      <guid>https://dev.to/malmon/what-i-built-the-day-apify-launched-mcp-connectors-5deh</guid>
      <description>&lt;p&gt;Apify launched MCP connectors on June 9, 2026. The idea: actors can now write directly to authenticated external apps — Notion, Slack, GitHub — without ever holding the user's credentials. I'd been running an African economic stress monitor on Apify for a few weeks, with results disappearing into datasets nobody looked at. MCP connectors seemed like a way to fix that.&lt;/p&gt;

&lt;p&gt;Here's what it took to add Notion output, what I learned building it, and what the result actually looks like.&lt;/p&gt;




&lt;h2&gt;
  
  
  What MCP Connectors Actually Are
&lt;/h2&gt;

&lt;p&gt;Before this launch, Apify actors could scrape the open web and return datasets. Anything requiring login — writing to your Notion workspace, posting to your Slack — had to happen outside Apify through separate integrations.&lt;/p&gt;

&lt;p&gt;MCP connectors fix this with a proxy layer. Apify hosts an MCP server; your actor connects to it at runtime. The proxy holds the external app credentials — your actor authenticates to the proxy using its own Apify token, and the proxy forwards requests to Notion on your behalf. Your actor never sees Notion credentials directly.&lt;/p&gt;

&lt;p&gt;The architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Actor (Node.js)
  └── @modelcontextprotocol/sdk Client
        └── StreamableHTTPClientTransport
              └── POST https://mcp.apify.com/{connectorId}
                    └── Apify MCP Proxy
                          └── Notion API
                                └── Your Notion Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Result First
&lt;/h2&gt;

&lt;p&gt;Before the technical walkthrough — here's what appeared in my Notion workspace after the first run:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl5kkv4kees7sattndgaq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl5kkv4kees7sattndgaq.png" alt=" " width="800" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A board view grouped by verdict. 9 stable, 2 on watch (Côte d'Ivoire and Rwanda). Updates automatically on every actor run. Zero manual work after the initial setup.&lt;/p&gt;

&lt;p&gt;The Today view shows the current snapshot across all 11 covered economies:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8e3xv96nq92lvzq0t396.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8e3xv96nq92lvzq0t396.png" alt=" " width="800" height="251"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And filtering by a specific country gives you the full history — here's Ghana's last four days:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc77nngu299mmgubp55qi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc77nngu299mmgubp55qi.png" alt=" " width="798" height="176"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ghana moved from &lt;code&gt;watch&lt;/code&gt; to &lt;code&gt;stable&lt;/code&gt; on June 10. The cedi's 30-day depreciation dropped below the 5% threshold that fires the FX momentum signal. That transition is visible directly in the Notion log.&lt;/p&gt;




&lt;h2&gt;
  
  
  One-Time Setup in Apify Console
&lt;/h2&gt;

&lt;p&gt;Before touching any actor code, you connect Notion to Apify once:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Console → Settings → API &amp;amp; Integrations → MCP Connectors → Add connector&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Choose Notion, authorize via OAuth. Apify creates a connector with a stable ID tied to your Notion workspace. That connector ID is what you pass into the actor as an input — Apify injects the proxy URL and auth at runtime.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Changed in the Actor
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1 — Declare the connector input in &lt;code&gt;input_schema.json&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"notionConnector"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"resourceType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcpConnector"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mcp.notion.com/mcp"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;resourceType: "mcpConnector"&lt;/code&gt; field is the key. It tells Apify this input is a connector reference, not a plain string. The Console UI renders a dropdown of your connected MCP connectors instead of a text field. No token management in the actor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2 — Connect at runtime:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/sdk/client/index.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StreamableHTTPClientTransport&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; 
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/sdk/client/streamableHttp.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StreamableHTTPClientTransport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APIFY_MCP_PROXY_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;connectorId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; 
      &lt;span class="na"&gt;requestInit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APIFY_TOKEN&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; 
      &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-actor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apify injects &lt;code&gt;APIFY_MCP_PROXY_URL&lt;/code&gt; and &lt;code&gt;APIFY_TOKEN&lt;/code&gt; automatically when the run starts. The actor authenticates to the proxy with its own token — not the user's Notion credentials.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3 — Discover available tools:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toolsResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listTools&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The proxy exposes Notion-scoped tools: &lt;code&gt;notion-search&lt;/code&gt;, &lt;code&gt;notion-retrieve-page&lt;/code&gt;, &lt;code&gt;notion-create-pages&lt;/code&gt;, &lt;code&gt;notion-update-page&lt;/code&gt;, &lt;code&gt;notion-retrieve-database&lt;/code&gt;. The one I needed was &lt;code&gt;notion-create-pages&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4 — Write results to Notion:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;database_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your-database-id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;countries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;country&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; — &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;Country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;Verdict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;verdict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Vulnerability Level&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vulnerabilityLevel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Coverage Level&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coverageLevel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Stress Score&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stressScore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fired Signal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firedSignals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;date:Last Updated:start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;asOf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Actor Run URL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;runUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;callTool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;notion-create-pages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt; 
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Notion write failed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Gotchas
&lt;/h2&gt;

&lt;p&gt;This is the part I wish I'd read before building.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Property values are scalars, not Notion blocks.&lt;/strong&gt; The MCP wrapper uses a simplified encoding — &lt;code&gt;string | number | null&lt;/code&gt; values only. If you pass raw Notion API property blocks (&lt;code&gt;{ title: [{ text: { content: "..." } }] }&lt;/code&gt;), you get &lt;code&gt;invalid_union at path: []&lt;/code&gt;. The wrapper handles type-mapping to Notion's internal format based on the database column schema.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Date fields use a special key format.&lt;/strong&gt; You can't use the column name directly for date fields. The format is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"date:COLUMN_NAME:start"  →  ISO 8601 string
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So &lt;code&gt;"Last Updated": "2026-06-10T14:30:00.000Z"&lt;/code&gt; silently fails or errors. The correct key is &lt;code&gt;"date:Last Updated:start": "2026-06-10T14:30:00.000Z"&lt;/code&gt;. This is specific to the Apify MCP wrapper — not a Notion API convention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;isError&lt;/code&gt; is a flag, not an exception.&lt;/strong&gt; A bad schema or Notion API error returns &lt;code&gt;isError: true&lt;/code&gt; on the result object with details in &lt;code&gt;content[0].text&lt;/code&gt;. It doesn't throw. You must check it explicitly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dockerfile gotcha.&lt;/strong&gt; If your Dockerfile has &lt;code&gt;COPY . ./&lt;/code&gt; after &lt;code&gt;RUN npm run build&lt;/code&gt;, it overwrites your freshly compiled &lt;code&gt;dist/&lt;/code&gt;. Add &lt;code&gt;dist/&lt;/code&gt; to &lt;code&gt;.dockerignore&lt;/code&gt; to prevent this. Cost me an embarrassing number of deploys.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tool schemas get truncated in Apify console logs.&lt;/strong&gt; Log lines are truncated. If you need to inspect the full tool schema, write it to a key-value store with &lt;code&gt;Actor.setValue()&lt;/code&gt; and read it via the Console's KV viewer.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Signals View
&lt;/h2&gt;

&lt;p&gt;One unexpected benefit of pushing to Notion: the chart views. Here's the signal distribution across all 11 economies today:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3s65jp463m43k467aajy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3s65jp463m43k467aajy.png" alt=" " width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The dominant signals aren't FX crises or inflation — they're debt distress and political stability concerns, each affecting 4 of 11 economies. That's not what I expected when I started building this. But it's what the data shows.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Enables
&lt;/h2&gt;

&lt;p&gt;A few workflows that weren't practical before this integration:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The canary view&lt;/strong&gt; — a Notion filtered view showing only countries where the verdict changed since the previous run. Usually empty. Noticeable when it isn't. Ghana's move from watch to stable on June 10 would have appeared here automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict change alerts&lt;/strong&gt; — Notion automations (available on paid plans) can trigger when a property value changes. When a country flips from &lt;code&gt;stable&lt;/code&gt; to &lt;code&gt;watch&lt;/code&gt;, send an email or Slack message. No Zapier required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-referencing with your own data&lt;/strong&gt; — add a "Portfolio Exposure" column to the same database alongside the automated signals. Build a linked view that filters to countries where you have exposure AND vulnerability is HIGH. The automated and manual data live in the same Notion workspace.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Actor
&lt;/h2&gt;

&lt;p&gt;The full African Economic Stress Monitor is available on Apify Store and RapidAPI. It covers Nigeria, Ghana, Kenya, South Africa, Zambia, Tanzania, Uganda, Morocco, Côte d'Ivoire, Ethiopia, and Rwanda — two analytical gauges (acute stress and structural vulnerability), every signal explained, no black-box scores.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://apify.com/malmon/african-economic-stress-monitor" rel="noopener noreferrer"&gt;Apify Store&lt;/a&gt;&lt;br&gt;
→ &lt;a href="https://rapidapi.com/malmon-malmon-default/api/african-economic-stress-monitor1" rel="noopener noreferrer"&gt;RapidAPI&lt;/a&gt;&lt;br&gt;
→ &lt;a href="https://malmonde.substack.com" rel="noopener noreferrer"&gt;Free monthly findings newsletter&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;MCP connectors documentation: docs.apify.com/platform/integrations/mcp-connectors&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>apify</category>
      <category>notionchallenge</category>
      <category>fintech</category>
    </item>
    <item>
      <title>There's No Good Programmatic Source for African Macro Data — So I Built One</title>
      <dc:creator>SM</dc:creator>
      <pubDate>Sun, 07 Jun 2026 21:59:56 +0000</pubDate>
      <link>https://dev.to/malmon/theres-no-good-programmatic-source-for-african-macro-data-so-i-built-one-1e4j</link>
      <guid>https://dev.to/malmon/theres-no-good-programmatic-source-for-african-macro-data-so-i-built-one-1e4j</guid>
      <description>&lt;p&gt;If you've ever tried to get structured, current, programmatically accessible macroeconomic data for African countries, you've hit the same wall. Bloomberg has it — behind a $24,000/year terminal. The World Bank has it — with a 6 to 12 month lag. Everything else is either a manually updated HTML table on a central bank website, a PDF, or a JavaScript-rendered dashboard that blocks automated access.&lt;/p&gt;

&lt;p&gt;That gap is disproportionately painful for the people who need it most: developers building African fintech products, researchers studying frontier markets, journalists covering economic crises as they happen, and AI agents that need structured data to reason about the world's fastest-growing economies.&lt;/p&gt;

&lt;p&gt;So I built the infrastructure myself.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;A suite of Apify actors that pull macroeconomic data directly from the issuing source — central banks, national statistics offices, and multilateral institutions — and return clean, normalised, attributed JSON. No aggregators, no lagged annual releases, no guessing where the number came from.&lt;/p&gt;

&lt;p&gt;The suite currently covers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Five regional FX rate actors&lt;/strong&gt; — West Africa, East Africa, Southern Africa, Central Africa, and Maghreb. Daily official exchange rates from the Central Bank of Nigeria, Bank of Ghana, BCEAO (covering 8 WAEMU nations), SARB, Bank of Tanzania, Bank of Uganda, and others. Every record includes the source bank, URL, retrieval timestamp, and licence note. Where rates are derived (the XOF and CVE are both pegged to the euro by treaty), the derivation is flagged explicitly with &lt;code&gt;rateType: "derived"&lt;/code&gt; and &lt;code&gt;sharedRate: true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An African Inflation Monitor&lt;/strong&gt; — monthly CPI data for 7 countries pulled directly from national statistics offices: Ghana (GSS), Morocco (HCP, CC-BY 4.0), Tunisia (INS), South Africa (SARB), Zambia (BoZ), Kenya (CBK), and Uganda (UBOS). Returns CPI index, YoY %, MoM %, and food sub-indices where published. Null fields are honest — no values synthesised where the source doesn't publish them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An Africa Commodity Prices actor&lt;/strong&gt; — monthly prices for 70+ commodities from the World Bank Pink Sheet (CC-BY 4.0), enriched with African trade context. Every record includes YoY and MoM change, an Africa relevance tier (explicit/high/medium/low), and for 15 key commodities, full trade profiles showing top global exporters and importers and Africa's share of world trade. Export-dependence weights sourced from UN Comtrade, stored as a static reference file updated annually.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An African Economic Stress Monitor&lt;/strong&gt; — the analytical layer that sits on top. More on this below.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Design Principles That Differentiate This From a Generic Scraper
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Source attribution on every record.&lt;/strong&gt; Not just "World Bank" — the specific indicator code, the exact URL used for that run, the retrieval timestamp, and the licence note. If you're building a product on this data, you need to know exactly where each number came from and whether you're legally clear to use it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Honest null fields.&lt;/strong&gt; When a central bank doesn't publish a food sub-index, the field is &lt;code&gt;null&lt;/code&gt;, not &lt;code&gt;0&lt;/code&gt; and not omitted. This is more important than it sounds — a downstream model that treats missing data as zero is a different (wrong) model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Partial success by design.&lt;/strong&gt; One source going down doesn't fail the run. You get data from the remaining sources with a warning in the affected record. This is critical for production use where reliability matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Freshness guards.&lt;/strong&gt; Every source has a staleness cutoff. Beyond it, the signal is flagged &lt;code&gt;unavailable&lt;/code&gt; — stale data never silently drives a verdict. The cutoffs are documented: FX 7 days, inflation 90 days, commodity 45 days.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Legal basis documented.&lt;/strong&gt; Each actor's README explains the legal rationale for redistribution — which sources carry explicit CC-BY licences, which rely on the factual data principle (raw numbers aren't copyrightable), which required explicit permission from the institution. This took time to research and it's the part that separates a publishable data product from a scraper that gets taken down.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Stress Monitor: Turning Raw Data Into Analytical Output
&lt;/h2&gt;

&lt;p&gt;The most interesting piece is the analytical layer: &lt;code&gt;african-economic-stress-monitor&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It combines the FX, inflation, and commodity data into a two-gauge stress assessment for 11 African economies:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gauge 1 — Acute stress.&lt;/strong&gt; Five fast-moving signals, each &lt;code&gt;fired / not-fired / unavailable&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FX momentum&lt;/strong&gt; — local currency depreciation vs USD, z-score normalised against the country's own 2-year history. A 5% move means more for a managed peg than a free float.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inflation level&lt;/strong&gt; — YoY CPI above 20%, or above 10% and accelerating month-on-month.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commodity shock&lt;/strong&gt; — the country's primary export commodity move, weighted by export dependence. Cocoa falling 54% is a -15pp terms-of-trade hit for Côte d'Ivoire (cocoa 28% of exports) but a fraction of that for a marginal producer. This turns an exogenous global price into a country-specific signal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real interest rate&lt;/strong&gt; — policy rate minus inflation YoY below -3pp, the classic financial repression / capital flight signal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reserve drawdown&lt;/strong&gt; — official FX reserves falling more than 12.5% over 6 months, the textbook FX crisis predictor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Gauge 2 — Structural vulnerability.&lt;/strong&gt; Six slow-moving structural signals covering debt distress, external adequacy, fiscal position, REER misalignment, banking sector health, and political stability (World Bank WGI). This gauge is kept separate from acute stress deliberately — a country can be calm today yet structurally fragile, or stressed today yet fundamentally sound.&lt;/p&gt;

&lt;p&gt;The verdict is transparent: every record shows exactly which signals fired, the measured value, the threshold, the trend direction, and a plain-language reason. No black-box scores. The continuous stress score (0-100) uses z-score-weighted signal contributions so it differentiates countries the coarse verdict lumps together.&lt;/p&gt;

&lt;p&gt;This week's most analytically interesting finding: Zambia scores 1.5 out of 100 on acute stress (copper up 42%, kwacha appreciating, inflation easing) while carrying a structural vulnerability score of 70.5 (external debt 114% of GNI, debt service 30% of exports). The copper boom is real. The post-default debt overhang hasn't gone anywhere. The tool surfaces both simultaneously.&lt;/p&gt;




&lt;h2&gt;
  
  
  The MCP Angle
&lt;/h2&gt;

&lt;p&gt;Every Apify actor works out of the box as an MCP server via &lt;code&gt;mcp.apify.com&lt;/code&gt;. An AI agent connected to Claude or GPT-4 can call &lt;code&gt;crisis-scan-run&lt;/code&gt; and get back structured, attributed macro stress signals for all 11 countries without any additional integration.&lt;/p&gt;

&lt;p&gt;This is genuinely useful for AI-powered research workflows. The stress monitor returns enough structured context — z-scores, trend directions, compound flags, source attribution, delta versus previous run — that a language model can produce a substantive economic briefing rather than just restating numbers.&lt;/p&gt;




&lt;h2&gt;
  
  
  What It Doesn't Do
&lt;/h2&gt;

&lt;p&gt;Worth being explicit about the limitations:&lt;/p&gt;

&lt;p&gt;Data is delayed official data — not real-time. FX rates are daily but not tick-level. Inflation is monthly. Commodity prices are monthly (World Bank Pink Sheet). This is a research and monitoring tool, not a trading feed.&lt;/p&gt;

&lt;p&gt;Coverage has gaps. Reserves data from the IMF lags by 3-18 months depending on country reporting schedules — a structural limitation of the global data ecosystem that national alternatives don't fix (checked every one). Egypt and Angola remain blocked by WAF/JS walls. Uganda's central bank FX is blocked from datacenter IPs (revenue authority rate used instead, labelled as such).&lt;/p&gt;

&lt;p&gt;Not every country has every signal. The &lt;code&gt;coverageLevel&lt;/code&gt; field (full/partial/minimal/none) and &lt;code&gt;computableCount&lt;/code&gt; tell you exactly what you're working with per country per run.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;p&gt;Pay-per-event on Apify:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Product&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FX snapshot (all countries, one region)&lt;/td&gt;
&lt;td&gt;$0.10 per run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FX historical row&lt;/td&gt;
&lt;td&gt;$0.015 per (country, currency, date)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inflation snapshot (all 7 countries)&lt;/td&gt;
&lt;td&gt;$0.10 per run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inflation historical row&lt;/td&gt;
&lt;td&gt;$0.03 per (country, month)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commodity snapshot (70+ commodities)&lt;/td&gt;
&lt;td&gt;$0.20 per run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commodity historical row&lt;/td&gt;
&lt;td&gt;$0.008 per (commodity, month)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stress monitor — crisis scan (all 11 countries)&lt;/td&gt;
&lt;td&gt;$1.50 flat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stress monitor — country snapshot&lt;/td&gt;
&lt;td&gt;$0.50 per country&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Free tier available on all actors.&lt;/p&gt;




&lt;h2&gt;
  
  
  Get Started
&lt;/h2&gt;

&lt;p&gt;All actors are live on Apify Store. The stress monitor is also available on RapidAPI.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://apify.com/malmon/african-economic-stress-monitor" rel="noopener noreferrer"&gt;African Economic Stress Monitor&lt;/a&gt;&lt;br&gt;
→ &lt;a href="https://apify.com/malmon" rel="noopener noreferrer"&gt;Full actor suite&lt;/a&gt;&lt;br&gt;
→ &lt;a href="https://rapidapi.com/malmon-malmon-default/api/african-economic-stress-monitor1" rel="noopener noreferrer"&gt;RapidAPI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Free monthly newsletter with the findings if you'd rather not run it yourself:&lt;br&gt;
→ &lt;a href="https://malmonde.substack.com/p/african-macro-signal-june-2026" rel="noopener noreferrer"&gt;African Macro Signal on Substack&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Data is sourced from official publications. Attribution is included on every output record. Not investment advice.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>fintech</category>
      <category>datascience</category>
    </item>
    <item>
      <title>25 ans de taux de change officiels en Afrique de l'Ouest — en un seul appel API</title>
      <dc:creator>SM</dc:creator>
      <pubDate>Wed, 03 Jun 2026 18:32:29 +0000</pubDate>
      <link>https://dev.to/malmon/-25-ans-de-taux-de-change-officiels-en-afrique-de-louest-en-un-seul-appel-api-2cl</link>
      <guid>https://dev.to/malmon/-25-ans-de-taux-de-change-officiels-en-afrique-de-louest-en-un-seul-appel-api-2cl</guid>
      <description>&lt;h1&gt;
  
  
  25 ans de taux de change officiels en Afrique de l'Ouest — en un seul appel API
&lt;/h1&gt;

&lt;p&gt;Le naira nigérian s'échangeait à environ 130 dollars en 2015. En 2023, il avait chuté au-delà de 700. Début 2024, il a franchi le seuil de 1 500. Si vous vouliez étudier cette dépréciation de manière programmatique — la visualiser, la modéliser, construire un produit par-dessus — vous aviez une option réaliste : payer Trading Economics 50 $/mois, ou assembler des feuilles de calcul à la main depuis le site de la Banque Centrale du Nigeria.&lt;/p&gt;

&lt;p&gt;Il existe maintenant une meilleure solution.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ce que c'est
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;west-africa-fx-rates&lt;/code&gt; est un actor Apify qui scrape et normalise les taux de change officiels publiés quotidiennement par les banques centrales d'Afrique de l'Ouest. Il couvre &lt;strong&gt;11 pays, 4 devises ISO&lt;/strong&gt;, avec des données historiques remontant à &lt;strong&gt;1996&lt;/strong&gt; — le tout dans un schéma JSON cohérent et unique.&lt;/p&gt;

&lt;p&gt;Couverture :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Nigeria&lt;/strong&gt; (NGN) — taux officiel NFEM de la Banque Centrale du Nigeria&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ghana&lt;/strong&gt; (GHS) — taux médian de la Bank of Ghana&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;8 pays de l'UEMOA&lt;/strong&gt; — Bénin, Burkina Faso, Côte d'Ivoire, Guinée-Bissau, Mali, Niger, Sénégal, Togo (XOF, via BCEAO/BCE)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cap-Vert&lt;/strong&gt; (CVE, via Banco de Cabo Verde/BCE)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Les données proviennent directement des banques centrales émettrices — pas des publications annuelles de la Banque Mondiale avec 6 à 12 mois de décalage, pas d'agrégateurs qui ne précisent pas l'origine du chiffre.&lt;/p&gt;




&lt;h2&gt;
  
  
  Le franc CFA et le mécanisme de parité (utile à comprendre)
&lt;/h2&gt;

&lt;p&gt;Huit des onze pays partagent le franc CFA ouest-africain (XOF), arrimé à l'euro par traité depuis 1999 — à exactement &lt;strong&gt;655,957 XOF pour 1 EUR&lt;/strong&gt;, un taux qui n'a jamais varié depuis sa création et qui est garanti par le Trésor français. Il n'existe pas de taux USD indépendant pour le XOF. Le taux en dollar est dérivé mathématiquement du taux de référence EUR/USD quotidien publié par la Banque Centrale Européenne.&lt;/p&gt;

&lt;p&gt;C'est utile à savoir si vous construisez quoi que ce soit sur les données de change ouest-africaines. Cela signifie :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Les 8 pays de l'UEMOA ont des taux de change identiques — une seule source couvre tous les pays&lt;/li&gt;
&lt;li&gt;Le taux XOF/USD évolue en phase avec EUR/USD, et non avec une politique monétaire locale&lt;/li&gt;
&lt;li&gt;L'escudo cap-verdien (CVE) est également arrimé à l'euro, selon un traité similaire à 110,265 CVE/EUR&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;L'actor gère tout cela de manière transparente : les taux dérivés sont signalés via &lt;code&gt;rateType: "derived"&lt;/code&gt; et &lt;code&gt;sharedRate: true&lt;/code&gt;, mais chaque pays conserve sa propre ligne pour faciliter les filtres et les jointures par code ISO.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deux produits distincts en un seul actor
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Pour les usages quotidiens : le snapshot &lt;code&gt;usd-core&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Un seul appel API, une seule facturation (0,10 $), dernier taux USD pour les 11 pays. Si vous développez une calculatrice de transferts, un moteur de tarification transfrontalier, ou un tableau de bord fintech qui a besoin du taux officiel CBN ou BoG du jour — c'est la solution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"XOF"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-28"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"usdMid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;613.24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rateType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"derived"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sharedRate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"midMethod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"derived_from_ecb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bank"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BCEAO / Banque Centrale Européenne"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.bceao.int"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"retrievedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-28T08:34:12Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"licence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Données factuelles officielles, attribution incluse"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Avec un planning quotidien à 0,10 $ par exécution, le coût est d'environ &lt;strong&gt;2,20 $/mois&lt;/strong&gt; pour les 11 pays.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pour les chercheurs : la série historique&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;C'est le produit qui n'existe nulle part ailleurs à ce prix. Une année complète de taux USD quotidiens pour les 11 pays coûte &lt;strong&gt;11,40 $&lt;/strong&gt;. Dix ans coûtent &lt;strong&gt;114 $&lt;/strong&gt;. La série nigériane remonte à une plage arbitraire ; l'UEMOA et le Ghana remontent respectivement à &lt;strong&gt;1999 et 1996&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Extraire 10 ans d'historique NGN et GHS&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"mode"&lt;/span&gt;: &lt;span class="s2"&gt;"historical"&lt;/span&gt;,
  &lt;span class="s2"&gt;"countries"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"NG"&lt;/span&gt;, &lt;span class="s2"&gt;"GH"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,
  &lt;span class="s2"&gt;"dateFrom"&lt;/span&gt;: &lt;span class="s2"&gt;"2016-01-01"&lt;/span&gt;,
  &lt;span class="s2"&gt;"dateTo"&lt;/span&gt;: &lt;span class="s2"&gt;"2026-01-01"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cette plage temporelle couvre la crise des changes nigériane de 2016, la crise de la dette ghanéenne de 2022 et l'effondrement du cédi qui s'en est suivi, ainsi que la dépréciation du naira de 2023–24. Trois événements monétaires majeurs en Afrique, données quotidiennes propres, un seul appel API.&lt;/p&gt;




&lt;h2&gt;
  
  
  Qualité des données : ce qui a demandé du travail
&lt;/h2&gt;

&lt;p&gt;Quelques fonctionnalités qui ont nécessité un vrai effort d'ingénierie et qui comptent si vous utilisez ces données en production :&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attribution de la source sur chaque enregistrement.&lt;/strong&gt; Nom de la banque, URL, horodatage de récupération, date de référence et note de licence sur chaque ligne de sortie. Vous savez toujours exactement d'où vient un chiffre.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Documentation du &lt;code&gt;midMethod&lt;/code&gt;.&lt;/strong&gt; Le taux médian est calculé différemment selon les banques centrales — certaines le publient directement, d'autres ne publient que les cours acheteur/vendeur. Ce champ documente la méthode utilisée pour chaque source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Garde de fraîcheur.&lt;/strong&gt; Toute source dont la dernière date publiée remonte à plus de 10 jours est silencieusement exclue de l'output plutôt que de retourner des données périmées comme si elles étaient actuelles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tableau de warnings.&lt;/strong&gt; Les anomalies (cours acheteur supérieur au cours vendeur, variation journalière anormalement élevée) sont signalées par enregistrement sans faire planter l'exécution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Récupération indépendante des sources.&lt;/strong&gt; La panne du site d'une banque centrale ne fait pas échouer l'exécution — vous récupérez les données des autres sources avec un avertissement dans l'enregistrement concerné.&lt;/p&gt;




&lt;h2&gt;
  
  
  À qui s'adresse cet actor ?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Développeurs fintech&lt;/strong&gt; — en particulier dans l'écosystème de la diaspora nigériane et ghanéenne. Si vous développez sur Paystack, Flutterwave, ou toute infrastructure de paiement transfrontalier et que vous avez besoin du taux officiel CBN NFEM pour la conformité réglementaire, c'est la source programmatique la plus propre disponible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chercheurs quantitatifs&lt;/strong&gt; — la dynamique des devises africaines est sous-étudiée en partie parce que des données historiques propres et normalisées n'existaient pas. 25+ ans de taux officiels quotidiens pour 11 pays dans un schéma cohérent constitue un jeu de données significatif pour la recherche.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plateformes e-commerce et SaaS transfrontalières&lt;/strong&gt; — les entreprises opérant sur plusieurs marchés d'Afrique de l'Ouest ont besoin d'inputs de change fiables pour leurs modèles de tarification. L'output normalisé sur 11 pays est conçu exactement pour ce cas d'usage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Journalistes et data journalists&lt;/strong&gt; — la trajectoire de dépréciation du naira, le débat sur l'arrimage du franc CFA, l'effondrement du cédi ghanéen lié à la dette souveraine. Ce sont des sujets qui ont besoin de données. Cet actor retourne des données structurées et attribuées, directement exploitables dans un graphique.&lt;/p&gt;




&lt;h2&gt;
  
  
  Base légale
&lt;/h2&gt;

&lt;p&gt;Il est important d'être explicite sur ce point, car les produits de données africains ont un historique compliqué en matière de conditions de redistribution.&lt;/p&gt;

&lt;p&gt;La &lt;strong&gt;CBN Nigeria&lt;/strong&gt; accorde explicitement la permission de copier les données avec attribution — mentionné directement sur leur page de taux de change. La &lt;strong&gt;Bank of Ghana&lt;/strong&gt; et la &lt;strong&gt;BCE&lt;/strong&gt; (source pour XOF et CVE) ne comportent aucune restriction contractuelle de redistribution. L'actor s'appuie sur le principe des données factuelles — le droit d'auteur protège l'expression créative, pas les chiffres bruts — et attribue chaque enregistrement à sa banque émettrice.&lt;/p&gt;

&lt;p&gt;La justification légale complète est documentée dans le README de l'actor. Chaque enregistrement contient la note de licence correspondante dans son bloc &lt;code&gt;source&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tarification
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Prix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Snapshot &lt;code&gt;usd-core&lt;/code&gt; (11 pays)&lt;/td&gt;
&lt;td&gt;0,10 $ par exécution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ligne &lt;code&gt;historical&lt;/code&gt; ou &lt;code&gt;all-currencies&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;0,015 $ par point (pays, devise, date)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Planning quotidien &lt;code&gt;usd-core&lt;/code&gt; : &lt;strong&gt;~2,20 $/mois&lt;/strong&gt;&lt;br&gt;
Un an d'historique, 11 pays : &lt;strong&gt;~11,40 $&lt;/strong&gt;&lt;br&gt;
Dix ans d'historique, 11 pays : &lt;strong&gt;~114 $&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Niveau gratuit disponible pour les nouveaux utilisateurs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ce qui arrive ensuite
&lt;/h2&gt;

&lt;p&gt;Des actors pour l'Afrique de l'Est, l'Afrique australe et l'Afrique du Nord sont en cours de développement, dans le même schéma normalisé. La Gambie, la Guinée, le Libéria et la Sierra Leone sont les membres CEDEAO restants non encore couverts ; la reconnaissance des sources est en cours.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pour commencer
&lt;/h2&gt;

&lt;p&gt;→ &lt;a href="https://apify.com/malmon/west-africa-fx-rates" rel="noopener noreferrer"&gt;API West Africa FX Rates sur l'Apify Store&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Les données proviennent des publications officielles des banques centrales. L'attribution est incluse dans chaque enregistrement. Cet actor n'est ni affilié à ni approuvé par les banques centrales mentionnées. Les données sont fournies à titre informatif et ne doivent pas être utilisées comme seule base pour des décisions financières ou de trading.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>fintech</category>
      <category>datascience</category>
    </item>
    <item>
      <title>25 Years of West African Official FX Rates — Now in One API Call</title>
      <dc:creator>SM</dc:creator>
      <pubDate>Wed, 03 Jun 2026 14:51:18 +0000</pubDate>
      <link>https://dev.to/malmon/25-years-of-west-african-official-fx-rates-now-in-one-api-call-3ig6</link>
      <guid>https://dev.to/malmon/25-years-of-west-african-official-fx-rates-now-in-one-api-call-3ig6</guid>
      <description>&lt;p&gt;The Nigerian naira traded at around 130 to the dollar in 2015. By 2023 it had fallen past 700. By early 2024 it crossed 1,500. If you wanted to study that collapse programmatically — plot it, model it, build a product on top of it — you had one realistic option: pay Trading Economics $50/month or stitch together spreadsheets from the Central Bank of Nigeria's website by hand.&lt;/p&gt;

&lt;p&gt;There's a better way now.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Is
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;west-africa-fx-rates&lt;/code&gt; is an Apify actor that scrapes and normalises official foreign exchange rates published daily by West African central banks. It covers &lt;strong&gt;11 countries, 4 ISO currencies&lt;/strong&gt;, with historical data going back to &lt;strong&gt;1996&lt;/strong&gt; — all in a single, consistent JSON schema.&lt;/p&gt;

&lt;p&gt;Coverage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Nigeria&lt;/strong&gt; (NGN) — Central Bank of Nigeria NFEM official rate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ghana&lt;/strong&gt; (GHS) — Bank of Ghana mid rate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;8 WAEMU nations&lt;/strong&gt; — Benin, Burkina Faso, Côte d'Ivoire, Guinea-Bissau, Mali, Niger, Senegal, Togo (XOF, via BCEAO/ECB)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cape Verde&lt;/strong&gt; (CVE, via Banco de Cabo Verde/ECB)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The data comes directly from the issuing central banks — not from World Bank annual releases with a 6–12 month lag, not from aggregators who don't tell you where the number came from.&lt;/p&gt;




&lt;h2&gt;
  
  
  The WAEMU Angle (Worth Understanding)
&lt;/h2&gt;

&lt;p&gt;Eight of the eleven countries share the West African CFA franc (XOF), which has been pegged to the euro by treaty since 1999 — at exactly &lt;strong&gt;655.957 XOF per EUR&lt;/strong&gt;, a rate that has never changed and is guaranteed by the French Treasury. There is no independently set USD rate for XOF. The USD equivalent is derived mathematically from the ECB's daily EUR/USD reference rate.&lt;/p&gt;

&lt;p&gt;This is actually useful to know if you're building anything on West African FX data. It means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All 8 WAEMU countries have identical FX rates — one published source covers all of them&lt;/li&gt;
&lt;li&gt;The XOF/USD rate moves in lockstep with EUR/USD, not with any local monetary policy&lt;/li&gt;
&lt;li&gt;Cape Verde's escudo (CVE) is also euro-pegged, under a similar treaty at 110.265 CVE/EUR&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The actor handles all of this transparently: derived rates are flagged as &lt;code&gt;rateType: "derived"&lt;/code&gt; and &lt;code&gt;sharedRate: true&lt;/code&gt;, but each country still gets its own row so you can filter and join on ISO country code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Two Different Products in One Actor
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;For daily users: the &lt;code&gt;usd-core&lt;/code&gt; snapshot&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One API call, one charge ($0.10), latest USD rate for all 11 countries. If you're building a remittance calculator, a cross-border pricing engine, or a fintech dashboard that needs today's official CBN or BoG rate — this is it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NG"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NGN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-28"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"usdMid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1571.45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rateType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"published"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"midMethod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"published"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bank"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Central Bank of Nigeria"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.cbn.gov.ng/rates/ExchRateByCurrency.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"retrievedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-28T08:34:12Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"licence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CBN grants explicit permission to copy with attribution"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At $0.10 per run, a daily schedule costs &lt;strong&gt;$2.20/month&lt;/strong&gt; for all 11 countries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For researchers: the historical dataset&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the product that doesn't exist anywhere else at this price. A full year of daily USD rates for all 11 countries costs &lt;strong&gt;$11.40&lt;/strong&gt;. Ten years costs &lt;strong&gt;$114&lt;/strong&gt;. The naira series goes back to an arbitrary range; WAEMU and Ghana go back to &lt;strong&gt;1999 and 1996&lt;/strong&gt; respectively.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Pull 10 years of NGN and GHS history&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"mode"&lt;/span&gt;: &lt;span class="s2"&gt;"historical"&lt;/span&gt;,
  &lt;span class="s2"&gt;"countries"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"NG"&lt;/span&gt;, &lt;span class="s2"&gt;"GH"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,
  &lt;span class="s2"&gt;"dateFrom"&lt;/span&gt;: &lt;span class="s2"&gt;"2016-01-01"&lt;/span&gt;,
  &lt;span class="s2"&gt;"dateTo"&lt;/span&gt;: &lt;span class="s2"&gt;"2026-01-01"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For context: this date range covers the 2016 Nigerian FX crisis, the 2022 Ghana debt crisis and subsequent cedi collapse, and the 2023–24 naira devaluation. Three major African currency events, clean daily data, one API call.&lt;/p&gt;




&lt;h2&gt;
  
  
  Data Quality Features Worth Knowing
&lt;/h2&gt;

&lt;p&gt;A few things that took real engineering effort and matter if you're building on this in production:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source attribution on every record.&lt;/strong&gt; Bank name, URL, retrieval timestamp, as-of date, and licence note on every single output row. You always know exactly where a number came from.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;midMethod&lt;/code&gt; documentation.&lt;/strong&gt; The mid rate is calculated differently by different central banks — some publish it directly, others publish only buy/sell spreads. The field documents which method was used so you can adjust your models accordingly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Freshness guard.&lt;/strong&gt; Any source whose latest published date is more than 10 days old is silently dropped from the output rather than returning stale data as if it were current.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Warnings array.&lt;/strong&gt; Anomalies (buying rate above selling rate, suspiciously large day-over-day moves) are flagged per record without crashing the run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Independent source fetching.&lt;/strong&gt; One central bank's site going down doesn't fail the run — you get data from the remaining sources with a warning in the affected record.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who Is This For?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Fintech developers&lt;/strong&gt; — particularly in the Nigerian and Ghanaian diaspora ecosystem. If you're building on Paystack, Flutterwave, or any cross-border payment infrastructure and you need the official CBN NFEM rate (not the parallel market rate, the &lt;em&gt;official&lt;/em&gt; rate), this is the cleanest programmatic source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quantitative researchers&lt;/strong&gt; — African currency dynamics are under-studied partly because clean, normalised historical data didn't exist. 25+ years of daily official rates for 11 countries in a consistent schema is a meaningful research dataset.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-border e-commerce and SaaS platforms&lt;/strong&gt; — companies operating across multiple West African markets need reliable FX inputs for pricing. The 11-country normalised output is designed for exactly this use case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data journalists&lt;/strong&gt; — the naira devaluation trajectory, the WAEMU peg debate, Ghana's debt-driven cedi collapse. These are stories that need data. This actor returns structured, attributed data you can drop straight into a chart.&lt;/p&gt;




&lt;h2&gt;
  
  
  Legal Basis
&lt;/h2&gt;

&lt;p&gt;This is worth being explicit about, because African data products have a complicated history with redistribution terms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CBN Nigeria&lt;/strong&gt; grants explicit permission to copy data with attribution — stated directly on their exchange rate page. &lt;strong&gt;Bank of Ghana&lt;/strong&gt; and the &lt;strong&gt;ECB&lt;/strong&gt; (source for XOF and CVE) carry no contractual redistribution restriction. The actor relies on the factual-data principle — copyright protects creative expression, not raw numbers — and attributes every record to its issuing bank.&lt;/p&gt;

&lt;p&gt;Full legal rationale is documented in the actor README. Every record carries the relevant licence note in its &lt;code&gt;source&lt;/code&gt; block.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;usd-core&lt;/code&gt; snapshot (all 11 countries)&lt;/td&gt;
&lt;td&gt;$0.10 per run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;historical&lt;/code&gt; or &lt;code&gt;all-currencies&lt;/code&gt; row&lt;/td&gt;
&lt;td&gt;$0.015 per (country, currency, date) point&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Daily &lt;code&gt;usd-core&lt;/code&gt; schedule: &lt;strong&gt;~$2.20/month&lt;/strong&gt;&lt;br&gt;
Full year of history, all 11 countries: &lt;strong&gt;~$11.40&lt;/strong&gt;&lt;br&gt;
Full decade of history, all 11 countries: &lt;strong&gt;~$114&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Free tier available for new users.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Coming
&lt;/h2&gt;

&lt;p&gt;This is the first actor in a planned regional family — East African, Southern African, and North African FX actors will follow in the same normalised schema. Gambia, Guinea, Liberia, and Sierra Leone are the remaining ECOWAS members not yet covered; source recon is underway.&lt;/p&gt;




&lt;h2&gt;
  
  
  Get Started
&lt;/h2&gt;

&lt;p&gt;→ &lt;a href="https://apify.com/malmon/west-africa-fx-rates" rel="noopener noreferrer"&gt;West Africa FX Rates on Apify Store&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Data is sourced from official central bank publications. Attribution is included in every output record. This actor is not affiliated with or endorsed by any of the central banks listed. Data is provided for informational purposes and should not be used as the sole basis for financial or trading decisions.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>datascience</category>
      <category>api</category>
      <category>fintech</category>
    </item>
    <item>
      <title>BRVM Market Data : l'API qui existait enfin pour la bourse d'Afrique de l'Ouest</title>
      <dc:creator>SM</dc:creator>
      <pubDate>Fri, 29 May 2026 12:59:02 +0000</pubDate>
      <link>https://dev.to/malmon/brvm-market-data-lapi-qui-existait-enfin-pour-la-bourse-dafrique-de-louest-41jp</link>
      <guid>https://dev.to/malmon/brvm-market-data-lapi-qui-existait-enfin-pour-la-bourse-dafrique-de-louest-41jp</guid>
      <description>&lt;p&gt;Si vous avez déjà essayé d'accéder aux &lt;strong&gt;données boursières de la BRVM&lt;/strong&gt; de manière programmatique, vous connaissez déjà le problème. Il n'existe pas de terminal Bloomberg pour l'Afrique de l'Ouest. Yahoo Finance couvre Sonatel de temps en temps, quand ça lui convient. Le site de la BRVM lui-même est une page HTML mise à jour manuellement. Et tout développeur qui a cherché une &lt;strong&gt;API fiable pour les données boursières de l'Afrique de l'Ouest&lt;/strong&gt; est reparti les mains vides.&lt;/p&gt;

&lt;p&gt;Jusqu'à maintenant.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;⚠️ Mise à jour (juin 2026) :&lt;/strong&gt; Cet actor a été abandonné. Suite à des échanges avec la BRVM, il s’avère que les licences de redistribution commerciale des données boursières sont tariffiées pour des acheteurs institutionnels et ne sont pas viables pour des développeurs indépendants. Si vous cherchez un accès programmatique aux données financières d’Afrique de l’Ouest, notre &lt;a href="https://apify.com/malmon/west-africa-fx-rates" rel="noopener noreferrer"&gt;API West Africa FX Rates&lt;/a&gt; couvre les taux de change officiels quotidiens pour 11 pays dont les 8 nations de l’UEMOA, avec un historique remontant à 1996 — sans restriction de licence.
&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Qu'est-ce que la BRVM ?
&lt;/h2&gt;

&lt;p&gt;La &lt;strong&gt;Bourse Régionale des Valeurs Mobilières (BRVM)&lt;/strong&gt; est la place boursière régionale qui dessert les huit pays membres de l'UEMOA : la Côte d'Ivoire, le Sénégal, le Burkina Faso, le Bénin, le Mali, le Niger, le Togo et la Guinée-Bissau. Elle regroupe ~47 valeurs cotées, parmi lesquelles certaines des entreprises les plus importantes d'Afrique francophone :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sonatel&lt;/strong&gt; (filiale d'Orange en Afrique de l'Ouest) — Sénégal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ecobank Transnational&lt;/strong&gt; — banque panafricaine&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SOLIBRA&lt;/strong&gt; (filiale de Heineken) — Côte d'Ivoire&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BOAD&lt;/strong&gt; (Banque Ouest Africaine de Développement, obligations)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PALMCI&lt;/strong&gt; (huile de palme) — Côte d'Ivoire&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La BRVM est opérationnelle depuis 1998, ce qui signifie que plus de &lt;strong&gt;25 ans d'historique de cours&lt;/strong&gt; sont aujourd'hui enfermés derrière un site web sans API — jusqu'à maintenant.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ce que couvre l'API
&lt;/h2&gt;

&lt;p&gt;L'&lt;a href="https://apify.com/malmon/brvm-market-data" rel="noopener noreferrer"&gt;API BRVM Market Data&lt;/a&gt; propose cinq modes de données en un seul outil :&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Cours en différé
&lt;/h3&gt;

&lt;p&gt;Les prix actuels des ~47 valeurs cotées, mis à jour avec un décalage de 15 minutes pendant les heures de cotation (lun–ven, 09h00–15h00 GMT). Chaque enregistrement inclut :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dernier cours, clôture précédente, variation en %&lt;/li&gt;
&lt;li&gt;Volume et montant échangé en XOF&lt;/li&gt;
&lt;li&gt;Ticker, ISIN, nom de la société, pays, secteur&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Indices boursiers
&lt;/h3&gt;

&lt;p&gt;Tous les indices BRVM en un seul appel : &lt;strong&gt;BRVM Composite&lt;/strong&gt;, &lt;strong&gt;BRVM 30&lt;/strong&gt;, &lt;strong&gt;BRVM Prestige&lt;/strong&gt;, ainsi que les indices sectoriels disponibles. Indispensable pour suivre le marché dans son ensemble.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Données historiques OHLCV
&lt;/h3&gt;

&lt;p&gt;Cours journaliers open/high/low/close/volume sur plus de 25 ans pour n'importe quelle valeur cotée. C'est ce jeu de données qui n'existait tout simplement nulle part ailleurs dans un format structuré et propre.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Données fondamentales des sociétés
&lt;/h3&gt;

&lt;p&gt;ISIN, secteur, pays, capitalisation boursière, nombre d'actions en circulation et informations de cotation pour chaque entreprise de la cote.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Avis et annonces des émetteurs
&lt;/h3&gt;

&lt;p&gt;Accès structuré aux annonces de la BRVM — publications de résultats, avis de dividendes, gouvernance — avec titre, date et lien direct.&lt;/p&gt;




&lt;h2&gt;
  
  
  À qui s'adresse cette API ?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Développeurs fintech au service de la diaspora africaine
&lt;/h3&gt;

&lt;p&gt;On estime à plus de 3 millions le nombre de ressortissants d'Afrique de l'Ouest vivant en France, en Belgique, au Canada et aux États-Unis, avec une capacité d'investissement réelle dans leurs marchés d'origine. Les applications de type plateforme d'investissement pour la diaspora, outils de transfert avec fonctionnalité d'investissement, ou tableaux de bord de portefeuille ont besoin de ces données — et jusqu'ici n'avaient aucun moyen propre d'y accéder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usage typique :&lt;/strong&gt; interroger l'endpoint des cours 2 à 4 fois par jour pendant les heures de cotation, alimenter un tableau de bord ou envoyer des alertes de prix.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chercheurs et analystes quantitatifs
&lt;/h3&gt;

&lt;p&gt;Les marchés actions d'Afrique de l'Ouest sont très peu étudiés dans la recherche académique en finance. Corrélation avec les marchés mondiaux, effets de change sur les rendements libellés en XOF, rotations sectorielles — rien de tout cela n'a été correctement documenté, faute de données accessibles. 25 ans de données OHLCV journalières pour 47 valeurs représentent un jeu de données significatif pour tout chercheur travaillant sur les marchés frontières.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usage typique :&lt;/strong&gt; un backfill historique unique par valeur, puis des mises à jour quotidiennes. L'historique complet sur 25 ans pour les 47 tickers représente environ 430 000 lignes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agents IA et pipelines automatisés
&lt;/h3&gt;

&lt;p&gt;Les agents de recherche financière, les outils de synthèse de marché basés sur des LLM et les pipelines de reporting automatisés ont de plus en plus besoin de données structurées sur des marchés non couverts par les sources habituelles. L'API BRVM est compatible avec tout système capable d'appeler un endpoint HTTP et de parser du JSON — y compris les frameworks IA comme LangChain, les workflows n8n ou Make.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usage typique :&lt;/strong&gt; exécutions planifiées alimentant une base de données vectorielle ou générant des synthèses quotidiennes du marché.&lt;/p&gt;

&lt;h3&gt;
  
  
  Analystes des marchés de capitaux africains
&lt;/h3&gt;

&lt;p&gt;Les banques d'investissement, fonds de pension et institutions de financement du développement opérant dans l'espace UEMOA (BAD, IFC, Proparco, BOAD) produisent des recherches sur les actions de la zone. Leurs analystes récupèrent aujourd'hui les données manuellement depuis le site de la BRVM. Un accès API structuré économise des heures par rapport.&lt;/p&gt;




&lt;h2&gt;
  
  
  Une note sur la fraîcheur des données
&lt;/h2&gt;

&lt;p&gt;Pour être transparent : il ne s'agit &lt;strong&gt;pas d'un flux en temps réel&lt;/strong&gt;. Les données sont décalées d'environ 15 minutes pendant les heures de cotation, et proviennent des pages publiques de brvm.org. Les snapshots de fin de séance sont les données les plus fiables et les plus utilisées.&lt;/p&gt;

&lt;p&gt;Si vous avez besoin de données tick à tick ou infraseconde, il vous faut un flux direct sous licence auprès de la bourse — ce qui existe mais coûte significativement plus cher et nécessite un accord commercial. Pour la grande majorité des cas d'usage (tableaux de bord, recherche, pipelines IA, alertes), des données décalées de 15 minutes sont amplement suffisantes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Comment l'utiliser
&lt;/h2&gt;

&lt;p&gt;L'API est disponible sur l'Apify Store. Vous pouvez l'appeler via :&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript :&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApifyClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apify-client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;VOTRE_TOKEN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;VOTRE_ACTOR_ID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;quotes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;listItems&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Python :&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;VOTRE_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;VOTRE_ACTOR_ID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quotes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;curl :&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://api.apify.com/v2/acts/VOTRE_ACTOR_ID/runs?token=VOTRE_TOKEN"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"mode": "quotes"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Tarification
&lt;/h2&gt;

&lt;p&gt;L'API utilise une tarification au paiement par événement sur Apify :&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ce que vous obtenez&lt;/th&gt;
&lt;th&gt;Prix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Snapshot complet du marché (~47 cours)&lt;/td&gt;
&lt;td&gt;0,25 $ par exécution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cours d'une valeur spécifique (filtré)&lt;/td&gt;
&lt;td&gt;0,05 $ par ticker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tous les indices&lt;/td&gt;
&lt;td&gt;0,10 $ par exécution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ligne OHLCV historique&lt;/td&gt;
&lt;td&gt;0,01 $ par ligne&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Données fondamentales d'une société&lt;/td&gt;
&lt;td&gt;0,10 $ par société&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Annonce émetteur&lt;/td&gt;
&lt;td&gt;0,01 $ par annonce&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Un tableau de bord interrogeant le marché complet 4 fois par jour représente environ &lt;strong&gt;22 $/mois&lt;/strong&gt;. Un backfill historique complet pour une valeur (25 ans ≈ 6 500 lignes) coûte &lt;strong&gt;65 $&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Un niveau gratuit est disponible pour les nouveaux utilisateurs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pourquoi ça n'existait pas avant
&lt;/h2&gt;

&lt;p&gt;La réponse honnête : la BRVM couvre un univers de cotation relativement restreint sur des marchés que les grands fournisseurs de données mondiaux considèrent comme secondaires. Bloomberg, Refinitiv et FactSet concentrent leurs développements sur les bourses avec des milliers de sociétés cotées et des millions de transactions quotidiennes. Les 47 valeurs de la BRVM et sa liquidité modeste passent sous leur radar.&lt;/p&gt;

&lt;p&gt;Cela crée un manque particulièrement douloureux pour les personnes qui ont précisément besoin de ces données — développeurs fintech africains, investisseurs de la diaspora, chercheurs sur les marchés frontières — et qui n'ont de toute façon pas le budget pour un terminal Bloomberg.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pour commencer
&lt;/h2&gt;

&lt;p&gt;→ &lt;a href="https://apify.com/malmon/brvm-market-data" rel="noopener noreferrer"&gt;API BRVM Market Data sur l'Apify Store&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Service temporairement indisponible&lt;/strong&gt; — une licence de redistribution des données est en cours d'obtention auprès de la BRVM. L'accès sera rétabli prochainement.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Les données proviennent des pages publiques de brvm.org. Tous les prix sont en XOF (franc CFA d'Afrique de l'Ouest). Cette API n'est ni affiliée à la BRVM ni approuvée par elle. Les données sont fournies à titre informatif et ne sont pas adaptées à la prise de décisions de trading automatisé.&lt;/p&gt;

</description>
      <category>api</category>
      <category>data</category>
      <category>programming</category>
      <category>showdev</category>
    </item>
    <item>
      <title>BRVM Market Data: The API That Finally Exists for West Africa's Stock Exchange</title>
      <dc:creator>SM</dc:creator>
      <pubDate>Thu, 28 May 2026 17:30:52 +0000</pubDate>
      <link>https://dev.to/malmon/brvm-market-data-the-api-that-finally-exists-for-west-africas-stock-exchange-547</link>
      <guid>https://dev.to/malmon/brvm-market-data-the-api-that-finally-exists-for-west-africas-stock-exchange-547</guid>
      <description>&lt;p&gt;If you've ever tried to get programmatic access to &lt;strong&gt;BRVM stock data&lt;/strong&gt;, you already know the problem. There's no Bloomberg terminal for West Africa. Yahoo Finance covers Sonatel sometimes, when it feels like it. The BRVM website itself is a manually-updated HTML page. And any developer who's gone looking for a clean, reliable API for &lt;strong&gt;West African equity data&lt;/strong&gt; has come back empty-handed.&lt;/p&gt;

&lt;p&gt;Until now.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Update (June 2026):&lt;/strong&gt; This actor has been discontinued. Following discussions with the BRVM, formal licensing for commercial redistribution of exchange data is priced for institutional buyers and is not viable for independent developers. If you're looking for programmatic access to West African financial data, our &lt;a href="https://apify.com/malmon/west-africa-fx-rates" rel="noopener noreferrer"&gt;West Africa FX Rates API&lt;/a&gt; covers official daily exchange rates for 11 countries including all 8 WAEMU nations, with history back to 1996 — no licensing restrictions.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What Is the BRVM?
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Bourse Régionale des Valeurs Mobilières (BRVM)&lt;/strong&gt; is the regional stock exchange serving the eight WAEMU nations: Côte d'Ivoire, Senegal, Burkina Faso, Benin, Mali, Niger, Togo, and Guinea-Bissau. It lists ~47 equities, including some of West Africa's most significant companies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sonatel&lt;/strong&gt; (Orange's West Africa subsidiary) — Senegal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ecobank Transnational&lt;/strong&gt; — Pan-African banking&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SOLIBRA&lt;/strong&gt; (Heineken subsidiary) — Côte d'Ivoire&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BOAD&lt;/strong&gt; (West African Development Bank bonds)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PALMCI&lt;/strong&gt; (palm oil) — Côte d'Ivoire&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The BRVM has been operating since 1998, which means there's over &lt;strong&gt;25 years of price history&lt;/strong&gt; sitting behind a website with no API — until now.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the API Covers
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://apify.com" rel="noopener noreferrer"&gt;BRVM Market Data API&lt;/a&gt; provides five data modes in a single actor:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Delayed Quotes
&lt;/h3&gt;

&lt;p&gt;Current prices for all ~47 listed equities, updated with a 15-minute delay during trading hours (Mon–Fri, 09:00–15:00 GMT). Each record includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Last price, previous close, change %&lt;/li&gt;
&lt;li&gt;Volume and turnover in XOF&lt;/li&gt;
&lt;li&gt;Ticker, ISIN, company name, country, sector&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Market Indices
&lt;/h3&gt;

&lt;p&gt;All BRVM indices in one call: &lt;strong&gt;BRVM Composite&lt;/strong&gt;, &lt;strong&gt;BRVM 30&lt;/strong&gt;, &lt;strong&gt;BRVM Prestige&lt;/strong&gt;, and available sector indices. Essential for anyone tracking the market at a macro level.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Historical OHLCV Data
&lt;/h3&gt;

&lt;p&gt;Daily open/high/low/close/volume going back over 25 years for any listed equity. This is the dataset that simply didn't exist anywhere in a clean, structured format before.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Company Fundamentals
&lt;/h3&gt;

&lt;p&gt;ISIN, sector, country, market cap, shares outstanding, and listing details for every company on the exchange.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Issuer Announcements
&lt;/h3&gt;

&lt;p&gt;Structured access to BRVM issuer announcements — earnings releases, dividend notices, governance updates — with title, date, and direct link.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who Is This For?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Fintech developers building for the African diaspora
&lt;/h3&gt;

&lt;p&gt;There are an estimated 3 million West Africans living in Europe and North America with money to invest in their home markets. Apps like diaspora investing platforms, remittance tools with investment features, or portfolio trackers for dual-market investors all need this data — and until now had no clean way to get it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Typical usage:&lt;/strong&gt; Poll the quotes endpoint 2–4 times per day during trading hours. Feed results into a dashboard or push notifications for price alerts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quantitative researchers
&lt;/h3&gt;

&lt;p&gt;West African equity markets are significantly under-researched in academic finance. Correlation with global markets, currency effects on XOF-denominated returns, sector rotation patterns — none of this has been properly studied because the data wasn't accessible. 25 years of daily OHLCV for 47 tickers is a meaningful dataset for any researcher studying frontier markets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Typical usage:&lt;/strong&gt; One-time historical backfill per ticker, then daily updates. A full 25-year history for all 47 tickers is roughly 430,000 rows.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI agents and automated pipelines
&lt;/h3&gt;

&lt;p&gt;Financial research agents, LLM-powered market summary tools, and automated reporting pipelines increasingly need structured data from non-standard markets. The BRVM API is compatible with any system that can call an HTTP endpoint and parse JSON — including AI frameworks like LangChain, n8n workflows, and Make automations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Typical usage:&lt;/strong&gt; Scheduled actor runs feeding a vector database or generating daily market briefings.&lt;/p&gt;

&lt;h3&gt;
  
  
  African capital market analysts
&lt;/h3&gt;

&lt;p&gt;Investment banks, pension funds, and development finance institutions operating in WAEMU (AfDB, IFC, Proparco) produce research on West African equities. Their analysts currently pull data manually from the BRVM website. Structured API access saves hours per report.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Note on Data Freshness
&lt;/h2&gt;

&lt;p&gt;To be transparent: this is &lt;strong&gt;not a real-time feed&lt;/strong&gt;. Data is delayed by approximately 15 minutes during trading hours, sourced from public pages on brvm.org. End-of-day snapshots are the most reliable and most commonly used data point.&lt;/p&gt;

&lt;p&gt;If you need tick-level or sub-second data, you need a licensed direct feed from the exchange — which exists but costs significantly more and requires a commercial agreement. For the vast majority of use cases (dashboards, research, AI pipelines, alerts), 15-minute delayed data is entirely sufficient.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Use It
&lt;/h2&gt;

&lt;p&gt;The API is available on Apify Store. You can call it via:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApifyClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apify-client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_TOKEN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_ACTOR_ID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;quotes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;listItems&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Python:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_ACTOR_ID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quotes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;curl:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://api.apify.com/v2/acts/YOUR_ACTOR_ID/runs?token=YOUR_TOKEN"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"mode": "quotes"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;p&gt;The API uses pay-per-event pricing on Apify:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What you get&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Full market snapshot (all ~47 quotes)&lt;/td&gt;
&lt;td&gt;$0.25 per run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Single ticker quote (filtered)&lt;/td&gt;
&lt;td&gt;$0.05 per ticker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;All indices&lt;/td&gt;
&lt;td&gt;$0.10 per run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Historical OHLCV row&lt;/td&gt;
&lt;td&gt;$0.01 per row&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Company fundamentals&lt;/td&gt;
&lt;td&gt;$0.10 per company&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Announcement&lt;/td&gt;
&lt;td&gt;$0.01 each&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A dashboard polling the full market 4× per day costs roughly &lt;strong&gt;$22/month&lt;/strong&gt;. A one-time full historical backfill for a single ticker (25 years ≈ 6,500 rows) costs &lt;strong&gt;$65&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There's a free discovery tier for first-time users.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Didn't Exist Before
&lt;/h2&gt;

&lt;p&gt;The honest answer: the BRVM serves a relatively small listed universe across markets that global data vendors consider low-priority. Bloomberg, Refinitiv, and FactSet focus their engineering on exchanges with thousands of listed companies and millions of daily trades. The BRVM's 47 stocks and modest liquidity fall below their threshold for dedicated coverage.&lt;/p&gt;

&lt;p&gt;That leaves a gap that's disproportionately painful for the people who actually need the data — African fintech developers, diaspora investors, frontier market researchers — none of whom have the budget for a Bloomberg terminal anyway.&lt;/p&gt;




&lt;h2&gt;
  
  
  Get Started
&lt;/h2&gt;

&lt;p&gt;→ &lt;a href="https://apify.com/malmon/brvm-market-data" rel="noopener noreferrer"&gt;BRVM Market Data API on Apify Store&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Service temporarily unavailable&lt;/strong&gt; — a data-redistribution license is being arranged with the BRVM. Access will be restored soon.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Data is sourced from public pages on brvm.org. All prices are in XOF (West African CFA franc). This API is not affiliated with or endorsed by the BRVM. Data is provided for informational purposes and is not suitable for automated trading decisions.&lt;/p&gt;

</description>
      <category>api</category>
      <category>data</category>
      <category>news</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
