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-authenticatedPOST /save(capture is queued, answer comes back as ajob_idyou poll for). - An explicit outcome vocabulary.
submittedis notarchived. This library only ever reportsarchived: Truealongside 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) viacategorize_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, login credentials for pages behind a form, a cookie for the target page, a custom capture User-Agent, and delayed Wayback availability. - A default timeout that matches archive.org's own documented capture ceiling (2 minutes) rather than an arbitrary short one, so a slow-but-legitimate anonymous capture doesn't get reported as failed when archive.org would have finished it given more time.
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
capture_cookie="session=abc123", # a cookie sent to the *target page*, not archive.org
use_user_agent="MyBot/1.0", # what archive.org's capture bot presents to the target site
delay_wb_availability=True, # capture is visible in Wayback ~12h later instead of immediately
)
Cookie-based session auth to archive.org itself (the alternative to an S3
key pair) is not supported — this client only authenticates with S3 keys,
which archive.org's own docs call "highly preferable" anyway. Don't confuse
that with capture_cookie above, which is unrelated: a cookie sent to
whatever page you're asking archive.org to capture, not to archive.org.
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_daysis 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.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| spn_client-0.3.0.tar.gz | 34.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| spn_client-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 59.4 kB
Release files / spn_client-0.3.0.tar.gz
| Download URL | spn_client-0.3.0.tar.gz |
|---|---|
| Size | 34.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c01343711e513dcf2c1793bde8a3b2f81cbbcc10e6ab480614ccb539bf62336a
|
|
BLAKE2b-256 checksum How to use checksums |
b16446ec1a91b5d93f2010130a2ac1660f1036855866fe34048511713703308b
|
| 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.3.0-py3-none-any.whl
| Download URL | spn_client-0.3.0-py3-none-any.whl |
|---|---|
| Size | 24.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ff8b02a684a867d5b221f6fdcdb34ac115baeb7ae561e86d8efadd3aa1472f8d
|
|
BLAKE2b-256 checksum How to use checksums |
2b36a52dfa01c1942cf038eab650fd06f899d1031796a0ae7caf503491d4c2fd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.10
|