<?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: Mohamed Fouad</title>
    <description>The latest articles on DEV Community by Mohamed Fouad (@mohamd764).</description>
    <link>https://dev.to/mohamd764</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%2F4195918%2F5087d8c0-1b82-4bdb-b5b5-a26cb2b13d6b.jpg</url>
      <title>DEV Community: Mohamed Fouad</title>
      <link>https://dev.to/mohamd764</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mohamd764"/>
    <language>en</language>
    <item>
      <title>How to Recover a Website's Lost Images from the Wayback Machine (in Bulk, with Python)</title>
      <dc:creator>Mohamed Fouad</dc:creator>
      <pubDate>Tue, 20 Oct 2026 10:02:27 +0000</pubDate>
      <link>https://dev.to/mohamd764/how-to-recover-a-websites-lost-images-from-the-wayback-machine-in-bulk-with-python-2mfe</link>
      <guid>https://dev.to/mohamd764/how-to-recover-a-websites-lost-images-from-the-wayback-machine-in-bulk-with-python-2mfe</guid>
      <description>&lt;p&gt;You migrate a CMS, change a CDN path, or let an old domain lapse — and suddenly every image on the site returns 404. Backups? Not always. But there's a good chance those files still exist in a public web archive: the Internet Archive's &lt;strong&gt;Wayback Machine&lt;/strong&gt; or &lt;strong&gt;Common Crawl&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Getting them back one by one through the Wayback calendar is painful. In this tutorial I'll show how the recovery works under the hood, and how to do it in bulk with an open-source CLI I wrote: &lt;a href="https://github.com/mohamd764/wayback-image-recovery" rel="noopener noreferrer"&gt;wayback-image-recovery&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why manual recovery doesn't scale
&lt;/h2&gt;

&lt;p&gt;A few gotchas show up quickly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Wayback UI wraps captures in HTML.&lt;/strong&gt; You want the original file, not the page with the toolbar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The newest capture can be fake.&lt;/strong&gt; Many captures are soft-404 error pages saved with HTTP 200.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Duplicates everywhere.&lt;/strong&gt; The same logo lives at dozens of URLs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wayback doesn't have everything.&lt;/strong&gt; A file missing there may still be in Common Crawl.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 1: Ask the CDX API what exists
&lt;/h2&gt;

&lt;p&gt;The Wayback Machine exposes a CDX index. For one URL you can request the latest capture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://web.archive.org/cdx/search/cdx?url=example.com/images/logo.png&amp;amp;output=json&amp;amp;sort=reverse&amp;amp;limit=1&amp;amp;filter=statuscode:200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a whole domain you can list unique image URLs with &lt;code&gt;matchType=domain&lt;/code&gt;, a &lt;code&gt;mimetype:image/.*&lt;/code&gt; filter, and &lt;code&gt;collapse=urlkey&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gotcha:&lt;/strong&gt; &lt;code&gt;collapse=urlkey&lt;/code&gt; keeps the &lt;em&gt;first&lt;/em&gt; row per URL, and rows are oldest-first. So the listing is good for discovery, but you must re-query each URL with &lt;code&gt;sort=reverse&amp;amp;limit=1&lt;/code&gt; (latest) or &lt;code&gt;sort=closest&amp;amp;closest=20200101&lt;/code&gt; (nearest to a date) to get the capture you actually want.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Download the original bytes
&lt;/h2&gt;

&lt;p&gt;Take the capture timestamp and add the &lt;code&gt;id_&lt;/code&gt; modifier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://web.archive.org/web/20210314120000id_/https://example.com/images/logo.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;id_&lt;/code&gt; returns the raw archived bytes with no rewriting or toolbar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Validate before you save
&lt;/h2&gt;

&lt;p&gt;Don't trust the status code. Check that the response isn't HTML, that the magic bytes match an image type, and that the file isn't suspiciously small or truncated. Then hash it (SHA-256) so identical files are stored once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Fall back to Common Crawl
&lt;/h2&gt;

&lt;p&gt;If Wayback has nothing usable, query Common Crawl's index for the URL. A hit gives you a WARC filename, offset and length — and an HTTP &lt;code&gt;Range&lt;/code&gt; request fetches only that record instead of a multi-gigabyte file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing all of this with one command
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;wayback-image-recovery&lt;/code&gt; wraps these steps into a resumable pipeline.&lt;/p&gt;

&lt;p&gt;Install (Python 3.9+):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"git+https://github.com/mohamd764/wayback-image-recovery.git"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Recover every archived image under a domain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wir recover &lt;span class="nt"&gt;--domain&lt;/span&gt; example.com &lt;span class="nt"&gt;--output&lt;/span&gt; ./recovered
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or a list of URLs, preferring captures close to a date:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wir recover urls.txt &lt;span class="nt"&gt;--closest&lt;/span&gt; 2020-01-01 &lt;span class="nt"&gt;--output&lt;/span&gt; ./recovered
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or a CSV export of broken links from your SEO crawler (a &lt;code&gt;url&lt;/code&gt;, &lt;code&gt;image_url&lt;/code&gt; or &lt;code&gt;src&lt;/code&gt; column):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wir recover &lt;span class="nt"&gt;--csv&lt;/span&gt; broken-images.csv &lt;span class="nt"&gt;--min-bytes&lt;/span&gt; 2048
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Files keep their original host and path, so putting them back on the server is straightforward. Each run writes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;manifest.csv&lt;/code&gt; / &lt;code&gt;manifest.json&lt;/code&gt; — one row per URL: source, snapshot timestamp, status, sha256, path, bytes, MIME type, error&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;manifest.jsonl&lt;/code&gt; — an append-only log, so re-running the same command skips files already recovered&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A run summary looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Processed 4 URL(s)
recovered=2 duplicate=1 skipped=0 not_found=1 invalid=0 error=0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;not_found&lt;/code&gt; is normal — archives don't have everything. Use &lt;code&gt;--strict&lt;/code&gt; in CI if you want any miss to fail the job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Be polite to the archives
&lt;/h2&gt;

&lt;p&gt;The Internet Archive and Common Crawl are free public resources. Keep concurrency low, respect rate limits, and only recover content you have rights to. The tool never crawls the live site; it only reads the archives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;The key ideas: discover with CDX, re-resolve each URL to the right capture, download with &lt;code&gt;id_&lt;/code&gt;, validate and dedupe, and fall back to Common Crawl WARC range requests. You can implement that yourself — or use the tool:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/mohamd764/wayback-image-recovery" rel="noopener noreferrer"&gt;github.com/mohamd764/wayback-image-recovery&lt;/a&gt;&lt;/strong&gt; (MIT, tested in CI)&lt;/p&gt;

&lt;p&gt;If it saves you a migration headache, a ⭐ helps others find it, and issues/PRs are welcome.&lt;/p&gt;

</description>
      <category>python</category>
      <category>webscraping</category>
      <category>opensource</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
