Skip to main content

spn-client

A hardened Python client for archive.org's availability API and Save Page Now (SPN2) — the pieces that are easy to get wrong when you actually run this against archive.org at any volume:

  • Process-wide rate pacing with a circuit breaker. archive.org throttles per IP, not per thread or per endpoint. A naive per-thread backoff (or none at all) looks fine in testing and then silently stops archiving anything the first time it's run concurrently or against a busy queue.
  • Both submission modes. Anonymous GET /save/<url> (capture runs inline, answer comes back as a redirect) and S3-key-authenticated POST /save (capture is queued, answer comes back as a job_id you poll for).
  • An explicit outcome vocabulary. submitted is not archived. This library only ever reports archived: True alongside a real snapshot URL that came back from archive.org — never as a synonym for "we asked."
  • Staleness checking, so callers can skip re-archiving a URL that already has a recent-enough snapshot.
  • Submissions are paced and breaker-protected too, at archive.org's own documented capture-endpoint limits (7/min authenticated, 3/min anonymous) — not just the availability check.
  • Categorized capture failures. archive.org's SPN2 API documents ~30 specific error codes; check_job_status() sorts each into "permanent" (retrying won't help), "transient" (worth trying again), or "quota_exhausted" (back off the whole run, not just this URL) via categorize_job_error().
  • The rest of the documented capture options, exposed as optional keyword arguments on submit(): screenshots, outlink availability, skipping the first-capture check, JS render timeout, and login credentials for pages behind a form.

Every non-obvious piece of behavior in client.py is a documented response to a specific, dated, measured incident against the real archive.org API — not a guess.

Install

pip install spn-client

Usage

import spn_client

result = spn_client.check("https://example.com/some-page")
if result["archived"] is False or result.get("snapshot_stale"):
    submission = spn_client.submit(
        "https://example.com/some-page",
        access_key=ACCESS_KEY,   # optional — omit for anonymous, lower-rate submission
        secret_key=SECRET_KEY,
    )
    if submission["job_id"]:
        # authenticated path: poll for the real outcome
        status = spn_client.check_job_status(
            submission["job_id"], access_key=ACCESS_KEY, secret_key=SECRET_KEY
        )

Call spn_client.reset_rate_limit_state() once at the start of each independent run/process if you're running this as a long-lived worker — the pacing/breaker state is process-wide and intentionally does not reset itself, so a breaker tripped by one run would otherwise silently degrade the next.

Handling a failed capture

status = spn_client.check_job_status(job_id, access_key=ACCESS_KEY, secret_key=SECRET_KEY)
if status["state"] == "failed":
    category = status["retry_category"]  # "permanent" / "transient" / "quota_exhausted" / None
    if category == "quota_exhausted":
        ...  # stop submitting new URLs this run, not just this one
    elif category != "permanent":
        ...  # worth another attempt later (also covers the unknown/None case)

Optional capture options

spn_client.submit(
    url,
    access_key=ACCESS_KEY, secret_key=SECRET_KEY,
    capture_screenshot=True,       # -> status["screenshot_url"] once the job succeeds
    outlinks_availability=True,    # -> status["outlinks"]
    skip_first_archive=True,
    js_behavior_timeout=15,        # seconds, 0-30 (archive.org's default is 5)
    target_username="user", target_password="pass",  # pages behind a login form
)

What this doesn't do

This is the archive.org client only — it has no opinion about:

  • What staleness policy is right for your use case (stale_days is always a caller-supplied parameter)
  • How you track which URLs you've already archived (that's a caller-side ledger/cache concern)
  • Batch-loop concerns like a progress heartbeat or a wall-clock budget across many submissions — those depend on your own operational needs

License

MIT

Metadata

Release files for spn-client 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for spn-client 0.2.1
File Size Uploaded
spn_client-0.2.1.tar.gz 33.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for spn-client 0.2.1
File Interpreter ABI Platform
spn_client-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 56.3 kB

Release files / spn_client-0.2.1.tar.gz

Download URL spn_client-0.2.1.tar.gz
Size 33.1 kB
Tags Source
SHA-256 checksum
How to use checksums
59e5c993bda86a1cef5f25bacc59bac3b7c6d2486d76dd23d962ca1da6e8c967
BLAKE2b-256 checksum
How to use checksums
e152e690b8982e40e2f144be4f2dae0c33fdee913c80f97719c680fc16fcfc20
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Release files / spn_client-0.2.1-py3-none-any.whl

Download URL spn_client-0.2.1-py3-none-any.whl
Size 23.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6fc50984fb41024638660ed3db874712718a0c32c51d08fa94ca8683c16da003
BLAKE2b-256 checksum
How to use checksums
5e94b90867f40a346d1be01332efa6e5ea630306aed70b5959ae6743573d9a12
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Release history Release notifications | RSS feed

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

This release

0.2.1 This release

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page