<?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: Harman Singh</title>
    <description>The latest articles on DEV Community by Harman Singh (@harrymakez).</description>
    <link>https://dev.to/harrymakez</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%2F4138262%2F1be03496-b769-4f67-8921-d3f232964a27.png</url>
      <title>DEV Community: Harman Singh</title>
      <link>https://dev.to/harrymakez</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/harrymakez"/>
    <language>en</language>
    <item>
      <title>An open JSON index of Toronto resale prices, with curl and jq</title>
      <dc:creator>Harman Singh</dc:creator>
      <pubDate>Tue, 29 Sep 2026 10:54:30 +0000</pubDate>
      <link>https://dev.to/harrymakez/an-open-json-index-of-toronto-resale-prices-with-curl-and-jq-c15</link>
      <guid>https://dev.to/harrymakez/an-open-json-index-of-toronto-resale-prices-with-curl-and-jq-c15</guid>
      <description>&lt;p&gt;On 27 September 2026, 4,629 pieces were live at the two Toronto boutiques we list, with a median asking price of $400 in Canadian dollars. That reading is a public JSON file you can fetch with one curl.&lt;/p&gt;

&lt;p&gt;We build shopwithin.co, which lists what is in stock at independent luxury boutiques. These are our notes on the price index we publish over those shelves: what it counts, how the median works, when it is counted, and how to query it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short answer
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Page:&lt;/strong&gt; &lt;a href="https://shopwithin.co/index/toronto-resale" rel="noopener noreferrer"&gt;Toronto Resale Price Index&lt;/a&gt;, with a schema.org &lt;code&gt;Dataset&lt;/code&gt; entity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File:&lt;/strong&gt; &lt;a href="https://shopwithin.co/index/toronto-resale.json" rel="noopener noreferrer"&gt;the JSON distribution&lt;/a&gt;, CC BY 4.0, CORS open to any origin&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Counted:&lt;/strong&gt; 4,629 pieces, 3,699 at Utopia and 930 at WDLT117&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prices:&lt;/strong&gt; $10 to $21,000 CAD, median $400&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pre owned:&lt;/strong&gt; 1,379 pieces carry a wear grade, 30 percent, median $400&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rows:&lt;/strong&gt; 20 segments, 15 of them sneaker models&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every number here was read with curl on 27 September 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it counts
&lt;/h2&gt;

&lt;p&gt;The index walks every live listing at the Toronto boutiques we list. Each price is the asking price the boutique set, unchanged.&lt;/p&gt;

&lt;p&gt;A piece priced in another currency is dropped from every number, and the file says how many. On 27 September, &lt;code&gt;counted.skipped&lt;/code&gt; was 0.&lt;/p&gt;

&lt;p&gt;Condition comes from the wear grade the boutique itself wrote: pre owned, lightly used or worn once. Since 26 September 2026, "vintage" in a title no longer counts as a grade, because boutiques use it for a wash or an era.&lt;/p&gt;

&lt;p&gt;Sold prices are not in it. We do not hold them, so we do not estimate them. These are asking prices, and the method text inside the JSON says so in plain words.&lt;/p&gt;

&lt;p&gt;One finding surprised us. Only 1 of the 4,629 pieces states outright that it is new, and 3,249 state no condition at all. So the file carries &lt;code&gt;statedNewMedian: null&lt;/code&gt; instead of a guess, and the page says why.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the median is computed
&lt;/h2&gt;

&lt;p&gt;The median is the lower of the two middle prices, which statisticians call the lower median. It is never an average. This is the whole function, shared by the page, the JSON route, the monthly snapshot and the tests.&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="c1"&gt;// app/lib/designer-prices.ts, with line breaks added&lt;/span&gt;
&lt;span class="cm"&gt;/**
 * Low, median and high over a set of asking prices. The median is the lower
 * of the two middle prices on an even count, so every number a price page
 * prints is a price some piece is actually asking.
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;bandOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prices&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="nx"&gt;PriceBand&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sorted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;prices&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isFinite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&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;sorted&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;low&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sorted&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="na"&gt;median&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;high&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Give it &lt;code&gt;[300, 350, 400, 450]&lt;/code&gt; and it returns 350, not 375. We chose that on purpose.&lt;/p&gt;

&lt;p&gt;Every median in the file is a price some piece on a Toronto shelf is asking today. A journalist can quote it and a reader can go find that piece.&lt;/p&gt;

&lt;p&gt;Two floors keep thin rows out. A segment needs 10 live pieces before it gets a row at all. The pre owned and stated new medians each need 3 pieces on their side.&lt;/p&gt;

&lt;p&gt;You can see the second floor in the live file. Yeezy Slide had 12 pieces on 27 September, so it gets a row. Only 1 of them carried a wear grade, so its &lt;code&gt;resaleMedian&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  When it is counted
&lt;/h2&gt;

&lt;p&gt;Nothing is precomputed into a table. The route reads the live catalog, runs the arithmetic, and lets the CDN share each response for ten minutes.&lt;/p&gt;

&lt;p&gt;Each server instance holds its own count in memory for up to an hour, then reads the catalog again on the next request. So &lt;code&gt;countedAt&lt;/code&gt; matters.&lt;/p&gt;

&lt;p&gt;At 07:17 UTC on 27 September, the copy the CDN held said it was counted at 06:28 UTC, and its &lt;code&gt;age&lt;/code&gt; header said 523 seconds. A read that missed the cache said 07:07 UTC.&lt;/p&gt;

&lt;p&gt;The HTML page's &lt;code&gt;dateModified&lt;/code&gt; said 06:28 UTC. All three said 4,629 pieces.&lt;/p&gt;

&lt;p&gt;The series is separate. A script appends one row a month to a JSON file committed to the repo, and a page render never writes it.&lt;/p&gt;

&lt;p&gt;A second run in the same month replaces that month's row. On 27 September the &lt;code&gt;history&lt;/code&gt; array held one row: September 2026, 4,700 pieces, median $400.&lt;/p&gt;

&lt;p&gt;The route refuses to publish a partial count. If the latest read missed part of any boutique's catalog, it falls back to the last whole count this instance took in the past 24 hours.&lt;/p&gt;

&lt;p&gt;With no whole count on hand, it answers 503 with &lt;code&gt;no-store&lt;/code&gt;. It never answers 200 with a small number, and never 404.&lt;/p&gt;

&lt;p&gt;This rule came from a real bug. A sync blip once read only a small slice of the catalog. The JSON published that count under the shared cache header, and the CDN went on serving it after the catalog was whole again.&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="c1"&gt;// app/routes/index_.toronto-resale[.]json.tsx, lightly trimmed and reformatted&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;census&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;wholeCensusOrNull&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// a whole reading, or null&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;census&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nf"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// 503, retry after 10 s, cache-control: no-store&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;resolveResaleIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;census&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SEO_CITY&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nf"&gt;dark&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;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;resaleIndexJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;resaleIndexHistory&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&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;body&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n`&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content-type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json; charset=utf-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// "public, max-age=0, s-maxage=600, stale-while-revalidate=86400"&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cache-control&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SHARED_PRICE_CACHE_CONTROL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;https://creativecommons.org/licenses/by/4.0/&amp;gt;; rel="license"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;access-control-allow-origin&lt;/span&gt;&lt;span class="dl"&gt;"&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="p"&gt;},&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;p&gt;The 404 rule is about search. A 404 tells Google to drop a URL, and a catalog that failed to answer is not an empty one. A 503 with &lt;code&gt;retry-after&lt;/code&gt; asks the crawler to come back.&lt;/p&gt;

&lt;p&gt;The licence travels in a &lt;code&gt;Link&lt;/code&gt; header, so a script that never sees the page still gets it.&lt;/p&gt;

&lt;p&gt;One trap if you check the headers yourself. The CDN keeps &lt;code&gt;s-maxage&lt;/code&gt; for itself, so a GET on 27 September showed only &lt;code&gt;public, max-age=0&lt;/code&gt;. The &lt;code&gt;age&lt;/code&gt; and &lt;code&gt;x-vercel-cache: HIT&lt;/code&gt; headers are what tell you a shared copy answered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using it with curl and jq
&lt;/h2&gt;

&lt;p&gt;These ran against the live file on 27 September 2026. The output is what came back, with tabs shown as spaces.&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;# The headline numbers, and when they were counted&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://shopwithin.co/index/toronto-resale.json &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'{countedAt, pieces: .counted.pieces, median: .counted.median, resaleShare: .counted.resaleShare}'&lt;/span&gt;
&lt;span class="c"&gt;# {"countedAt":"2026-09-27T06:28:15.861Z","pieces":4629,"median":400,"resaleShare":30}&lt;/span&gt;

&lt;span class="c"&gt;# Sneaker models, deepest first: label, pieces, median, pre owned median&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://shopwithin.co/index/toronto-resale.json &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.segments[] | select(.parent == "sneakers") | [.label, .pieces, .median, .resaleMedian] | @tsv'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-4&lt;/span&gt;
&lt;span class="c"&gt;# Jordan 1      241  350  300&lt;/span&gt;
&lt;span class="c"&gt;# Jordan 4      134  350  300&lt;/span&gt;
&lt;span class="c"&gt;# Travis Scott  132  450  1000&lt;/span&gt;
&lt;span class="c"&gt;# Kobe          107  400  350&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Travis Scott row is the one to read slowly. Its 57 pre owned pieces ask a median of $1,000, while all 132 ask a median of $450. A wear grade does not always mean a lower price.&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;# Which rows link to a page on the site&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://shopwithin.co/index/toronto-resale.json &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'[.segments[] | select(.shelf != null) | .label]'&lt;/span&gt;
&lt;span class="c"&gt;# ["Sneakers","Jordan 1","Jordan 4","Chrome Hearts","Supreme"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five of the 20 rows carry a &lt;code&gt;shelf&lt;/code&gt; URL, such as &lt;code&gt;/shop/toronto/sneakers/jordan-1&lt;/code&gt;. The other 15 leave the &lt;code&gt;shelf&lt;/code&gt; key out entirely. jq reads a missing key as &lt;code&gt;null&lt;/code&gt;, so the filter works either way.&lt;/p&gt;

&lt;p&gt;A sneaker model links only where the site has a page for that model. So Travis Scott and Kobe get no &lt;code&gt;shelf&lt;/code&gt; key, not a link to the nearest shelf.&lt;/p&gt;

&lt;p&gt;Drift against the stored month is one more filter. &lt;code&gt;jq '.counted.pieces - .history[-1].pieces'&lt;/code&gt; returned -71 on 27 September, from 4,700 in the September row to 4,629 live.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we still owe
&lt;/h2&gt;

&lt;p&gt;Nothing schedules the monthly script yet. We run it by hand.&lt;/p&gt;

&lt;p&gt;While checking this post on 27 September, we found that script still calls a catalog read the server module no longer exports. The October row needs that one line fixed first.&lt;/p&gt;

&lt;p&gt;The lesson is general. The script is plain &lt;code&gt;.mjs&lt;/code&gt; and loads the TypeScript modules through Vite's &lt;code&gt;ssrLoadModule&lt;/code&gt;. Those exports come back untyped, so the type checker never saw the stale name.&lt;/p&gt;

&lt;p&gt;Two boutiques is a small sample. The file names both and their counts, so you can weigh it yourself.&lt;/p&gt;

&lt;p&gt;If you use the numbers, cite them the way the file's own &lt;code&gt;citation&lt;/code&gt; field does: "Source: within, Toronto Resale Price Index, September 2026, shopwithin.co/index/toronto-resale". The licence is &lt;a href="https://creativecommons.org/licenses/by/4.0/" rel="noopener noreferrer"&gt;CC BY 4.0&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>opendata</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>api</category>
    </item>
  </channel>
</rss>
