Public PDF Batch Downloader and ZIP Archive
Pricing
from $1.44 / 1,000 pdf archiveds
Public PDF Batch Downloader and ZIP Archive
Download supplied public PDF URLs unchanged into one bounded ZIP, preserving filenames in per-input folders with SHA-256 hashes and a completion manifest.
Pricing
from $1.44 / 1,000 pdf archiveds
Rating
0.0
(0)
Developer
Automation Lab
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
6 days ago
Last modified
Categories
Share
This PDF batch downloader collects a supplied list of anonymous public PDF URLs into one ZIP of unchanged original documents. It keeps original safe filenames in numbered folders and includes a per-input status manifest with SHA-256 checksums. There is no OCR, conversion or website crawling.
Who is it for?
Researchers collecting public reports, operations teams assembling document packs, and analysts retaining source documents can use the archive as a repeatable input to their own review workflows. Supply direct document links, not website homepages. Use only documents you are authorized to download and retain.
Why use this PDF batch downloader?
- One downloadable ZIP instead of manually saving every document.
- Original bytes and filenames, not extracted text or reconstructed PDFs.
- One status row for every supplied input, including failures and duplicates.
- SHA-256 hashes for checking integrity and comparing later collections.
- Identical filenames cannot overwrite each other: each unique file has a numbered directory.
Getting started
- Add 1–100 direct, public HTTP or HTTPS PDF URLs to Direct PDF URLs.
- Keep the default size caps or choose smaller caps for your collection.
- Start the Actor and wait for its terminal status.
- Open Run completeness and check
completeandfailedCount. - Download Download ZIP and inspect
manifest.jsoninside it.
Example input:
{"urls": ["https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"],"maxFileSizeMb": 5,"maxTotalSizeMb": 20,"requestTimeoutSecs": 15}
The public W3C test document is a convenient starting example; its availability is controlled by W3C, not this Actor.
Input parameters
| Field | Default | Meaning |
|---|---|---|
urls | required | 1–100 direct PDF URL strings in processing order |
maxFileSizeMb | 5 | Maximum original bytes per unique PDF, in binary MiB; range 1–10 |
maxTotalSizeMb | 20 | Maximum summed successful unique original bytes per run, in binary MiB; range 1–32 |
requestTimeoutSecs | 15 | Wall-clock timeout per attempt, range 5–30 seconds |
Zero or unlimited limits are not supported. Each MiB is 1,048,576 bytes. The ZIP may contain up to 1 MiB of bounded packaging/manifest overhead beyond the original-byte cap. PDFs are processed sequentially; a large file that cannot fit is rejected, while a later smaller file can still fit. Every download shares a 220-second run download budget.
Supported URLs and exclusions
Only anonymous public destinations on standard HTTP/HTTPS ports are allowed. URL credentials and known credential/signature query parameters are rejected, so authenticated, tokenized or signed links are not supported. Anonymous public query parameters such as document IDs are supported. Input and final URLs in output have their queries, userinfo and fragments removed for privacy; use inputIndex to correlate a row with your original list. Fragments are removed. The Actor does not accept cookies, custom headers, private credentials, local files or internal addresses.
All DNS answers and each redirect destination are checked for public reachability; the validated address is pinned for the connection. There are no proxies or browser fallbacks. Supply an anonymous direct PDF URL; a source that requires login or a browser challenge is outside this product's scope.
Output files
The run's Key-Value Store contains:
| Key | Content |
|---|---|
ARCHIVE.zip | ZIP containing unchanged successful PDFs and manifest.json; absent if all inputs fail |
MANIFEST | JSON with the completeness summary and a status row for every input |
SUMMARY | JSON counts, successful original-byte total and completeness flag |
The default dataset contains the same per-input status rows. It can be exported as JSON, CSV or Excel for reconciliation. Exporting the dataset does not export the binary PDFs; download the ZIP separately.
Manifest fields
| Field | Meaning |
|---|---|
inputIndex | One-based position in the original list |
url | Input URL with credentials, query and fragment removed; invalid URLs use a safe placeholder |
finalUrl | Final validated public URL, or null on failure |
status | downloaded, duplicate or failed |
filename | Preserved safe original basename, or null |
archivePath | Numbered ZIP path; duplicates reference the first successful member |
bytes | Original PDF byte count, or null |
sha256 | SHA-256 digest of original bytes, or null |
httpStatus | Known final HTTP status, or null |
attempts | Attempt count, zero for duplicates or rejected inputs |
error | Safe failure message, or null |
archivedAt | ISO timestamp at the start of input processing |
A representative W3C test-document result:
{"inputIndex": 1,"url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf","finalUrl": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf","status": "downloaded","filename": "dummy.pdf","archivePath": "001/dummy.pdf","bytes": 13264,"sha256": "3df79d34abbca99308e79cb94461c1893582604d68329a41fd4bec1885e6adb4","httpStatus": 200,"attempts": 1,"error": null,"archivedAt": "2026-10-14T20:07:16.679Z"}
Filenames, duplicates and original bytes
The filename comes from the final server's Content-Disposition header, preferring a UTF-8 filename*, then from the final URL basename. A URL without a basename uses document.pdf. Unsafe filenames, path separators, control characters, reserved device names and names above 200 UTF-8 bytes are rejected instead of silently renamed.
Unique files use paths such as 001/report.pdf and 002/report.pdf. Their basenames and PDF bytes are unchanged. Identical normalized URLs reuse the first successful member and incur no additional PDF event charge. Different URLs with identical bytes are separate documents; there is no hash-based deduplication.
Responses must begin with %PDF-. HTTP content encoding other than identity is rejected rather than decoded. The Actor checks byte completeness but does not parse the entire PDF, repair corrupt documents or guarantee that a viewer will render every supplied file.
Failure behavior and troubleshooting
Network errors, HTTP 429 and HTTP 5xx responses receive at most one bounded retry. HTTP 4xx other than 429, invalid URLs, unsafe destinations, non-PDF bodies and oversize files are not repeatedly retried. Redirects are limited to five and cannot bypass destination checks.
If at least one unique PDF is archived, the run can succeed with complete: false. This is a partial archive, not 100% completion. Inspect every failed status before accepting the batch. If all inputs fail, the run fails after writing its manifest and summary. Malformed top-level input fails before collection begins.
No ZIP? Check whether all inputs failed. HTML instead of PDF? Use the actual document link, not its landing page. Size limit? Raise the limit within the supported range or split the collection. URL parameters rejected? Public document ID parameters are supported, but known token, signature and credential keys are rejected; do not paste tokens or private links.
How much does it cost to archive public PDFs?
The Actor uses pay-per-event pricing: a $0.0005 start event per valid run and one PDF archived event per unique PDF saved into the ZIP. The BRONZE rate is $0.0024 per archived PDF. Duplicate inputs, failed downloads and manifest rows have no additional PDF event charge.
At BRONZE, 1 archived PDF costs $0.0029, 10 cost $0.0245, and 100 cost $0.2405 in Actor event charges. Other subscription tiers have their own displayed event prices. Any platform charges shown separately by Apify are not included in these examples. Set a suitable total-charge limit before running.
Integrations
Use Make or n8n to supply a document URL list and run the Actor. On completion, inspect SUMMARY, route failed inputs for review, and copy ARCHIVE.zip to your own approved document storage. In an analysis pipeline, keep the original ZIP alongside the dataset export and use the hashes to detect byte changes between runs. The Actor does not schedule itself or emit change alerts.
API usage
Keep your Apify token in an environment variable; never put it in public document URLs.
curl -X POST 'https://api.apify.com/v2/acts/automation-lab~public-pdf-batch-archive/runs' \-H "Authorization: Bearer $APIFY_TOKEN" \-H 'Content-Type: application/json' \-d '{"urls":["https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"]}'
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('automation-lab/public-pdf-batch-archive').call({urls: ['https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf']});const { items } = await client.dataset(run.defaultDatasetId).listItems({ limit: 100 });console.log(items);// Download ARCHIVE.zip from this run's defaultKeyValueStoreId after checking SUMMARY.
Python:
import osfrom apify_client import ApifyClientclient = ApifyClient(os.environ['APIFY_TOKEN'])run = client.actor('automation-lab/public-pdf-batch-archive').call(run_input={'urls': ['https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf']})rows = client.dataset(run['defaultDatasetId']).list_items(limit=100).itemsprint(rows)
MCP use
Select the stable Actor-specific subset:
claude mcp add --transport http apify \'https://mcp.apify.com?tools=automation-lab/public-pdf-batch-archive'
Equivalent Claude Desktop, Cursor and VS Code client configuration:
{"mcpServers":{"apify":{"url":"https://mcp.apify.com?tools=automation-lab/public-pdf-batch-archive"}}}
Configure authorized authentication in your MCP client. Example prompts can ask for completeness or failure reconciliation; they do not authorize repeated runs. Discover actual tool names and argument schemas with read-only tools/list; selecting an Actor also adds run/data tools, not exactly one tool. An example consumer prompt is: “Archive these public direct PDF URLs once, then report the completeness summary and ZIP location.”
Start once and retain the returned run ID and storage IDs. call-actor waits at most 45 seconds; IDs are not results. If pending, check that same run with bounded backoff (2, 4, 8, then 10 seconds), a 120-second total deadline, and a client timeout above the selected server wait. Do not restart after a client timeout; recover the known run. At deadline report pending and stop polling.
After successful completion, read SUMMARY, then use get-dataset-items with the returned run's defaultDatasetId. For example, send this argument object using the actual tool name and field format confirmed by tools/list (replace the placeholder with the returned dataset ID):
{"datasetId": "<returned defaultDatasetId>","limit": 20,"offset": 0,"fields": ["inputIndex", "status", "filename", "archivePath", "bytes", "sha256", "error"]}
Use untransformed JSON pages of 20 source rows, with a total consumer budget of 100 rows or 64 KiB of serialized UTF-8 result content admitted to model context across all pages, whichever comes first. Process each response host-side before model injection. Advance the source offset by the number of source rows consumed: after a full first page, request offset 20, then 40, 60 and 80. Track source pagination metadata, not counts after any host-side filtering or summarization. Stop at source exhaustion (a short/empty untransformed page or the reported total) or either budget.
Enforce the byte ceiling host-side even for an oversized row/page: omit or summarize with explicit truncation disclosure rather than injecting the full response. If your client cannot intercept responses, do not claim a hard byte guarantee. Report admitted row count, next source offset (including any unadmitted rows), and partial/truncated status when incomplete. Keep full dataset exports and binary ZIP downloads in storage or downstream processing outside model context. These consumer budgets do not reduce Actor collection limits. A failed run's rows are partial evidence, not a complete successful result. Client discovery/caching does not guarantee numeric token savings.
Legality and privacy
Public access is not permission to redistribute copyrighted documents. Respect source terms, robots guidance where applicable, ownership and applicable law. This tool does not bypass authentication or anti-bot controls. It is not affiliated with or endorsed by any document source. No AI model is used at runtime. Report problems through the Actor's Apify Issues tab, including the run ID but not private credentials. Do not provide private, personal or credential-bearing inputs.
Documents and manifests remain in the run's Apify storage according to your account's access and retention settings. Failed operations send sanitized diagnostic input, exceptions and actor/build/run IDs to our private GlitchTip service for repair; secret fields and URL queries are removed and reports are retained for 30 days.
FAQ
Does this discover PDFs on a website? No. Supply the direct PDF URLs yourself.
Does it extract text or use OCR? No. It packages unchanged original PDFs.
Will it finish every URL? Not guaranteed. The manifest explicitly reports failures and bounded-limit exclusions. A successful run can be partial.
Can filenames collide? Equal basenames are retained in different input-number directories; no successful file overwrites another.
Can I repeat the collection? Yes, with a new run. Compare manifests or hashes downstream; no monitoring or persistent history is built in.
Related Actors
For downstream analysis, PDF Text Extractor extracts text, while PDF to Structured Markdown Converter supports a separate document conversion workflow. This Actor's archive remains unconverted.