<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>Spinov · Web Scraping &amp; AI Research</title><description>Practical case studies, code-heavy tutorials, and production-grade Apify actors for data extraction at scale. Author: Aleksei Spinov.</description><link>https://blog.spinov.online/</link><item><title>3 Telegram Channels Worth Following for Production Data Engineering</title><link>https://blog.spinov.online/blog/3-telegram-channels-production-data-engineering/</link><guid isPermaLink="true">https://blog.spinov.online/blog/3-telegram-channels-production-data-engineering/</guid><description>Three Telegram channels (@dataeng, @apache_airflow, @bigdata_en) I keep reading for distributed-systems patterns, orchestration depth, and downstream feedback that improves real production scrapers.</description><pubDate>Mon, 11 May 2026 00:00:00 GMT</pubDate></item><item><title>5 Apify run-log patterns that make production debugging 10x faster</title><link>https://blog.spinov.online/blog/5-apify-run-log-patterns-faster-debugging/</link><guid isPermaLink="true">https://blog.spinov.online/blog/5-apify-run-log-patterns-faster-debugging/</guid><description>Five Apify-actor logging patterns I use across 78 production scrapers — tag-prefix, structured retries, soft-block detection, dedup checkpointing, summary-line — turning 30-min log greps into 30-sec lookups.</description><pubDate>Fri, 01 May 2026 00:00:00 GMT</pubDate></item><item><title>5 Apify Scheduler Mistakes That Quietly Burn Compute Units (And the Cron Fixes)</title><link>https://blog.spinov.online/blog/5-apify-scheduler-mistakes-burning-compute-units/</link><guid isPermaLink="true">https://blog.spinov.online/blog/5-apify-scheduler-mistakes-burning-compute-units/</guid><description>Five scheduler misconfigurations I&apos;ve made or watched customers make on Apify, with exact cron/actor.json fixes and cost-of-mistake math from running 31 published actors.</description><pubDate>Fri, 01 May 2026 00:00:00 GMT</pubDate></item><item><title>5 production scraping failures from 1000+ runs (and the fixes that actually shipped)</title><link>https://blog.spinov.online/blog/5-production-scraping-failures-1k-runs/</link><guid isPermaLink="true">https://blog.spinov.online/blog/5-production-scraping-failures-1k-runs/</guid><description>Real failure modes from 2190 lifetime Apify runs across 32 actors — schema drift caught silently, retry self-DDoS, concurrency WAF traps, memory creep on long runs, silent webhook failures. With code.</description><pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate></item><item><title>A Budget Brake That Stops a Scraper Before $200</title><link>https://blog.spinov.online/blog/a-budget-brake-stops-your-scraper-before-200/</link><guid isPermaLink="true">https://blog.spinov.online/blog/a-budget-brake-stops-your-scraper-before-200/</guid><description>Spend alerts fire after the money is gone. A budget brake refuses the next run before it spends. Here&apos;s a 40-line preventive fuse, run locally, with the real output, plus where it stops working.</description><pubDate>Wed, 03 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Your AI Agent Logged Its Own API Key. I Wrote the 40-Line Redactor.</title><link>https://blog.spinov.online/blog/agent-logged-its-own-api-key-secret-redaction/</link><guid isPermaLink="true">https://blog.spinov.online/blog/agent-logged-its-own-api-key-secret-redaction/</guid><description>Your agent logs its tool calls. One of the arguments is your API key, and your log shipper forwards it to a third-party vendor in plaintext. A redaction layer at the log boundary (known-key regex + a Shannon-entropy net) masks it before egress. Code + deterministic run below.</description><pubDate>Wed, 24 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Your Agent Doesn&apos;t Run Out of Context. It Degrades at 79%</title><link>https://blog.spinov.online/blog/agent-quietly-degrades-context-occupancy-handoff/</link><guid isPermaLink="true">https://blog.spinov.online/blog/agent-quietly-degrades-context-occupancy-handoff/</guid><description>Long agent sessions don&apos;t fail at overflow. Step quality bends down once the window is ~70-80% full, with nothing in the logs. A runnable model of where it breaks and a deterministic handoff that resets it before the cliff.</description><pubDate>Sat, 20 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Your AI Agent Re-Reads Every Page It Already Saw. I Measured the 8x Context Tax</title><link>https://blog.spinov.online/blog/agent-re-reads-every-page-context-tax/</link><guid isPermaLink="true">https://blog.spinov.online/blog/agent-re-reads-every-page-context-tax/</guid><description>A naive agent loop re-sends the whole transcript every turn, so walking 20 pages costs 8x what a bounded window costs. Here is the honest math, the prompt-caching counter, and a 40-line file you can run.</description><pubDate>Sun, 14 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Your Agent Success Rate Counts Only the Survivors</title><link>https://blog.spinov.online/blog/agent-success-rate-survivorship-bias/</link><guid isPermaLink="true">https://blog.spinov.online/blog/agent-success-rate-survivorship-bias/</guid><description>A naive success rate divides wins by the runs that returned a clean pass or fail, so every run that timed out, got aborted, or is still hung quietly leaves the denominator. A stdlib demo: the same ledger reads 90.0% or 72.0% depending only on what you count.</description><pubDate>Tue, 30 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Your Agent Trusts the Tool&apos;s Description. The Attack Hides There.</title><link>https://blog.spinov.online/blog/agent-trusts-the-tools-own-description/</link><guid isPermaLink="true">https://blog.spinov.online/blog/agent-trusts-the-tools-own-description/</guid><description>The most dangerous field in an MCP tool manifest isn&apos;t inputSchema. It&apos;s description: free-form prose a stranger writes straight into your agent&apos;s system prompt, and most code treats it as inert metadata. Here&apos;s a stdlib scanner that reads it as untrusted input before you register the tool, plus the honest line where it stops working.</description><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Your AI Agent&apos;s Memory Has No Expiry Date: I Scored Freshness on a Real Corpus</title><link>https://blog.spinov.online/blog/ai-agent-memory-has-no-expiry-date-freshness-score/</link><guid isPermaLink="true">https://blog.spinov.online/blog/ai-agent-memory-has-no-expiry-date-freshness-score/</guid><description>A memory or RAG chunk was true when you stored it, then quietly went stale, and your agent still injects it into reasoning as an authoritative fact. The retrieval is clean. The fact is dead. Here&apos;s a small freshness gate that tags each chunk with age, lineage, and a freshness score, then blocks the stale ones before the model ever reads them, with the real deterministic stdout.</description><pubDate>Mon, 15 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Your AI Agent Scraped a Page. The Page Told It What to Do.</title><link>https://blog.spinov.online/blog/ai-agent-scraped-page-told-it-what-to-do/</link><guid isPermaLink="true">https://blog.spinov.online/blog/ai-agent-scraped-page-told-it-what-to-do/</guid><description>Indirect prompt injection at the scrape-then-agent ingest layer: a valid 200 page carries an instruction that hijacks control flow. A runnable, stdlib-only demo of the trust boundary that stops it — and the summarizer-laundering problem I haven&apos;t solved.</description><pubDate>Sun, 21 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Your AI Agent Trusts a 200 OK. I Logged How Often the Page Was Garbage</title><link>https://blog.spinov.online/blog/ai-agent-trusts-200-ok-page-was-garbage/</link><guid isPermaLink="true">https://blog.spinov.online/blog/ai-agent-trusts-200-ok-page-was-garbage/</guid><description>Your agent&apos;s web-fetch tool returns HTTP 200 and a non-empty string, and your agent believes it. But that body can be a Cloudflare challenge, an empty JS shell, or a half-loaded page, and the agent plans on it anyway. Here&apos;s a 40-line sanity gate that tags every fetch OK / BLOCKED / EMPTY_SHELL / TRUNCATED before reasoning, with the real, deterministic stdout.</description><pubDate>Sat, 13 Jun 2026 00:00:00 GMT</pubDate></item><item><title>I write production scrapers. AI made 30% of them worse. Here&apos;s the rule of thumb.</title><link>https://blog.spinov.online/blog/ai-for-production-scrapers-rule-of-thumb/</link><guid isPermaLink="true">https://blog.spinov.online/blog/ai-for-production-scrapers-rule-of-thumb/</guid><description>After 1,819 production runs across 32 Apify actors — a practical map of where AI helps with scraper code, where it&apos;s neutral, and the 30% where it quietly breaks production.</description><pubDate>Mon, 11 May 2026 00:00:00 GMT</pubDate></item><item><title>5 Apify dataset deduplication patterns that stop double-billing your customers</title><link>https://blog.spinov.online/blog/apify-dataset-deduplication-patterns/</link><guid isPermaLink="true">https://blog.spinov.online/blog/apify-dataset-deduplication-patterns/</guid><description>Five production patterns to prevent silent dataset duplication on Apify — uniqueKey, content hashing, KV-store guards, and SQL-backed dedup. Real numbers from 968 Trustpilot runs.</description><pubDate>Sun, 17 May 2026 00:00:00 GMT</pubDate></item><item><title>5 Apify run-log patterns that make production debugging 10× faster</title><link>https://blog.spinov.online/blog/apify-run-log-patterns-debugging/</link><guid isPermaLink="true">https://blog.spinov.online/blog/apify-run-log-patterns-debugging/</guid><description>Five log patterns I added to a production Apify scraper after 951 runs: fatal markers, pagination cursors, proxy audit, retry telemetry, run summary.</description><pubDate>Fri, 01 May 2026 00:00:00 GMT</pubDate></item><item><title>5 Apify scheduler mistakes that quietly burn compute units</title><link>https://blog.spinov.online/blog/apify-scheduler-mistakes-cost-cu/</link><guid isPermaLink="true">https://blog.spinov.online/blog/apify-scheduler-mistakes-cost-cu/</guid><description>Five real Apify scheduler misconfigurations from 32-actor portfolio + cron / actor.json fixes + cost-of-mistake math, ordered by cost-impact.</description><pubDate>Fri, 15 May 2026 00:00:00 GMT</pubDate></item><item><title>Apify vs. self-hosted: the three numbers I use to decide</title><link>https://blog.spinov.online/blog/apify-vs-self-hosted-decision/</link><guid isPermaLink="true">https://blog.spinov.online/blog/apify-vs-self-hosted-decision/</guid><description>A decision framework for when to use Apify Store vs build a self-hosted scraper, based on run data from 31 public actors including one at 949 production runs.</description><pubDate>Thu, 30 Apr 2026 00:00:00 GMT</pubDate></item><item><title>5 Apify webhook patterns that turn one-off scrapers into reliable data pipelines</title><link>https://blog.spinov.online/blog/apify-webhook-integration-patterns/</link><guid isPermaLink="true">https://blog.spinov.online/blog/apify-webhook-integration-patterns/</guid><description>Five production-tested Apify webhook patterns from 1818 lifetime runs across 79 actors: signed payloads, idempotency, dead-letter queues, retry budgets, and schema-drift detection.</description><pubDate>Sun, 03 May 2026 00:00:00 GMT</pubDate></item><item><title>Your AI Agent Will Double-Charge on a Lost Response</title><link>https://blog.spinov.online/blog/at-most-once-tool-calls-idempotency-ledger/</link><guid isPermaLink="true">https://blog.spinov.online/blog/at-most-once-tool-calls-idempotency-ledger/</guid><description>A retry isn&apos;t a network problem — it&apos;s a semantics problem. When a write tool&apos;s response is lost, the side effect already happened. A 20-line idempotency ledger gives AI agents at-most-once tool calls. Runnable demo, real numbers.</description><pubDate>Tue, 16 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Caching LLM Calls: A Raw Prompt Key Almost Never Hits</title><link>https://blog.spinov.online/blog/caching-llm-calls-raw-prompt-key-never-hits/</link><guid isPermaLink="true">https://blog.spinov.online/blog/caching-llm-calls-raw-prompt-key-never-hits/</guid><description>A cache keyed on the raw prompt string looks green in tests and barely fires in prod, because every production prompt carries a volatile envelope. Here is the fix in a stdlib script, plus the case it still gets wrong.</description><pubDate>Sat, 27 Jun 2026 00:00:00 GMT</pubDate></item><item><title>The Cheaper API Was 2.5x Cheaper. It Cost 1.6x More.</title><link>https://blog.spinov.online/blog/cheaper-api-cost-per-successful-task/</link><guid isPermaLink="true">https://blog.spinov.online/blog/cheaper-api-cost-per-successful-task/</guid><description>A per-call price says one API is 2.5x cheaper. Per successful task, it was 1.63x more expensive — the failed attempts you pay for flip the ranking. Here&apos;s the arithmetic, with a runnable 70-line script for your own numbers.</description><pubDate>Fri, 19 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Conditional GET in production scrapers: what I learned wiring it into 3 actors</title><link>https://blog.spinov.online/blog/conditional-get-incremental-scraping/</link><guid isPermaLink="true">https://blog.spinov.online/blog/conditional-get-incremental-scraping/</guid><description>Real numbers from 2,190 lifetime runs: 304 Not Modified saved 32-71% of bandwidth across Trustpilot, exchange-rate and npm-package actors. Code, failure modes, and when to skip it.</description><pubDate>Tue, 19 May 2026 00:00:00 GMT</pubDate></item><item><title>Cost per result: a 4-line worksheet for Apify actors</title><link>https://blog.spinov.online/blog/cost-per-result-apify-worksheet/</link><guid isPermaLink="true">https://blog.spinov.online/blog/cost-per-result-apify-worksheet/</guid><description>What does one record actually cost end-to-end? A simple 4-line worksheet that surfaces hidden costs across every Apify actor in your portfolio.</description><pubDate>Thu, 30 Apr 2026 00:00:00 GMT</pubDate></item><item><title>Dead features in your own code: a self-audit story from my Apify actor</title><link>https://blog.spinov.online/blog/dead-features-in-your-own-code/</link><guid isPermaLink="true">https://blog.spinov.online/blog/dead-features-in-your-own-code/</guid><description>An honest postmortem on finding two README-documented features that didn&apos;t exist in my own production scraper, with the audit script I now run on every actor.</description><pubDate>Thu, 30 Apr 2026 00:00:00 GMT</pubDate></item><item><title>Description drift in serverless function catalogs — a monthly refresh playbook</title><link>https://blog.spinov.online/blog/description-drift-serverless-catalogs/</link><guid isPermaLink="true">https://blog.spinov.online/blog/description-drift-serverless-catalogs/</guid><description>Why function-catalog descriptions go stale within months, and a 30-second monthly refresh playbook with Python code, drawn from a 32-actor Apify portfolio.</description><pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate></item><item><title>A 30-Line Probe That Tells You If a Page Needs a Browser</title><link>https://blog.spinov.online/blog/does-this-page-need-a-browser/</link><guid isPermaLink="true">https://blog.spinov.online/blog/does-this-page-need-a-browser/</guid><description>Half the &apos;you don&apos;t need a browser&apos; takes on my feed are right and none of them tell you how to check. Here&apos;s a stdlib probe that reads the raw HTTP response and votes NO_BROWSER, JS_REQUIRED, or MAYBE. I ran it on 10 named public URLs; 6 returned their data without Chrome.</description><pubDate>Fri, 05 Jun 2026 00:00:00 GMT</pubDate></item><item><title>DuckDB + dbt: a zero-cost analytics warehouse for projects under 100 GB</title><link>https://blog.spinov.online/blog/duckdb-dbt-zero-cost-analytics/</link><guid isPermaLink="true">https://blog.spinov.online/blog/duckdb-dbt-zero-cost-analytics/</guid><description>Why I run dbt-duckdb on a  VM instead of paying 90/month for Snowflake, with the full repo layout and CI workflow.</description><pubDate>Thu, 30 Apr 2026 00:00:00 GMT</pubDate></item><item><title>I&apos;ve Run 2,190 Production Scrapes — &quot;Ethical&quot; Isn&apos;t a robots.txt Question, It&apos;s a Rate-Limit One</title><link>https://blog.spinov.online/blog/ethical-scraping-is-a-rate-limit-question/</link><guid isPermaLink="true">https://blog.spinov.online/blog/ethical-scraping-is-a-rate-limit-question/</guid><description>Ethics-of-scraping posts argue about robots.txt and ToS. After 2,190 production runs across 32 scrapers, the line between &apos;ethical&apos; and &apos;banned&apos; turned out to be the same line — and it&apos;s drawn by conditional GET and a sane rate limit, not by a checkbox. Here&apos;s the working pattern.</description><pubDate>Mon, 25 May 2026 00:00:00 GMT</pubDate></item><item><title>Spoofing Your Scraper&apos;s Fingerprint Is a Losing Arcade</title><link>https://blog.spinov.online/blog/fingerprint-spoofing-is-a-losing-arcade/</link><guid isPermaLink="true">https://blog.spinov.online/blog/fingerprint-spoofing-is-a-losing-arcade/</guid><description>Spoofing JA3, TLS and header order is a race you lose by design. After 2,190 production scraper runs, the thing that survives is how the run behaves — not how human its fingerprint looks.</description><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Five Apify Input Schema Mistakes And The Fixes That Stuck</title><link>https://blog.spinov.online/blog/five-input-schema-mistakes-and-fixes/</link><guid isPermaLink="true">https://blog.spinov.online/blog/five-input-schema-mistakes-and-fixes/</guid><description>Five real input-schema mistakes I shipped across 78 Apify actors, what each cost in support emails and re-runs, and the exact schema patterns I use now.</description><pubDate>Fri, 01 May 2026 00:00:00 GMT</pubDate></item><item><title>I&apos;ve Run 2,190 Production Scrapes. The Framework You Pick Isn&apos;t What Breaks — Here&apos;s What Actually Does</title><link>https://blog.spinov.online/blog/framework-isnt-what-breaks-your-scraper/</link><guid isPermaLink="true">https://blog.spinov.online/blog/framework-isnt-what-breaks-your-scraper/</guid><description>After 2,190 production scraper runs, the framework almost never decided whether a job lived or died. Three disciplines did: element-targeted waiting (not networkidle), browser memory recycling, and bounded retry with backoff and jitter — with reproducible stdlib code.</description><pubDate>Thu, 28 May 2026 00:00:00 GMT</pubDate></item><item><title>10 Free Art &amp; Museum APIs With No Key (2026)</title><link>https://blog.spinov.online/blog/free-art-museum-apis-no-key/</link><guid isPermaLink="true">https://blog.spinov.online/blog/free-art-museum-apis-no-key/</guid><description>Ten free art and museum collection APIs (The Met, Art Institute of Chicago, Cleveland, V&amp;A, Yale LUX, SMK, Wellcome, Getty Vocabularies, Harvard LibraryCloud, Auckland Museum) that need no API key and no signup. Every endpoint re-verified with a live curl on July 16, 2026, each with its real trimmed response and one gotcha. Plus the trap that runs through all of them: the Met&apos;s free API reports 182 Rembrandts, a valid HTTP 200, and six of the first forty are actually Rembrandt. total, hasImages, and isPublicDomain each answer a narrower question than their names promise.</description><pubDate>Thu, 16 Jul 2026 00:00:00 GMT</pubDate></item><item><title>9 Free Company Data APIs With No Key or Signup (2026)</title><link>https://blog.spinov.online/blog/free-company-data-apis-no-key/</link><guid isPermaLink="true">https://blog.spinov.online/blog/free-company-data-apis-no-key/</guid><description>Nine free company data APIs that resolve a company name into an LEI, CIK, ticker, or national registration ID with no API key and no signup. Every endpoint re-verified with a live curl on July 4, 2026, each with a real trimmed response and its documented limit.</description><pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate></item><item><title>8 Free CVE &amp; Vulnerability APIs With No Key (2026)</title><link>https://blog.spinov.online/blog/free-cve-vulnerability-apis-no-key/</link><guid isPermaLink="true">https://blog.spinov.online/blog/free-cve-vulnerability-apis-no-key/</guid><description>Eight free CVE and vulnerability APIs that look up, triage, and find exposure with no API key and no signup. Every endpoint re-verified with a live curl on July 3, 2026, each with a real trimmed response and its documented rate limit.</description><pubDate>Sat, 04 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Your Dictionary API 404s on Real Words. Here Are 9 Keyless.</title><link>https://blog.spinov.online/blog/free-dictionary-word-apis-no-key/</link><guid isPermaLink="true">https://blog.spinov.online/blog/free-dictionary-word-apis-no-key/</guid><description>Nine free dictionary, word and language APIs that need no key and no signup (Free Dictionary API, FreeDictionaryAPI, Datamuse, Urban Dictionary, RhymeBrain, Random Word API, LanguageTool, MyMemory, PoetryDB), each re-verified with a live curl on July 18, 2026, each with its real response and one gotcha. Plus the trap underneath the list: on a keyless dictionary API a 404 means &apos;not in my frozen wordlist&apos;, not &apos;not a word&apos;, so the same word returns 404 on one host and a real definition on another, and a confidence field of match:1 can sit on a wrong translation. HTTP status and a vendor confidence score are not facts about the word.</description><pubDate>Sat, 18 Jul 2026 00:00:00 GMT</pubDate></item><item><title>8 Free Food &amp; Nutrition APIs (No Key, Tested 2026)</title><link>https://blog.spinov.online/blog/free-food-nutrition-apis-no-key/</link><guid isPermaLink="true">https://blog.spinov.online/blog/free-food-nutrition-apis-no-key/</guid><description>Eight free food and nutrition APIs (barcode lookup, ingredients, fruit, crowd-sourced prices) that return JSON with no API key and no signup, plus three recipe and USDA bonuses that need a shared key. Every endpoint re-verified with a live curl on July 8, 2026, each with a real trimmed response and the one field that turns a clean HTTP 200 into a silent wrong answer.</description><pubDate>Thu, 09 Jul 2026 00:00:00 GMT</pubDate></item><item><title>10 Free Facts, Jokes &amp; Name APIs With No Key (2026)</title><link>https://blog.spinov.online/blog/free-fun-facts-apis-no-key/</link><guid isPermaLink="true">https://blog.spinov.online/blog/free-fun-facts-apis-no-key/</guid><description>Ten free fun and facts APIs (agify, genderize, nationalize, icanhazdadjoke, Advice Slip, Useless Facts, Cat Facts, yesno.wtf, Chuck Norris, JokeAPI) that need no API key and no signup. Every endpoint re-verified with a live curl on July 12, 2026, each with its real trimmed response and the same trap: a 200 that parses, matches your schema, and still hands you null, an empty array, or a guess with 0.69 confidence.</description><pubDate>Mon, 13 Jul 2026 00:00:00 GMT</pubDate></item><item><title>16 Keyless Game APIs, 5 Incompatible Ways to Say &quot;Empty&quot;</title><link>https://blog.spinov.online/blog/free-game-apis-no-key-http-200-lies/</link><guid isPermaLink="true">https://blog.spinov.online/blog/free-game-apis-no-key-http-200-lies/</guid><description>Sixteen free game and gaming APIs that need no key and no signup (Scryfall, PokeAPI, Deck of Cards, Open Trivia DB, FreeToGame, CheapShark, dnd5eapi, Open5e, MTG.io, PokemonTCG, Guild Wars 2, Speedrun.com, mcsrvstat, Jikan, Valorant-API, Steam appdetails), each verified with a live curl on July 19, 2026. Then the part that matters: I asked all sixteen for something that does not exist, and got five incompatible dialects of nothing. Seven answered HTTP 200 with a body carrying no result, which a res.ok wrapper passes downstream as data. Two served a 404 whose body is text/plain Not Found, which breaks any code that parses before it checks. One returned 200 with ip 127.0.0.1 for a host that does not exist. One disagreed with itself: HTTP 404 outside, status 0 inside.</description><pubDate>Mon, 20 Jul 2026 00:00:00 GMT</pubDate></item><item><title>8 Free Geocoding APIs With No Key and No Signup (2026)</title><link>https://blog.spinov.online/blog/free-geocoding-apis-no-key/</link><guid isPermaLink="true">https://blog.spinov.online/blog/free-geocoding-apis-no-key/</guid><description>Eight free geocoding APIs that turn addresses into coordinates (and back) with no API key and no signup. Every endpoint re-verified with a live curl in July 2026, each with a real, trimmed response and its rate limit.</description><pubDate>Fri, 03 Jul 2026 00:00:00 GMT</pubDate></item><item><title>10 Free Government APIs With No Key or Signup (2026)</title><link>https://blog.spinov.online/blog/free-government-apis-no-key/</link><guid isPermaLink="true">https://blog.spinov.online/blog/free-government-apis-no-key/</guid><description>Ten free government and public-sector data APIs (federal spending, drug safety, clinical trials, earthquakes, the national debt) that return JSON with no API key and no signup. Every endpoint re-verified with a live curl on July 6, 2026, each with a real trimmed response and the one field that turns a clean HTTP 200 into a silent wrong answer.</description><pubDate>Tue, 07 Jul 2026 00:00:00 GMT</pubDate></item><item><title>9 Free LLM APIs in 2026 You Can Use Without a Credit Card</title><link>https://blog.spinov.online/blog/free-llm-apis-2026-no-credit-card/</link><guid isPermaLink="true">https://blog.spinov.online/blog/free-llm-apis-2026-no-credit-card/</guid><description>Nine LLM APIs with a genuinely free tier and no credit card in 2026 — limits, OpenAI-compatibility, and which ones survive an extraction workload. Verified May 2026.</description><pubDate>Sun, 31 May 2026 00:00:00 GMT</pubDate></item><item><title>9 Free Mock &amp; Fake-Data APIs With No Key (2026)</title><link>https://blog.spinov.online/blog/free-mock-fake-data-apis-no-key/</link><guid isPermaLink="true">https://blog.spinov.online/blog/free-mock-fake-data-apis-no-key/</guid><description>Nine free mock and fake-data APIs (JSONPlaceholder, DummyJSON, Fake Store API, Random User Generator, httpbin, httpbingo, Postman Echo, Platzi Fake Store, Beeceptor Echo) that return test data with no API key and no signup. Every endpoint re-verified with a live curl on July 10, 2026, each with its real trimmed response and its specific trap: a 201 that created nothing, a 200 with a zero-byte body, a delete confirmation with a timestamp for a product that is still on the shelf.</description><pubDate>Sun, 12 Jul 2026 00:00:00 GMT</pubDate></item><item><title>11 Free Music APIs With No Key or Signup (2026)</title><link>https://blog.spinov.online/blog/free-music-apis-no-key/</link><guid isPermaLink="true">https://blog.spinov.online/blog/free-music-apis-no-key/</guid><description>Eleven free music APIs (MusicBrainz, Cover Art Archive, iTunes Search, Deezer, Lyrics.ovh, lrclib, Radio Browser, Open Opus, ListenBrainz, AcousticBrainz, Odesli) that need no API key and no signup. Every endpoint re-verified with a live curl on July 12, 2026, each with its real trimmed response and the same trap: a 200 with a full, well-shaped list where results[0] is the wrong song, the wrong recording, or the wrong entity entirely.</description><pubDate>Tue, 14 Jul 2026 00:00:00 GMT</pubDate></item><item><title>11 Free No-Key APIs Your AI Agent Can Use to Read the Web</title><link>https://blog.spinov.online/blog/free-no-key-apis-ai-agent-read-web/</link><guid isPermaLink="true">https://blog.spinov.online/blog/free-no-key-apis-ai-agent-read-web/</guid><description>Eleven REST APIs an AI agent can call to read and ground answers on the live web with no API key and no credit card. Every endpoint re-verified with a live curl in July 2026, each with a real, trimmed response.</description><pubDate>Thu, 02 Jul 2026 00:00:00 GMT</pubDate></item><item><title>8 Free Pop-Culture APIs With No Key (2026)</title><link>https://blog.spinov.online/blog/free-pop-culture-apis-no-key/</link><guid isPermaLink="true">https://blog.spinov.online/blog/free-pop-culture-apis-no-key/</guid><description>Eight free entertainment APIs (Open Trivia DB, TVMaze, Jikan, PokeAPI, Rick and Morty, SWAPI, Studio Ghibli, Deck of Cards) that return trivia, TV, anime, games, and card data with no API key and no signup. Every endpoint re-verified with a live curl on July 10, 2026, each with a real trimmed response and the one field that turns a clean HTTP 200 into a silent wrong answer, plus the two that quietly need a key.</description><pubDate>Sat, 11 Jul 2026 00:00:00 GMT</pubDate></item><item><title>9 Free Public Holiday &amp; Time APIs With No Key (2026)</title><link>https://blog.spinov.online/blog/free-public-holiday-time-apis-no-key/</link><guid isPermaLink="true">https://blog.spinov.online/blog/free-public-holiday-time-apis-no-key/</guid><description>Nine free public holiday, time zone, and calendar APIs that answer is-this-a-business-day, what-is-the-local-time, and what-is-this-date-in-another-calendar with no API key and no signup. Every endpoint re-verified with a live curl on July 4, 2026, each with a real trimmed response and the field that silently lies to you if you skip it.</description><pubDate>Mon, 06 Jul 2026 00:00:00 GMT</pubDate></item><item><title>8 Free Research Paper APIs With No Key (2026)</title><link>https://blog.spinov.online/blog/free-research-paper-apis-no-key/</link><guid isPermaLink="true">https://blog.spinov.online/blog/free-research-paper-apis-no-key/</guid><description>Eight free scholarly APIs (arXiv, Crossref, OpenAlex, DataCite, Unpaywall, DOAJ, OpenCitations, Zenodo) that return paper metadata, DOIs, citations, and open-access PDFs with no API key and no signup. Every endpoint re-verified with a live curl on July 9, 2026, each with a real trimmed response and the one field that turns a clean HTTP 200 into a silent wrong answer, plus the three that quietly need a key.</description><pubDate>Fri, 10 Jul 2026 00:00:00 GMT</pubDate></item><item><title>11 Free Space &amp; Astronomy APIs With No Key (2026)</title><link>https://blog.spinov.online/blog/free-space-astronomy-apis-no-key/</link><guid isPermaLink="true">https://blog.spinov.online/blog/free-space-astronomy-apis-no-key/</guid><description>Eleven free space and astronomy APIs (Where the ISS at, CelesTrak, TLE API, JPL SSD/CNEOS, JPL Horizons, NASA Exoplanet Archive, NASA Images, NOAA SWPC, Launch Library 2, SatNOGS DB, SIMBAD) that need no API key and no signup. Every endpoint re-verified with a live curl on July 13, 2026, each with its real trimmed response and one gotcha. Plus the trap that runs through all of them: two keyless ISS APIs, both HTTP 200, fourteen seconds apart, 9,300 km apart.</description><pubDate>Wed, 15 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Free Sports APIs: I Curl-Tested 15, 8 Need No Key</title><link>https://blog.spinov.online/blog/free-sports-score-apis-no-key/</link><guid isPermaLink="true">https://blog.spinov.online/blog/free-sports-score-apis-no-key/</guid><description>Eight free sports and score APIs that need no key and no signup (MLB StatsAPI, NHL, ESPN, OpenLigaDB, Jolpica-F1, Chess.com, Lichess, Fantasy Premier League), each re-verified with a live curl on July 19, 2026, each with its real response and one gotcha. Plus the trap underneath the list: on MLB StatsAPI the score field does not arrive as null or zero on games that have not been played, it usually disappears entirely, so a schema you inferred from a finished game raises KeyError on tomorrow&apos;s slate. The obvious guard fails too, because postponed and cancelled games both report abstractGameState Final with no score key at all, and 23 of the 94 games in my ten-day window landed on the wrong calendar day when I sliced gameDate instead of reading officialDate. HTTP 200 the whole time.</description><pubDate>Sun, 19 Jul 2026 00:00:00 GMT</pubDate></item><item><title>15 Keyless Transit APIs, and a Timestamp Their Own Docs Get Wrong</title><link>https://blog.spinov.online/blog/free-transit-flight-apis-no-key/</link><guid isPermaLink="true">https://blog.spinov.online/blog/free-transit-flight-apis-no-key/</guid><description>Fifteen transit, flight-tracking, bike-share and routing APIs that need no key and no signup (OpenSky, adsb.fi, adsb.lol, airplanes.live, hexdb, Citybikes, GBFS, MBTA, TfL, Amtraker, opendata.ch, iRail, Wienerlinien, OSRM, Transitous), each re-verified with a live curl on July 17, 2026, each with its real response and one gotcha. Plus the trap underneath the list: every ADS-B provider here calls its timestamp now, every one of them sends milliseconds, one of them documents seconds, and a plain arithmetic freshness check reads the result as data from the year 58510.</description><pubDate>Fri, 17 Jul 2026 00:00:00 GMT</pubDate></item><item><title>HTTP 200 Is a Lie: A 30-Line Schema Canary for Source Drift</title><link>https://blog.spinov.online/blog/http-200-is-a-lie-schema-canary/</link><guid isPermaLink="true">https://blog.spinov.online/blog/http-200-is-a-lie-schema-canary/</guid><description>Your scraper returns 200, the parser doesn&apos;t crash, and your corpus quietly rots. Across 962 runs on one source, the failure that bit me wasn&apos;t a block — it was the source reshaping its output. Here&apos;s a stdlib schema canary that asserts the shape of the data, not just the response.</description><pubDate>Sat, 30 May 2026 00:00:00 GMT</pubDate></item><item><title>The HTTP Code Your AI Agent Doesn&apos;t Handle Yet: 402</title><link>https://blog.spinov.online/blog/http-402-your-ai-agent-pay-per-crawl/</link><guid isPermaLink="true">https://blog.spinov.online/blog/http-402-your-ai-agent-pay-per-crawl/</guid><description>Your fetch agent branches on 200 and 403. Cloudflare&apos;s Pay-Per-Crawl adds a third outcome: HTTP 402 with a price tag. 403 cost you retries. 402 costs you money. A 40-line handler, and a synthetic run where a naive agent blew a $0.10 budget by ~10x.</description><pubDate>Wed, 17 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Idempotent webhook receivers in 50 lines of Python</title><link>https://blog.spinov.online/blog/idempotent-webhooks-in-50-lines/</link><guid isPermaLink="true">https://blog.spinov.online/blog/idempotent-webhooks-in-50-lines/</guid><description>Stop losing duplicate Stripe/GitHub/Slack webhooks. A 50-line Python + Postgres pattern that survives retries and crashed workers — code, schema, and a 5-minute reproducible test.</description><pubDate>Thu, 30 Apr 2026 00:00:00 GMT</pubDate></item><item><title>Give Your AI Agent a Web-Fetch Tool: a 60-Line MCP Server (Free, Self-Hosted)</title><link>https://blog.spinov.online/blog/mcp-server-web-fetch-tool-for-ai-agents/</link><guid isPermaLink="true">https://blog.spinov.online/blog/mcp-server-web-fetch-tool-for-ai-agents/</guid><description>Every MCP web-access tutorial this month points at a paid API. You dont need one. A 60-line, self-hosted MCP server that hands your agent a web_fetch tool returning clean text — with the production defaults (timeout, size cap, SSRF guard) tutorials skip. Real stdout included, tested on mcp 1.27.2.</description><pubDate>Fri, 12 Jun 2026 00:00:00 GMT</pubDate></item><item><title>The MCP Tool Your Agent Calls Changed Its Schema. It Didn&apos;t Notice.</title><link>https://blog.spinov.online/blog/mcp-tool-changed-schema-agent-didnt-notice/</link><guid isPermaLink="true">https://blog.spinov.online/blog/mcp-tool-changed-schema-agent-didnt-notice/</guid><description>An MCP server can rename a param, re-describe what it means, or narrow an enum between calls and still answer 200. JSON validation stays silent because the call is still structurally valid. Pin a SHA of each tool&apos;s contract and catch the drift before it executes. Stdlib only.</description><pubDate>Mon, 22 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Three memory-leak patterns in long-running scrapers (and how I caught them after 968 Trustpilot runs)</title><link>https://blog.spinov.online/blog/memory-leaks-long-running-scrapers/</link><guid isPermaLink="true">https://blog.spinov.online/blog/memory-leaks-long-running-scrapers/</guid><description>Production scraping memory leaks: BeautifulSoup retention, growing URL queues, and connection-pool exhaustion. Real fixes with measured before/after from 968+ live Trustpilot scraper runs.</description><pubDate>Mon, 18 May 2026 00:00:00 GMT</pubDate></item><item><title>Automate Your Backups with MinIO: Free S3-Compatible Storage for Everything</title><link>https://blog.spinov.online/blog/minio-backup-automation/</link><guid isPermaLink="true">https://blog.spinov.online/blog/minio-backup-automation/</guid><description>Replace paid S3 with self-hosted MinIO. Step-by-step backup automation in Python with 50 lines of code.</description><pubDate>Wed, 29 Apr 2026 00:00:00 GMT</pubDate></item><item><title>One Empty 200 OK Poisoned 5 of My Agent&apos;s 10 Steps</title><link>https://blog.spinov.online/blog/one-empty-200-ok-poisoned-5-agent-steps/</link><guid isPermaLink="true">https://blog.spinov.online/blog/one-empty-200-ok-poisoned-5-agent-steps/</guid><description>One tool call returned HTTP 200 with an empty body. My agent recorded a placeholder fact and never re-checked it. By the final answer, 5 of 10 steps were built on a price that was never fetched. A provenance-quarantine gate cut the blast radius to 0. Real, deterministic stdout, stdlib-only code, and the run counts behind it.</description><pubDate>Thu, 18 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Three operational rules I added after my Trustpilot scraper crossed 100 runs</title><link>https://blog.spinov.online/blog/operational-rules-after-100-runs/</link><guid isPermaLink="true">https://blog.spinov.online/blog/operational-rules-after-100-runs/</guid><description>Schema-drift detection, IP-budget enforcement, and golden-diff snapshots — three rules I added to a production Apify actor after 100+ runs revealed where silent failures hide.</description><pubDate>Thu, 30 Apr 2026 00:00:00 GMT</pubDate></item><item><title>Building a Proxy Health Monitor for 24/7 Scraper Uptime</title><link>https://blog.spinov.online/blog/proxy-health-monitor-247-scraper-uptime/</link><guid isPermaLink="true">https://blog.spinov.online/blog/proxy-health-monitor-247-scraper-uptime/</guid><description>A production-ready Python proxy health monitor: detects failures, rotates dead proxies, and alerts you before scrapers go down. Built from a 100K-page incident postmortem.</description><pubDate>Wed, 13 May 2026 00:00:00 GMT</pubDate></item><item><title>Your RAG Answers Confidently. The Source Doesn&apos;t Say That.</title><link>https://blog.spinov.online/blog/rag-answered-confidently-not-in-the-source/</link><guid isPermaLink="true">https://blog.spinov.online/blog/rag-answered-confidently-not-in-the-source/</guid><description>Your RAG can retrieve the perfect chunk and still answer with a fact that isn&apos;t in it. Retrieval metrics won&apos;t catch it. Here&apos;s a deterministic post-generation grounding floor in ~35 lines of stdlib Python.</description><pubDate>Tue, 23 Jun 2026 00:00:00 GMT</pubDate></item><item><title>RAG Chunking: Overlap=0 Drops Facts on the Boundary</title><link>https://blog.spinov.online/blog/rag-chunk-overlap-drops-boundary-facts/</link><guid isPermaLink="true">https://blog.spinov.online/blog/rag-chunk-overlap-drops-boundary-facts/</guid><description>A fixed-size chunker with overlap=0 cuts a fact in half at the window edge, so the chunk you retrieve carries the question&apos;s words but not the answer. A stdlib demo: recall 1/5 vs 4/5, plus the ceiling overlap can&apos;t fix.</description><pubDate>Mon, 29 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Feeding Raw HTML to Your LLM Is a Token Tax. I Measured It on 10 Real Pages — Median 7.4×, and It Hits Every Scheduled Run</title><link>https://blog.spinov.online/blog/raw-html-is-a-token-tax-i-measured-it/</link><guid isPermaLink="true">https://blog.spinov.online/blog/raw-html-is-a-token-tax-i-measured-it/</guid><description>Everyone says &apos;markdown beats HTML for tokens.&apos; Nobody shows a number. I tokenized raw HTML vs extracted text across 10 public pages with tiktoken. Here&apos;s the real multiplier, the 30-line meter, and what it costs when it repeats on a schedule.</description><pubDate>Fri, 29 May 2026 00:00:00 GMT</pubDate></item><item><title>Why your retry logic is broken (and the 30-line fix)</title><link>https://blog.spinov.online/blog/retry-logic-broken-30-line-fix/</link><guid isPermaLink="true">https://blog.spinov.online/blog/retry-logic-broken-30-line-fix/</guid><description>Most Python services retry network calls wrong — fixed delay, wrong errors, no upper bound. Here is the 30-line jitter+deadline pattern I use in production after a 4-minute incident.</description><pubDate>Thu, 30 Apr 2026 00:00:00 GMT</pubDate></item><item><title>Your LLM JSON Got Cut Off. Don&apos;t Just Raise max_tokens</title><link>https://blog.spinov.online/blog/salvage-truncated-llm-json/</link><guid isPermaLink="true">https://blog.spinov.online/blog/salvage-truncated-llm-json/</guid><description>When a model&apos;s JSON array hits the token cap mid-object, a naive json.loads throws and you lose every record you already paid for. The API already told you it truncated. A stdlib demo: the same cut-off response yields 0 records or 3, depending only on how you read it.</description><pubDate>Wed, 01 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Schema drift killed our pipeline — three contract tests that catch it</title><link>https://blog.spinov.online/blog/schema-drift-3-contract-tests/</link><guid isPermaLink="true">https://blog.spinov.online/blog/schema-drift-3-contract-tests/</guid><description>When a vendor silently flips a JSON field type or drops a value, your scraper keeps running and your data lies. Three small contract tests that catch schema drift before it lands in your warehouse.</description><pubDate>Thu, 30 Apr 2026 00:00:00 GMT</pubDate></item><item><title>Scraping All the Text Is the Easy 10%. Keeping the Corpus Worth Training On Is the Other 90% — Notes From 962 Runs</title><link>https://blog.spinov.online/blog/scraping-text-is-the-easy-10-percent-dedup-and-decay/</link><guid isPermaLink="true">https://blog.spinov.online/blog/scraping-text-is-the-easy-10-percent-dedup-and-decay/</guid><description>Getting the text out is the easy 10%. After 962 production scraper runs, the hard 90% is deduplication, re-collection, and decay — with a reproducible stdlib-only corpus deduper.</description><pubDate>Wed, 27 May 2026 00:00:00 GMT</pubDate></item><item><title>SSRF in AI Agents: Blocking 169.254 by String Isn&apos;t Enough</title><link>https://blog.spinov.online/blog/ssrf-allowlist-ai-agent-encoded-ip-bypass/</link><guid isPermaLink="true">https://blog.spinov.online/blog/ssrf-allowlist-ai-agent-encoded-ip-bypass/</guid><description>Your agent&apos;s SSRF guard blocks 169.254.169.254. Does it block 0xA9FEA9FE, the same address in hex? This is a teardown of my own web_fetch guard, with a stdlib script that proves where a string denylist leaks and what a default-deny allowlist catches instead.</description><pubDate>Sun, 28 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Token Bucket vs Exponential Backoff: What Changed After 966 Runs</title><link>https://blog.spinov.online/blog/token-bucket-vs-exponential-backoff/</link><guid isPermaLink="true">https://blog.spinov.online/blog/token-bucket-vs-exponential-backoff/</guid><description>After 966 production runs of the Trustpilot scraper, I replaced exponential backoff with a token bucket in five actors. Code, numbers, and the failure modes that disappeared.</description><pubDate>Fri, 15 May 2026 00:00:00 GMT</pubDate></item><item><title>Token Economics of Agent-Driven Scraping: When LLM Agents Cost 50× More Than a Cron Job</title><link>https://blog.spinov.online/blog/token-economics-agent-driven-scraping/</link><guid isPermaLink="true">https://blog.spinov.online/blog/token-economics-agent-driven-scraping/</guid><description>Six months of production scrapers (970r on a single actor) showed LLM agent loops cost 30-80× more than deterministic crawlers above ~50 pages. Real token math, two narrow agent-win cases, and the fallback-only pattern.</description><pubDate>Mon, 18 May 2026 00:00:00 GMT</pubDate></item><item><title>Traefik + Docker: Zero-Config Reverse Proxy That Discovers Your Containers Automatically</title><link>https://blog.spinov.online/blog/traefik-docker-reverse-proxy/</link><guid isPermaLink="true">https://blog.spinov.online/blog/traefik-docker-reverse-proxy/</guid><description>Traefik watches the Docker socket, auto-discovers new containers, and routes traffic to them based on labels. No config files to edit. No reloads. Just docker compose up and go.</description><pubDate>Wed, 29 Apr 2026 00:00:00 GMT</pubDate></item><item><title>How my Trustpilot scraper survived 949 production runs (and the 3 things that almost killed it)</title><link>https://blog.spinov.online/blog/trustpilot-scraper-949-runs-postmortem/</link><guid isPermaLink="true">https://blog.spinov.online/blog/trustpilot-scraper-949-runs-postmortem/</guid><description>A post-mortem of three production failures from the Trustpilot scraper that crossed 949 runs on Apify Store: silent selector death, IP-block math, and the residential-proxy temptation.</description><pubDate>Wed, 29 Apr 2026 00:00:00 GMT</pubDate></item><item><title>What 250 runs of a Trustpilot scraper taught me about anti-bot patterns</title><link>https://blog.spinov.online/blog/trustpilot-scraper-production/</link><guid isPermaLink="true">https://blog.spinov.online/blog/trustpilot-scraper-production/</guid><description>Real numbers from a public Apify actor: which anti-bot tricks actually mattered, what the error budget looked like, and what I would do differently.</description><pubDate>Sat, 25 Apr 2026 00:00:00 GMT</pubDate></item><item><title>Welcome — what this blog is for</title><link>https://blog.spinov.online/blog/welcome-and-roadmap/</link><guid isPermaLink="true">https://blog.spinov.online/blog/welcome-and-roadmap/</guid><description>A code-first blog about web scraping, data extraction, and AI research, written by someone shipping production scrapers on Apify.</description><pubDate>Mon, 27 Apr 2026 00:00:00 GMT</pubDate></item><item><title>When NOT to scrape: 3 patterns where I now reach for an API instead</title><link>https://blog.spinov.online/blog/when-not-to-scrape/</link><guid isPermaLink="true">https://blog.spinov.online/blog/when-not-to-scrape/</guid><description>After shipping 79 Apify actors, here&apos;s the 60-second decision rule I now apply before writing any selector code.</description><pubDate>Thu, 30 Apr 2026 00:00:00 GMT</pubDate></item><item><title>You Can&apos;t Unit-Test an AI Agent. You Can Regression-Gate It.</title><link>https://blog.spinov.online/blog/you-cant-unit-test-an-ai-agent-regression-gate/</link><guid isPermaLink="true">https://blog.spinov.online/blog/you-cant-unit-test-an-ai-agent-regression-gate/</guid><description>A strict assert == golden fails on every valid agent run. Here is a deterministic gate over a non-deterministic output, with the case it misses printed in the same stdout.</description><pubDate>Fri, 26 Jun 2026 00:00:00 GMT</pubDate></item><item><title>You Pay for the Bandwidth That Returns Nothing</title><link>https://blog.spinov.online/blog/you-pay-for-the-bandwidth-that-returns-nothing/</link><guid isPermaLink="true">https://blog.spinov.online/blog/you-pay-for-the-bandwidth-that-returns-nothing/</guid><description>A per-GB proxy bill charges you for failed requests and retries too. On one config 53% of the bytes returned zero rows. Here&apos;s a model you can run with your own numbers.</description><pubDate>Thu, 04 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Your Scraper Collected 50 Rows. There Were 4,000.</title><link>https://blog.spinov.online/blog/your-scraper-collected-50-rows-there-were-4000/</link><guid isPermaLink="true">https://blog.spinov.online/blog/your-scraper-collected-50-rows-there-were-4000/</guid><description>A scraper can finish green, return only valid rows, and still hand you a quarter of the dataset. Pagination cutoffs are silent. Here is a 40-line completeness probe that catches them.</description><pubDate>Sun, 07 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Your Scraper Died at Row 12,000. The Rerun Pattern.</title><link>https://blog.spinov.online/blog/your-scraper-died-at-row-12000/</link><guid isPermaLink="true">https://blog.spinov.online/blog/your-scraper-died-at-row-12000/</guid><description>A long scrape that dies three hours in didn&apos;t lose one request — it lost the whole run, and rerunning from zero means paying twice for data you already had. Here&apos;s the ~40-line stdlib pattern that resumes a crashed job, fetches only the missing delta, and writes zero duplicates. Real captured output of a crash and a clean resume.</description><pubDate>Sat, 06 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Your Scraper Got Clean Data. The Site Lied to It.</title><link>https://blog.spinov.online/blog/your-scraper-got-clean-data-the-site-lied/</link><guid isPermaLink="true">https://blog.spinov.online/blog/your-scraper-got-clean-data-the-site-lied/</guid><description>A site can detect your scraper and serve a 200 with a perfect schema and plausible values that are deliberately false. Status codes and sanity checks are blind to it by design. Here&apos;s a 30-line probe that grounds each row to an independent invariant — and why naive cross-source consensus gets fooled too.</description><pubDate>Tue, 09 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Your Scraper Passes Every Run. It&apos;s Still Rotting.</title><link>https://blog.spinov.online/blog/your-scraper-passes-every-run-its-still-rotting/</link><guid isPermaLink="true">https://blog.spinov.online/blog/your-scraper-passes-every-run-its-still-rotting/</guid><description>Your scraper exits 0 on every run. Schema valid, row count plausible. And the yield has been sliding for weeks. A 20-line lagged-baseline probe over your own run log catches the drift before it becomes a breakage.</description><pubDate>Mon, 08 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Your Scraper Re-Downloads Everything. Most Didn&apos;t Change.</title><link>https://blog.spinov.online/blog/your-scraper-re-downloads-everything-most-didnt-change/</link><guid isPermaLink="true">https://blog.spinov.online/blog/your-scraper-re-downloads-everything-most-didnt-change/</guid><description>A scheduled scraper re-downloads its whole corpus every run, even though almost nothing changed since last time. The fix isn&apos;t faster fetching — it&apos;s deciding FETCH/SKIP/CONDITIONAL from a manifest before the first request. A 30-line planner, its real output, and the production trap (weak rotating ETags) that fakes the savings.</description><pubDate>Wed, 10 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Your Scraper Returned a Clean Row. It Was Wrong.</title><link>https://blog.spinov.online/blog/your-scraper-returned-a-clean-row-it-was-wrong/</link><guid isPermaLink="true">https://blog.spinov.online/blog/your-scraper-returned-a-clean-row-it-was-wrong/</guid><description>HTTP was 200, the selectors held, the JSON parsed — and the LLM still returned a plausible, syntactically valid, semantically false value. A 60-line field-level sanity check catches the lie that schema validation can&apos;t see.</description><pubDate>Mon, 01 Jun 2026 00:00:00 GMT</pubDate></item></channel></rss>