<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Public-Data on ErrorZap</title><link>https://errorzap.com/tags/public-data/</link><description>Recent content in Public-Data on ErrorZap</description><image><title>ErrorZap</title><url>https://errorzap.com/og.png</url><link>https://errorzap.com/og.png</link></image><generator>Hugo</generator><language>en-US</language><copyright>ErrorZap</copyright><lastBuildDate>Wed, 04 Nov 2026 09:00:00 -0700</lastBuildDate><atom:link href="https://errorzap.com/tags/public-data/index.xml" rel="self" type="application/rss+xml"/><item><title>Last Good Data Is a Feature</title><link>https://errorzap.com/posts/last-good-data-is-a-feature/</link><pubDate>Wed, 04 Nov 2026 09:00:00 -0700</pubDate><guid>https://errorzap.com/posts/last-good-data-is-a-feature/</guid><description>When a public-data source breaks, replacing yesterday&amp;rsquo;s truth with today&amp;rsquo;s garbage is not freshness. It is vandalism with a timestamp.</description><content:encoded><![CDATA[<p>I used to think a data collector had two states: it ran or it failed.</p>
<p>Then I started collecting from public websites.</p>
<p>A public source can return HTTP 200 and still be useless. It can serve a login page where a table used to be. It can return only the first page, repeat yesterday&rsquo;s records with new timestamps, move columns, invent empty dates, or decide that a polite automated request looks suspicious. The transport succeeded. The data did not.</p>
<p>The most dangerous collector is not the one that crashes. It is the one that confidently replaces a known-good dataset with a plausible pile of nonsense.</p>
<h2 id="fresh-is-not-the-same-as-true">Fresh is not the same as true</h2>
<p>Imagine a local information page with 8,000 verified records. Tonight&rsquo;s refresh retrieves 14 rows because the source changed pagination. A naive pipeline writes those 14 rows over the old file and celebrates a successful run.</p>
<p>By morning, the website is technically current and practically destroyed.</p>
<p>The safer pattern keeps separate objects:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>incoming candidate
</span></span><span style="display:flex;"><span>       |
</span></span><span style="display:flex;"><span>       v
</span></span><span style="display:flex;"><span>validate structure, counts, dates, uniqueness
</span></span><span style="display:flex;"><span>       |
</span></span><span style="display:flex;"><span>       +---- bad ----&gt; quarantine + alert
</span></span><span style="display:flex;"><span>       |
</span></span><span style="display:flex;"><span>       +---- good ---&gt; atomically promote to current
</span></span><span style="display:flex;"><span>                              |
</span></span><span style="display:flex;"><span>                              +--&gt; preserve previous known-good
</span></span></code></pre></div><p>The last-good dataset is not merely a backup. It is part of the product contract.</p>
<p>If tonight&rsquo;s import fails validation, readers should see yesterday&rsquo;s honest data plus a visible freshness warning. They should not see an empty page pretending that nothing exists.</p>
<h2 id="validation-should-be-rude">Validation should be rude</h2>
<p>Collectors need to distrust their source in specific, testable ways.</p>
<p>Useful gates include:</p>
<ul>
<li>required columns are still present;</li>
<li>row count did not collapse unexpectedly;</li>
<li>identifiers remain unique;</li>
<li>dates are parseable and not absurdly in the future;</li>
<li>detail-page coverage stays within a known range;</li>
<li>a sample of old records still exists;</li>
<li>content is not an error page wearing a 200 status code.</li>
</ul>
<p>None of these proves perfection. Together they make silent catastrophe much harder.</p>
<p>The thresholds should also be explainable. &ldquo;Reject if the row count falls more than 40 percent&rdquo; is debatable but testable. &ldquo;The page looked weird&rdquo; is neither.</p>
<h2 id="stale-should-be-visible">Stale should be visible</h2>
<p>Preserving last-good data creates a new obligation: do not pass stale data off as fresh.</p>
<p>Every public-facing dataset should carry a small status card:</p>
<ul>
<li>last successful source retrieval;</li>
<li>last successful validation;</li>
<li>current row count;</li>
<li>whether detail coverage is complete or partial;</li>
<li>number of quarantined records;</li>
<li>link to the original source;</li>
<li>plain-language caveat when the source is blocked or changing.</li>
</ul>
<p>That little card is more useful than a hundred lines of invisible logging. It tells readers what the system knows about itself.</p>
<h2 id="failure-can-be-a-product-feature">Failure can be a product feature</h2>
<p>Most software demos focus on the happy path: click Refresh, watch new rows appear. Real reliability lives in the ugly path: the source returns less, later, malformed, duplicated, or nothing at all.</p>
<p>The mature response is not heroic scraping. It is restraint.</p>
<p>Keep yesterday&rsquo;s truth. Quarantine today&rsquo;s suspicious changes. Explain the age of the evidence. Try again later without turning one upstream failure into a downstream lie.</p>
<p>Sometimes the most important new data is the decision not to replace the old data.</p>
]]></content:encoded></item><item><title>The Public Website That Blocked the Polite Bot</title><link>https://errorzap.com/posts/the-public-website-that-blocked-the-polite-bot/</link><pubDate>Mon, 02 Nov 2026 17:15:00 -0700</pubDate><guid>https://errorzap.com/posts/the-public-website-that-blocked-the-polite-bot/</guid><description>A public data portal rejected an ordinary collector, and the right response was better failure handling—not a proxy-rotation arms race.</description><content:encoded><![CDATA[<p>The information was public. The website was reachable. The collector was slow, identified, and boring.</p>
<p>It still got blocked.</p>
<p>That moment divides scraping projects into two kinds. One kind asks, &ldquo;How do we get around this?&rdquo; The other asks, &ldquo;What is the responsible product behavior when the source says no?&rdquo;</p>
<p>I want to build the second kind.</p>
<h2 id="public-does-not-mean-permissionless">Public does not mean permissionless</h2>
<p>A page visible in a browser may sit behind rate controls, session logic, terms, copyright, privacy obligations, or infrastructure that was never designed for automated access. A government label does not automatically erase those constraints. Neither does civic usefulness.</p>
<p>Before collecting, identify:</p>
<ul>
<li>the official source and any documented API or export;</li>
<li>terms and licensing for reuse, not merely viewing;</li>
<li>robots.txt and any published crawl or rate guidance;</li>
<li>update cadence;</li>
<li>contact or data-request path;</li>
<li>personal information that should not be republished;</li>
<li>a reasonable request rate and cache policy.</li>
</ul>
<p>If a structured export exists, use it. If permission is required, ask. If the data can be linked without copying, consider whether that is enough.</p>
<h2 id="a-block-is-a-state-not-a-challenge">A block is a state, not a challenge</h2>
<p>When a collector receives a 403, a 429, a challenge page, or a suddenly empty response, it should classify the event and stop escalating.</p>
<p>The bad pattern rotates headers, IPs, accounts, and browser fingerprints until something slips through. That is expensive, fragile, and hostile. It also converts a data-quality problem into an adversarial-access problem.</p>
<p>The safer pattern is:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>detect block
</span></span><span style="display:flex;"><span>  -&gt; stop the batch (and honor any Retry-After)
</span></span><span style="display:flex;"><span>  -&gt; preserve last-good data
</span></span><span style="display:flex;"><span>  -&gt; record bounded evidence
</span></span><span style="display:flex;"><span>  -&gt; alert once
</span></span><span style="display:flex;"><span>  -&gt; use official contact/export path
</span></span></code></pre></div><p>Do not let retries become a denial-of-service attack with good intentions.</p>
<h2 id="design-the-site-for-source-failure">Design the site for source failure</h2>
<p>If a public-facing service depends on someone else&rsquo;s portal, source unavailability is normal architecture.</p>
<p>The user experience should expose:</p>
<ul>
<li>last successful update time;</li>
<li>original source link;</li>
<li>current collection status;</li>
<li>whether displayed data is complete or partial;</li>
<li>what is being done to restore the feed;</li>
<li>correction path.</li>
</ul>
<p>That turns a mysterious stale page into an honest service.</p>
<h2 id="keep-the-collector-replaceable">Keep the collector replaceable</h2>
<p>Source HTML changes. Portals get replaced. APIs appear and disappear. A collector should separate retrieval, parsing, validation, storage, and presentation so a new lawful source path does not require rebuilding everything.</p>
<p>Store normalized records with provenance. Preserve the original source identifier. Keep transformations documented. Test parsers against saved, non-sensitive fixtures rather than hammering the live site during development.</p>
<p>The collector is not the public product. Trust is.</p>
<h2 id="politeness-includes-accepting-no">Politeness includes accepting no</h2>
<p>There is a technical culture that treats every block as a puzzle. Sometimes the clever solution is to back off, keep known-good information available, and make a formal request for a better feed.</p>
<p>The portal blocking the bot did not end the project. It clarified the contract the project needed: ethical retrieval, visible freshness, last-good preservation, and no secret war against the source.</p>
<p>A public-interest tool should not have to behave like malware to stay current.</p>
]]></content:encoded></item></channel></rss>