<?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: Vadim Zabin</title>
    <description>The latest articles on DEV Community by Vadim Zabin (@truenorthdata).</description>
    <link>https://dev.to/truenorthdata</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%2F4014152%2F6fecd1d6-be70-4cd6-b16b-30de34c218b6.jpg</url>
      <title>DEV Community: Vadim Zabin</title>
      <link>https://dev.to/truenorthdata</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/truenorthdata"/>
    <language>en</language>
    <item>
      <title>Building Permit Leads in Canada: Toronto, Vancouver, Calgary, Edmonton (2026)</title>
      <dc:creator>Vadim Zabin</dc:creator>
      <pubDate>Sat, 04 Jul 2026 04:31:59 +0000</pubDate>
      <link>https://dev.to/truenorthdata/building-permit-leads-in-canada-toronto-vancouver-calgary-edmonton-2026-47ip</link>
      <guid>https://dev.to/truenorthdata/building-permit-leads-in-canada-toronto-vancouver-calgary-edmonton-2026-47ip</guid>
      <description>&lt;p&gt;&lt;em&gt;Every week, thousands of homeowners and businesses in Toronto, Vancouver, Calgary and Edmonton get building permits approved. Each permit is a public record — an address where someone is about to spend money on construction. Here is how to turn municipal open data into a contractor lead list, with working code and a no-code option.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why building permits are the best contractor leads
&lt;/h2&gt;

&lt;p&gt;A building permit is the strongest buying signal in the trades industry:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;roofing permit&lt;/strong&gt; means a homeowner is replacing a roof — and probably comparing quotes right now.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;new single-family dwelling&lt;/strong&gt; means a general contractor will need HVAC, electrical, plumbing, landscaping and materials for the next 12 months.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;demolition permit&lt;/strong&gt; often precedes a rebuild — early signal for every trade.&lt;/li&gt;
&lt;li&gt;High &lt;strong&gt;project values&lt;/strong&gt; identify commercial jobs worth a supplier's attention.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unlike purchased lead lists, permits are fresh, verified by the city, and include the address, the work description and often the contractor and estimated project cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the data lives (and what's actually open)
&lt;/h2&gt;

&lt;p&gt;Canada's four largest construction markets all publish permit data through official open-data programs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;City&lt;/th&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Update cadence&lt;/th&gt;
&lt;th&gt;Contractor name&lt;/th&gt;
&lt;th&gt;Project value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Toronto&lt;/td&gt;
&lt;td&gt;CKAN datastore&lt;/td&gt;
&lt;td&gt;Daily&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vancouver&lt;/td&gt;
&lt;td&gt;Opendatasoft API&lt;/td&gt;
&lt;td&gt;Frequent&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Calgary&lt;/td&gt;
&lt;td&gt;Socrata (SODA)&lt;/td&gt;
&lt;td&gt;Weekly-ish (can lag)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Edmonton&lt;/td&gt;
&lt;td&gt;Socrata (SODA)&lt;/td&gt;
&lt;td&gt;Frequent&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All four are published for reuse under open-data licences — no scraping grey zones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick example: pulling Calgary permits with Python
&lt;/h2&gt;

&lt;p&gt;Calgary's dataset is the richest — it includes contractor names, estimated project cost and square footage:&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;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://data.calgary.ca/resource/c2es-76ed.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;params&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;$limit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$order&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;issueddate DESC&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;$where&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;issueddate &amp;gt;= &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2026-05-01T00:00:00.000&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&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;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;5&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;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;issueddate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&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;|&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;contractorname&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;|&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;estprojectcost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vancouver uses the Opendatasoft Explore API, Toronto a CKAN datastore, Edmonton another Socrata dataset — each with different field names, pagination rules and quirks (Toronto's columns are UPPER_SNAKE, Calgary's dataset can lag a few weeks, Vancouver caps deep pagination).&lt;/p&gt;

&lt;h2&gt;
  
  
  The hard part: one schema, four cities
&lt;/h2&gt;

&lt;p&gt;If you want leads across cities, you need to normalize four different schemas into one: permit id, type of work, description, address, contractor, project value, dates. Then deduplicate, filter by keyword ("roof", "solar", "pool"), by minimum project value, and set up a scheduler so your sales team gets only &lt;em&gt;new&lt;/em&gt; permits every Monday — not the same list twice.&lt;/p&gt;

&lt;p&gt;That's a real engineering project. Here is the shortcut.&lt;/p&gt;

&lt;h2&gt;
  
  
  The no-code option: one API for all four cities
&lt;/h2&gt;

&lt;p&gt;I maintain an Apify Actor that does the normalization, filtering and monitoring:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://apify.com/truenorthdata/canada-building-permits" rel="noopener noreferrer"&gt;Building Permits Canada — Toronto, Vancouver, Calgary, Edmonton&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Example — roofing leads in Calgary, projects worth $20k+:&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;"cities"&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;"calgary"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"issuedWithinDays"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"keywords"&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;"roof"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"minProjectValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"onlyNewSinceLastRun"&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="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;Output — one clean record per permit:&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;"permitId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BP2026-11223"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Calgary"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"workClass"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alteration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"REROOF, SOLAR PV INSTALLATION"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"248 SADDLELAKE DR NE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"contractor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ACME ROOFING LTD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"projectValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;46089.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"issuedDate"&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-06-28"&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;onlyNewSinceLastRun&lt;/code&gt; flag turns it into a monitoring feed: schedule it weekly on Apify, connect email or Slack, and every Monday you get only the permits that didn't exist last week. Pricing is per record ($5 per 1,000) — a weekly lead list for a single trade in one city typically costs cents.&lt;/p&gt;

&lt;p&gt;Export to Excel/CSV works out of the box, so the list can go straight to a sales team or a CRM import.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use cases beyond lead gen
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Building-material suppliers&lt;/strong&gt;: target active job sites by neighbourhood and project value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Equipment rental&lt;/strong&gt;: new construction permits = upcoming demand for machinery.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PropTech and researchers&lt;/strong&gt;: construction activity by community, month and value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real estate investors&lt;/strong&gt;: renovation clusters signal appreciating streets.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is this legal?&lt;/strong&gt; Yes — all four cities publish permit data through official open-data programs licensed for reuse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does it include homeowner contact info?&lt;/strong&gt; No. Records contain the address, work description, and (in Calgary/Vancouver) the applicant or contractor business name — not personal phone numbers or emails. Door-knocking and direct mail are the standard plays.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which cities are next?&lt;/strong&gt; Ottawa and Montréal are on the roadmap — open an issue on &lt;a href="https://apify.com/truenorthdata/canada-building-permits" rel="noopener noreferrer"&gt;the Actor page&lt;/a&gt; to vote.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I try it free?&lt;/strong&gt; Apify's free tier includes $5 in credits — roughly a thousand permit records.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Building something with Canadian public data — permits, tenders, registries? I write about turning open data into products. Questions welcome in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>api</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Get Canadian Government Tenders by API (2026 Guide)</title>
      <dc:creator>Vadim Zabin</dc:creator>
      <pubDate>Sat, 04 Jul 2026 04:21:05 +0000</pubDate>
      <link>https://dev.to/truenorthdata/how-to-get-canadian-government-tenders-by-api-2026-guide-4oa1</link>
      <guid>https://dev.to/truenorthdata/how-to-get-canadian-government-tenders-by-api-2026-guide-4oa1</guid>
      <description>&lt;p&gt;&lt;em&gt;Canada's governments buy over $200 billion in goods and services every year. Every tender is public — but the data is scattered across a dozen portals. Here is how to get it programmatically in 2026: every official source, their APIs and formats, and a working setup for automated monitoring.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: one country, a dozen portals
&lt;/h2&gt;

&lt;p&gt;If you sell to the Canadian public sector — or build tools for companies that do — you quickly discover there is no single API for Canadian tenders:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Federal tenders&lt;/strong&gt; live on &lt;a href="https://canadabuys.canada.ca/" rel="noopener noreferrer"&gt;CanadaBuys&lt;/a&gt;, the Government of Canada's system of record.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Québec&lt;/strong&gt; runs its own system, SEAO, covering ministries, municipalities, schools and hospitals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nova Scotia&lt;/strong&gt; publishes procurement data through its open-data portal.&lt;/li&gt;
&lt;li&gt;Ontario, BC, Alberta and Saskatchewan run separate portals (Jaggaer, Ivalua and custom systems) with no public data feeds.&lt;/li&gt;
&lt;li&gt;Commercial aggregators like MERX charge subscription fees for a unified view.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The good news: the three biggest open sources publish machine-readable data under open licences. Let's walk through them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source 1: CanadaBuys (federal) — open CSV, updated every 2 hours
&lt;/h2&gt;

&lt;p&gt;CanadaBuys publishes &lt;a href="https://canadabuys.canada.ca/en/procurement-and-contracting-data" rel="noopener noreferrer"&gt;official open data files&lt;/a&gt; under the Open Government Licence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;New tender notices&lt;/strong&gt; — refreshed every 2 hours during business hours&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open tender notices&lt;/strong&gt; — all tenders currently accepting bids, refreshed daily&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Award notices and contract history&lt;/strong&gt; — who won what&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The files are bilingual CSVs with a documented data dictionary. A quick look with Python:&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;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://canadabuys.canada.ca/opendata/pub/openTenderNotice-ouvertAvisAppelOffres.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&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;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;# thousands of open tenders
&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;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;    &lt;span class="c1"&gt;# bilingual column names like "title-titre-eng"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Watch out for three things: bilingual column names (&lt;code&gt;title-titre-eng&lt;/code&gt;, &lt;code&gt;tenderClosingDate-appelOffresdateCloture&lt;/code&gt;), occasional schema drift, and multi-category rows (&lt;code&gt;procurementCategory&lt;/code&gt; can be &lt;code&gt;CNST,SRV&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Source 2: SEAO (Québec) — OCDS JSON on Données Québec
&lt;/h2&gt;

&lt;p&gt;Québec publishes SEAO data as weekly JSON files in the &lt;a href="https://www.donneesquebec.ca/recherche/dataset/systeme-electronique-dappel-doffres-seao" rel="noopener noreferrer"&gt;Open Contracting Data Standard&lt;/a&gt; (CC-BY). Each weekly file contains tender notices and awarded contracts as OCDS "releases" — with buyer, UNSPSC classification, award values in CAD.&lt;/p&gt;

&lt;p&gt;The catch: file URLs change weekly, so you must query the CKAN catalogue API first to find the latest resources, then download and parse each file. Titles are French-only.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source 3: Nova Scotia — awarded tenders on Socrata
&lt;/h2&gt;

&lt;p&gt;Nova Scotia publishes &lt;a href="https://data.novascotia.ca/Procurement-and-Contracts/Awarded-Public-Tenders/m6ps-8j6u" rel="noopener noreferrer"&gt;awarded public tenders&lt;/a&gt; — government plus municipalities, universities, school boards and health authorities — with the winning vendor and award amount:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;https://data.novascotia.ca/resource/m6ps-8j6u.json?$limit=100&amp;amp;$order=awarded_date DESC
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is market intelligence rather than live opportunities: who wins contracts in your category, and at what price.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about Ontario, BC, Alberta?
&lt;/h2&gt;

&lt;p&gt;As of mid-2026, none of them publish open data feeds for current tenders. Their portals are JavaScript applications that require browser-level scraping. If you only need federal + Québec + Nova Scotia, you can skip that complexity entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  The DIY reality check
&lt;/h2&gt;

&lt;p&gt;Building this yourself means: three different formats (CSV, OCDS JSON, Socrata), bilingual normalization, weekly-changing URLs, schema drift, deduplication across sources, and a scheduler with failure alerts. It's a fun weekend project — and then an unpaid maintenance job forever, because government portals change things without notice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 5-minute alternative: a ready-made API
&lt;/h2&gt;

&lt;p&gt;I maintain an Apify Actor that does all of the above and returns one normalized feed:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://apify.com/truenorthdata/canada-tenders-scraper" rel="noopener noreferrer"&gt;Canada Tenders Scraper — Federal, Quebec, Nova Scotia&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One schema across all three sources: title (EN/FR), buyer, category, UNSPSC, closing date, contact, notice URL, award value where applicable. Example input:&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;"sources"&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;"canadabuys"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"seao"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"keywords"&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;"software"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cloud"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"informatique"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"closingWithinDays"&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;"onlyNewSinceLastRun"&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;"maxResults"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;500&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;onlyNewSinceLastRun&lt;/code&gt; flag is the key feature for monitoring: schedule the Actor daily, and each run returns only tenders you haven't seen before. Connect it to email or Slack through Apify integrations and you have a tender-alert system for a few dollars a month — no subscription, you pay per record ($4 per 1,000).&lt;/p&gt;

&lt;p&gt;Calling it from your own code:&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_APIFY_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;truenorthdata/canada-tenders-scraper&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;keywords&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;construction&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;roofing&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;closingWithinDays&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxResults&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&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;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;titleEn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;titleFr&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;|&lt;/span&gt;&lt;span class="sh"&gt;"&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;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;closingDate&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;|&lt;/span&gt;&lt;span class="sh"&gt;"&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;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;buyerOrg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything runs on official open data — no proxies, no scraping grey zones, no logins.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is scraping Canadian government tenders legal?&lt;/strong&gt; The sources above are official open-data programs published for reuse (Open Government Licence – Canada, CC-BY Québec). Yes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How fresh is the data?&lt;/strong&gt; Federal: every 2 hours for new tenders. Québec: weekly files. Nova Scotia: monthly award updates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I get tenders for a specific province only?&lt;/strong&gt; Yes — filter by region (e.g. "Ontario") on federal data, or select the Québec/Nova Scotia sources directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the cheapest way to try it?&lt;/strong&gt; Apify's free tier includes $5 of credit — enough for ~1,000 tender records to see if the data fits your use case.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Questions or a source you'd like covered (Ontario? BC? municipal portals?) — open an issue on &lt;a href="https://apify.com/truenorthdata/canada-tenders-scraper" rel="noopener noreferrer"&gt;the Actor page&lt;/a&gt; and I'll take a look.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>tutorial</category>
      <category>python</category>
      <category>datascience</category>
    </item>
  </channel>
</rss>
