Written By Hanzala Saleem
Updated At July 31, 2026 | 12 min read
To archive a website, you capture a snapshot of each page and store it with a timestamp so you can revisit that exact version later. The fastest way to build this yourself is to schedule automated screenshots through a screenshot API, save each image to storage, and log the capture in a small database. This guide shows you how to do that in Node.js in under an hour.
The Internet Archive's Wayback Machine already does this at web scale. You build your own when you need control: private archives, specific pages captured on your schedule, visual evidence for compliance, or before-and-after records of client sites. ScreenshotAPI handles the browser rendering so you only write the scheduling, storage, and viewer layers. By the end of this guide you will have a working archiving system and a clear view of when a screenshot archive is the right tool and when it is not.
A self-hosted wayback machine is a small system that captures timestamped snapshots of web pages on a schedule and lets you browse past versions. In this build, a screenshot API renders each page in a real browser and returns an image, a scheduler triggers captures daily or weekly, a database records every snapshot, and a lightweight web viewer displays the timeline. It captures the visual state of a page, not its underlying HTML, which makes it ideal for brand monitoring, compliance evidence, and design history.
Before we dive into the technical details, let's understand what we're building. A wayback machine is essentially a web archive that captures and stores snapshots of websites over time. Think of it as a digital time capsule for the internet.
While creating a website archiving system, you need a reliable screenshot API that can handle multiple challenges. Here's why ScreenshotAPI.net stands out:
Reliable Rendering: The API uses real browsers to capture screenshots, ensuring you get pixel-perfect representations exactly as users see them, including proper fonts and layout.
Flexible Capture Options: You can capture full page screenshots, specific sections, or viewports at different screen sizes, which is useful for archiving both desktop and mobile versions. The service handles long pages and entire page capture automatically.
Scalability: Whether you're archiving ten websites or ten thousand, the API scales with your needs and supports developers with comprehensive documentation.
Before you build, it helps to know what a screenshot archive does and does not preserve. A screenshot captures the rendered pixels of a page: exactly what a visitor saw, including fonts, layout, images, and cookie or promo banners. It does not capture the underlying HTML, links, or interactive elements. Tools like ArchiveBox and the WARC format used by the Internet Archive store the full page code so you can click links and replay the page later.
That difference decides which approach fits your goal. Choose screenshot archiving when the visual record is the point: proving what an ad or price page displayed on a given date, tracking a competitor redesign, or showing a client the before and after of a rebuild. Choose HTML or WARC archiving when you need the page to remain browsable and its links to work.
Many teams run both. They use a full HTML archiver for browsable copies and a screenshot archive for tamper-evident visual proof. The build in this guide covers the screenshot side, which is the faster half to stand up and the harder half to fake.
Before settling on a stack, it's worth taking time to compare the main Wayback Machine alternatives, since each one optimizes for a different priority. ArchiveBox is a self-hosted, open-source option that saves HTML, screenshots, and PDFs together, making it a strong fit for teams that want full control and privacy over their archive. Conifer (Webrecorder) excels at interactive, JavaScript-heavy pages because it produces high-fidelity WARC captures that replay almost exactly like the original site. Archive.today is popular for quick, single-page snapshots, especially when a team only needs a one-off proof-of-record without setting up bulk automation. Browsertrix Crawler suits teams that need large-scale, scheduled crawling across many domains, offering more enterprise-level automation than the others. The right choice ultimately depends on whether an agency needs browsable, clickable copies of competitor pages or simply wants tamper-evident, date-stamped visual proof.
| Approach | What it captures | Best for | Setup effort | Ongoing cost |
|---|---|---|---|---|
| Build your own with ScreenshotAPI | Full page screenshots (PNG, JPEG, PDF) with timestamps | Private visual archives, compliance evidence, client reporting | Low, a few hours in Node.js | API usage plus your storage |
| ArchiveBox (self-hosted) | HTML, screenshots, PDF, and WARC | Browsable offline copies of many URLs | Medium, Docker and server upkeep | Server and storage you maintain |
| Internet Archive Save Page Now | Public HTML snapshot in the Wayback Machine | Public preservation of a single page | None, paste a URL | Free, but public and not on your schedule |
| Managed screenshot archiving | Scheduled screenshots stored for you | Teams that want archiving without code | None to low | Subscription per plan |
Building your own sits between a full HTML archiver and a paste-a-URL public service. You get private, scheduled, visual snapshots you control, without running a crawler or a heavy server.
Building a wayback machine involves three main components:
ScreenshotAPI handles the browser rendering, while you build the scheduling, storage, and interface around it. If you would rather send captures straight to your own cloud bucket instead of managing files on disk, the storage integration lets the API write each snapshot to S3 or a compatible provider automatically, which is the approach most teams move to once their archive grows past a few thousand images.
First of all you need access to the screenshot API. Go to ScreenshotAPI.net and sign up for a free account. Once registered, you can get an API key that authenticates your requests. Keep this key secure, as it's your gateway to the screenshot functionality.
For this tutorial, I'll use Node.js because it's perfect for API integrations and has excellent async support for handling multiple captures. Here's what you'll need:
First, create a new project directory and initialize it:
mkdir wayback-machine
cd wayback-machine
npm init -y
Install the necessary Node.js packages:
npm install axios node-cron express sqlite3 fs-extra dotenv
These libraries will help you make API calls, schedule regular captures, handle file operations, and manage your database.
Now let's write the core function that captures website screenshots. Create a file called capture.js:
const axios = require('axios');
const fs = require('fs-extra');
const path = require('path');
async function captureWebsite(url, apiKey, outputFolder = 'archives') {
try {
// Create output folder if it doesn't exist
await fs.ensureDir(outputFolder);
// Prepare the API request
const apiUrl = 'https://shot.screenshotapi.net/v3/screenshot';
const params = {
token: apiKey,
url: url,
full_page: 'true',
output: 'image',
file_type: 'png',
no_cookie_banners:'true',
wait_for_event: 'load'
};
// Make the request to capture the full page screen
const response = await axios.get(apiUrl, {
params: params,
responseType: 'arraybuffer'
});
if (response.status === 200) {
// Generate filename with timestamp
const timestamp = new Date().toISOString().replace(/:/g, '-').split('.')[0];
const safeUrl = url.replace(/https?:\/\//, '').replace(/\//g, '_');
const filename = `${safeUrl}_${timestamp}.png`;
const filepath = path.join(outputFolder, filename);
// Save the screenshot as PNG format
await fs.writeFile(filepath, response.data);
console.log(`Screenshot saved: ${filename}`);
return filepath;
}
} catch (error) {
console.error(`Error capturing screenshot: ${error.message}`);
return null;
}
}
module.exports = { captureWebsite };
This function constructs a proper API request with your credentials, handles the response asynchronously, generates a meaningful filename with timestamps, and saves the full image to your archive folder. The service automatically handles cookie banners and ads during the screenshot capture process.
A wayback machine isn't useful if you have to manually trigger it every time. Let's automate the capture process using node-cron. Create a file called scheduler.js:
const cron = require('node-cron');
const { captureWebsite } = require('./capture');
class ArchiveScheduler {
constructor(apiKey) {
this.apiKey = apiKey;
this.tasks = [];
}
async captureAll(urls) {
for (const url of urls) {
await captureWebsite(url, this.apiKey);
// Be respectful, don't hammer the API
await this.sleep(5000);
}
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
scheduleDaily(urls, hour = 2, minute = 0) {
// Schedule daily at specified time (default 2:00 AM)
const task = cron.schedule(`${minute} ${hour} * * *`, async () => {
console.log('Starting daily capture...');
await this.captureAll(urls);
});
this.tasks.push(task);
console.log(`Scheduled daily captures at ${hour}:${minute} for ${urls.length} websites`);
return task;
}
scheduleHourly(urls, minute = 0) {
// Schedule hourly at specified minute
const task = cron.schedule(`${minute} * * * *`, async () => {
console.log('Starting hourly capture...');
await this.captureAll(urls);
});
this.tasks.push(task);
console.log(`Scheduled hourly captures for ${urls.length} websites`);
return task;
}
scheduleWeekly(urls, dayOfWeek = 1, hour = 2, minute = 0) {
// Schedule weekly (default: Monday at 2:00 AM)
const task = cron.schedule(`${minute} ${hour} * * ${dayOfWeek}`, async () => {
console.log('Starting weekly capture...');
await this.captureAll(urls);
});
this.tasks.push(task);
console.log(`Scheduled weekly captures for ${urls.length} websites`);
return task;
}
stopAll() {
this.tasks.forEach(task => task.stop());
console.log('All scheduled tasks stopped');
}
}
module.exports = { ArchiveScheduler };
This scheduler runs in the background to automate screenshot capture at your specified intervals. Running captures at 2 AM when traffic is low is generally a good practice.
While storing images is straightforward, you need a way to track metadata and log each capture. Let's use SQLite for simplicity. Create a file called database.js:
const sqlite3 = require('sqlite3').verbose();
const path = require('path');
const fs = require('fs-extra');
class ArchiveDatabase {
constructor(dbPath = 'wayback_archive.db') {
this.db = new sqlite3.Database(dbPath);
this.initialize();
}
initialize() {
return new Promise((resolve, reject) => {
this.db.run(`
CREATE TABLE IF NOT EXISTS archives (
id INTEGER PRIMARY KEY AUTOINCREMENT,
url TEXT NOT NULL,
capture_date DATETIME DEFAULT CURRENT_TIMESTAMP,
filepath TEXT NOT NULL,
file_size INTEGER,
status TEXT DEFAULT 'success'
)
`, (err) => {
if (err) reject(err);
else resolve();
});
});
}
async logCapture(url, filepath) {
const stats = await fs.stat(filepath);
const fileSize = stats.size;
return new Promise((resolve, reject) => {
this.db.run(`
INSERT INTO archives (url, filepath, file_size)
VALUES (?, ?, ?)
`, [url, filepath, fileSize], function(err) {
if (err) reject(err);
else resolve(this.lastID);
});
});
}
getArchivesByUrl(url) {
return new Promise((resolve, reject) => {
this.db.all(`
SELECT * FROM archives
WHERE url = ?
ORDER BY capture_date DESC
`, [url], (err, rows) => {
if (err) reject(err);
else resolve(rows);
});
});
}
getAllUrls() {
return new Promise((resolve, reject) => {
this.db.all(`
SELECT DISTINCT url FROM archives ORDER BY url
`, (err, rows) => {
if (err) reject(err);
else resolve(rows.map(row => row.url));
});
});
}
close() {
this.db.close();
}
}
module.exports = { ArchiveDatabase };
Now every capture is logged with its URL, timestamp, location, and file size. This makes it easy to query your archive later and track the image size of each snapshot.
Having all these screenshots is great, but you need a way to browse them. Here's a simple Express-based web interface. Create a file called server.js:
const express = require('express');
const path = require('path');
const { ArchiveDatabase } = require('./database');
const app = express();
const db = new ArchiveDatabase();
// Serve static files (screenshots)
app.use('/archives', express.static('archives'));
app.use(express.static('public'));
// Set view engine
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
// Home page - list all archived URLs
app.get('/', async (req, res) => {
try {
const urls = await db.getAllUrls();
res.render('index', { urls });
} catch (error) {
res.status(500).send('Error loading archives');
}
});
// Timeline page - show all captures for a specific URL
app.get('/timeline', async (req, res) => {
try {
const url = req.query.url;
const captures = await db.getArchivesByUrl(url);
res.render('timeline', { url, captures });
} catch (error) {
res.status(500).send('Error loading timeline');
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Wayback Machine running on http://localhost:${PORT}`);
});
This creates a simple web app interface where you can select a website and view its capture timeline. Users can easily download and print archived versions.
Now let's create a main file that ties everything together. Create index.js:
require('dotenv').config();
const { captureWebsite } = require('./capture');
const { ArchiveScheduler } = require('./scheduler');
const { ArchiveDatabase } = require('./database');
// Configuration
const API_KEY = process.env.SCREENSHOTAPI_KEY;
const WEBSITES_TO_MONITOR = [
'https://example.com',
'https://competitor1.com',
'https://competitor2.com'
];
async function main() {
// Initialize database
const db = new ArchiveDatabase();
// Create scheduler
const scheduler = new ArchiveScheduler(API_KEY);
// Schedule daily captures at 2 AM
scheduler.scheduleDaily(WEBSITES_TO_MONITOR, 2, 0);
console.log('Wayback Machine initialized!');
console.log(`Monitoring ${WEBSITES_TO_MONITOR.length} websites`);
console.log('Scheduled captures will run daily at 2:00 AM');
// Optional: Capture immediately on start
console.log('Taking initial snapshots...');
for (const url of WEBSITES_TO_MONITOR) {
const filepath = await captureWebsite(url, API_KEY);
if (filepath) {
await db.logCapture(url, filepath);
}
}
console.log('Initial snapshots complete!');
}
// Handle graceful shutdown
process.on('SIGINT', () => {
console.log('Shutting down gracefully...');
process.exit(0);
});
main().catch(console.error);
Create a .env file to store your API key:
SCREENSHOTAPI_KEY=your_api_key_here
Local disk is fine for a first run, but a growing archive belongs in durable object storage. Two paths work here. The first is to keep the capture code above and upload each saved file to your bucket after writing it, using the AWS SDK. The second, which removes a step, is to let the screenshot request deliver the image directly to your storage through the storage integration, so the file never touches your server.
A minimal upload after capture looks like this:
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const fs = require('fs-extra');
const s3 = new S3Client({ region: process.env.AWS_REGION });
async function uploadSnapshot(filepath, key) {
const body = await fs.readFile(filepath);
await s3.send(new PutObjectCommand({
Bucket: process.env.ARCHIVE_BUCKET,
Key: key,
Body: body,
ContentType: 'image/png'
}));
console.log(`Uploaded to s3://${process.env.ARCHIVE_BUCKET}/${key}`);
}
module.exports = { uploadSnapshot };Store the returned object key in the same database row as the local path. That way your viewer can serve snapshots from the bucket and your disk stays clean between runs.
Competitor Analysis: A marketing agency archives competitors' homepages weekly, tracking messaging changes, design updates, and promotional campaigns using full page screenshots.
Compliance Monitoring: A legal firm captures regulated websites daily to ensure compliance with advertising standards and maintain evidence for cases, storing each version as data for review.
Portfolio Documentation: A web design agency archives client websites before and after redesigns, creating compelling before-and-after showcases with full size page screenshots.
Price Tracking: An e-commerce consultant monitors competitor pricing pages, capturing changes and identifying pricing strategies through regular webpage snapshots.
Content Preservation: A journalist archives news articles and social media posts for investigative research before they can be deleted or modified, ensuring no online content is lost.
Building your own wayback machine with ScreenshotAPI.net gives you unexpected control over website archiving. Whether you're tracking competitors, preserving content for legal purposes, or simply documenting the evolution of the web, this system puts you in the driver's seat.
The beauty of this approach is its flexibility. Start simple with basic scheduled captures, then expand with comparison tools, mobile viewports, and cloud storage as your needs grow. The service supports various features including custom width parameters, scale adjustments, and zoom control. You can also export to different formats like JPEG or PDF based on your requirements.
Pairing ScreenshotAPI's real-browser rendering with a small amount of Node.js gives you an archiving tool built to your exact needs, without running a crawler or maintaining a heavy server. You control what gets captured, how often, and where it is stored.
Websites change and disappear without warning. A private, scheduled screenshot archive means you keep a dated visual record of the pages that matter, whether that is a competitor's homepage, a regulated pricing page, or a client site mid-redesign.
Ready to start? Create a free ScreenshotAPI account, get your API key, and run your first scheduled capture today.
No, and it is not meant to. The Internet Archive stores browsable HTML for billions of public pages. A screenshot archive stores private, timestamped images of the specific pages you choose. Use it when you need visual proof and control, not a public, clickable copy of the whole web.
A screenshot archive saves the rendered image of a page, so you see exactly what a visitor saw. A WARC archive saves the raw HTML and assets, so the page stays clickable and its links work. Screenshots prove appearance. WARC preserves function.
Match the schedule to how fast the page changes. Pricing and news pages suit daily captures, marketing sites suit weekly, and legal or compliance pages often run hourly. The scheduler in this guide supports all three, so you can set different intervals per URL.
Object storage such as S3 is the durable choice once your archive grows. It is cheap, scales without limit, and keeps snapshots off your application server. Store the object key alongside each database record so your viewer can load images directly from the bucket.
Capturing publicly available pages for internal research, monitoring, or evidence is generally accepted, but rules vary by country and use. Avoid archiving pages behind a login without permission, and consult your legal team before relying on archives in a dispute. This is general information, not legal advice.