Guide

Reading Captures

How a timestamp becomes a capture, what comes back, and which archives can serve a body at all.

The call

ts
const response = await archive.content("https://example.com", { timestamp: "2002" });
const capture = await archive.getContent("https://example.com", { timestamp: "2002" });

capture.timestamp; // "2002-11-29T05:43:48Z"
capture.snapshot; // "https://web.archive.org/web/20021129054348/http://www.example.com:80/"
capture.mime; // "text/html"
capture.bytes; // 339
capture.content; // the decoded body

Reading is a separate operation from listing on purpose. Every provider lists through some CDX like index, but bodies come from an endpoint of the provider's own, and the only rule they share is that it must not return the archive's own UI. Wayback, Archive-It and Webarchiv Österreich replay the original response under the id_ modifier, Arquivo.pt under noFrame/replay, Common Crawl serves a byte range of a WARC file.

Picking the capture

timestamp is a Wayback stamp (YYYY through YYYYMMDDhhmmss) or an ISO date. The provider answers with the newest capture at or before it, or, when it holds nothing that old, the closest capture after it. So the body can postdate the request, and capture.timestamp is the date you should quote, not the one you asked for. Had to reach forward for it? Then capture._meta.selection reads "after". No more comparing timestamps by hand. Without timestamp you get the newest capture.

A snapshot URL works as the target too: pass the snapshot of a listed page and the provider reads that capture. The explorer reads by the original URL and the archive's own stamp instead, which lands on the same capture and does not trip over the :80 old originals carry.

What comes back

ts
interface ArchivedContent {
  url: string; // original URL, as the archive recorded it
  timestamp: string; // ISO 8601 date of the capture actually returned
  snapshot: string; // playback URL the body was read from
  content: string; // decoded body, as text
  mime?: string; // content type the archive reports
  bytes: number; // bytes read, after any cap
  sha256?: string; // hex SHA-256 of those bytes
  truncated: boolean; // the body was cut off at maxBytes
  body?: Uint8Array; // those bytes, only with body: true
  _meta: Record<string, unknown>;
}

The body is text. Transfer encodings, content encodings and the charset are handled before you see it. A capture that isn't text decodes lossily, and an image read as text is just noise.

Want the bytes instead? Pass body: true:

ts
const capture = await archive.getContent("https://example.com/logo.png", { body: true });

capture.body; // Uint8Array, the same bytes sha256 hashes

That read skips the cache. A storage driver that writes JSON would hand the bytes back as a plain object. Without body, _meta.rawSnapshot, or the WARC filename, offset and length for Common Crawl, still say where the bytes live.

sha256 hashes the bytes that were read, before the charset touches them. For a whole body it matches sha256sum over curl --compressed on the raw playback URL. So checking a saved copy against a capture doesn't need another read. A truncated body hashes only the part that was read.

A capture of a redirect is returned as recorded, never followed. _meta.status holds its status and _meta.location the destination the site named, cut at 2048 characters, so you can see where a moved page pointed without anything visiting it. The tool header prints both on a status: line.

maxBytes caps the read at 2 MiB by default. A capped body sets truncated: true, which the diff helper carries forward as partial, so a missing sentence in a truncated capture is never reported as a removal.

Who can serve a body

ProviderBody
Wayback Machineoriginal response, id_ replay
Arquivo.ptoriginal response, noFrame/replay
Webarchiv Österreichoriginal response, id_ replay, one exact URL
Vefsafnoriginal response, id_ replay, one exact URL
OSZK Webarchívumoriginal response, id_ replay under /pywb
Archive-Itoriginal response, id_ replay inside the collection
Common CrawlHTTP response from the WARC byte range
Mementothe selected TimeMap URI, MemGator proxy as fallback
Archive.todayits rendered wrapper page, not the original bytes
Conifer, Perma.cc, WebCitenone; content() answers unsupported with the reason
New Zealand Web Archivenone; its replay sits behind a bot check, so unsupported too

With providers.all(), content() tries Wayback, Arquivo.pt, Webarchiv Österreich, Vefsafn, OSZK Webarchívum, Archive.today and Common Crawl in order and returns the first body. It reports which provider served it in _meta.provider.

Text versus markup

The library returns the decoded body as it was served. The agent tools add a rendering step: format=text strips markup from an HTML capture and returns what a reader would see, format=raw keeps the source. The docs explorer exposes both, and the diff accepts either.