---
title: "Reading Captures"
description: "How a timestamp becomes a capture, what comes back, and which archives can serve a body at all."
canonical_url: "https://archives.agntn.dev/guide/content"
---
# 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](https://archives.agntn.dev/timeline) 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

<table>
<thead>
  <tr>
    <th>
      Provider
    </th>
    
    <th>
      Body
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Wayback Machine
    </td>
    
    <td>
      original response, <code>
        id_
      </code>
      
       replay
    </td>
  </tr>
  
  <tr>
    <td>
      Arquivo.pt
    </td>
    
    <td>
      original response, <code>
        noFrame/replay
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Webarchiv Österreich
    </td>
    
    <td>
      original response, <code>
        id_
      </code>
      
       replay, one exact URL
    </td>
  </tr>
  
  <tr>
    <td>
      Vefsafn
    </td>
    
    <td>
      original response, <code>
        id_
      </code>
      
       replay, one exact URL
    </td>
  </tr>
  
  <tr>
    <td>
      OSZK Webarchívum
    </td>
    
    <td>
      original response, <code>
        id_
      </code>
      
       replay under <code>
        /pywb
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Archive-It
    </td>
    
    <td>
      original response, <code>
        id_
      </code>
      
       replay inside the collection
    </td>
  </tr>
  
  <tr>
    <td>
      Common Crawl
    </td>
    
    <td>
      HTTP response from the WARC byte range
    </td>
  </tr>
  
  <tr>
    <td>
      Memento
    </td>
    
    <td>
      the selected TimeMap URI, MemGator proxy as fallback
    </td>
  </tr>
  
  <tr>
    <td>
      Archive.today
    </td>
    
    <td>
      its rendered wrapper page, not the original bytes
    </td>
  </tr>
  
  <tr>
    <td>
      Conifer, Perma.cc, WebCite
    </td>
    
    <td>
      none; <code>
        content()
      </code>
      
       answers <code>
        unsupported
      </code>
      
       with the reason
    </td>
  </tr>
  
  <tr>
    <td>
      New Zealand Web Archive
    </td>
    
    <td>
      none; its replay sits behind a bot check, so <code>
        unsupported
      </code>
      
       too
    </td>
  </tr>
</tbody>
</table>

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](https://archives.agntn.dev/guide/diff) accepts either.


## Sitemap

See the full [sitemap](https://archives.agntn.dev/sitemap.md) for all pages.
