PDF Text Extractor: URL to Text avatar

PDF Text Extractor: URL to Text

Pricing

Pay per event

Go to Apify Store
PDF Text Extractor: URL to Text

PDF Text Extractor: URL to Text

Extract text from public PDFs for search and RAG preparation. Export one row per document or page as JSON/CSV; inspect extraction status and page limits. Text-layer PDFs only: no OCR or table reconstruction. Start with the one-PDF example in Example tasks.

Pricing

Pay per event

Rating

0.0

(0)

Developer

Data Lantern

Data Lantern

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

2

Monthly active users

5 days ago

Last modified

Share

PDF Text Extractor reads PDF files from links and returns their text as clean, structured data: one row per PDF with the full text, page count, title, creation date, word count and file size, or one row per page. It handles reports, papers, forms, contracts, manuals and e-books that contain real text, in any language, and it is fast (a 15-page paper takes about a second). Use it to feed documents into search, RAG and AI pipelines, to monitor published reports, or to build datasets from public PDFs.

What can this tool do?

  • Turn PDF links into plain text, one row per document or one row per page.
  • Read many PDFs in one run; document mode lists failed links with a reason. Failed files do not incur document/page result charges. The run start charge applies once at least one PDF has been read; a run where no PDF can be read fails and costs nothing.
  • Return the page count, the document title and creation date from the PDF's metadata, the language tag when the PDF has one, and word and character counts.
  • Limit the pages read per PDF (the default is the first 300 pages; set 0 for all) and the maximum file size (30 MB by default).
  • Report scanned PDFs (pages that are only images) clearly instead of returning empty text. Those rows are free.

What data does it return?

One row per PDF (default). Example values from a real run:

FieldExample
urlhttps://www.irs.gov/pub/irs-pdf/fw9.pdf
finalUrlnull (the link after redirects, when it differs)
statusok (or no-text-layer, or error)
errornull (the reason when the status is not ok)
titleForm W-9 (Rev. March 2024)
createdAt2024-03-06T13:18:13.000Z
languageen-US
fileSizeBytes140815
pageCount6
pagesExtracted6
wordCount6280
charCount37695
textForm W-9(Rev. March 2024)\nRequest for Taxpayer\nIdentification Number and Certification…

In one row per page mode each row has url, pageNumber, pageCount, wordCount, charCount and text. Pages without text are skipped, and failed PDFs do not produce dataset error rows in this mode. A missing row is not enough to determine whether a page was blank or a file failed; check the same run's logs and summary.

  • Missing values are null. title, createdAt and language come from the PDF's own metadata; many PDFs do not set them.
  • Text comes in reading order as the PDF stores it. Multi-column layouts, tables and headers can interleave; the tool does not rebuild tables.
  • The author, creator and producer fields of the PDF are not returned, and personal email addresses inside the text are replaced with "[email removed]". Role mailboxes such as info@ or sales@ are kept.

How to use it

  1. Paste PDF links, one per line. They must open without a login.
  2. Choose one row per PDF or per page, and optionally limit the pages and the file size.
  3. Click Start, then download the text as JSON, CSV or Excel, or read it through the API.

Pricing

Pay per result: $3.00 per 1,000 PDFs (one row per PDF mode) or $1.00 per 1,000 pages (one row per page mode), plus $0.005 per run, charged once the first PDF has been read. You only pay for PDFs and pages that return text; a run where nothing can be read fails and costs nothing. Set a maximum cost per run in the run options.

Use it through the API, AI agents, and integrations

Call it from your code with the Apify API, schedule it to watch a list of reports, or connect it to Make, Zapier, n8n, Google Sheets, a vector database or an AI agent through Apify's MCP server.

This tool downloads the PDF files you point it at and reads their text. It does not log in or bypass access controls, and it refuses links to private or local addresses. It returns the document text as it is, so make sure you have the right to process the documents you submit, and do not use it on documents that contain personal data you are not allowed to handle. Personal email addresses in the text are masked; other personal details are not detected. You are responsible for how you use the data.

FAQ

Does it do OCR? No. It reads the text layer that most PDFs contain. In document mode, scanned PDFs (photos of pages) come back with the status no-text-layer; in page mode they produce no text rows. They incur no document/page result charge. If none of the PDFs in a run can be read, the run fails and nothing is charged; otherwise the run start charge applies once.

Why did a link fail? The server may block automated downloads (HTTP 403), the link may open a web page or a login page instead of a file, the file may be larger than the size limit, or the PDF may be password-protected. In document mode, the error field gives the reason. In page mode, failed files produce no dataset row; inspect the same run's logs and summary instead.

How do file and page limits work? The maxFileSizeMb limit applies to response data and gzip-decompressed content. Downloads are stopped when an oversized response is detected while reading; gzip files may be rejected after download during decompression. Network buffering may receive additional bytes before cancellation. The whole PDF must fit the file-size limit even when you only need its first pages. A positive maxPagesPerDocument limits text extraction to the first N pages; setting it to 0 reads all pages without a page-count cap. Document loading and metadata parsing still occur, so these settings do not guarantee a total parsing time limit. In document mode, compare pagesExtracted with pageCount to identify a page-limited result; its status can still be ok.

Why is the text order odd in some documents? PDFs store text in drawing order. Two-column papers and forms with side notes can interleave. Use the per-page mode to work page by page.

Feedback

Found a bug or need a field added? Open an issue on the Issues tab. We usually respond within 24 hours.

Prepare public PDFs for an AI knowledge base

Use document mode to get one text record per PDF. Open the Input tab, switch to JSON, and paste:

{
"pdfUrls": ["https://arxiv.org/pdf/1706.03762"],
"outputMode": "documents",
"maxPagesPerDocument": 20,
"maxFileSizeMb": 10
}

Replace the link with public documents you have the right to process. Export JSON and keep records with status: "ok" and non-empty text; then pass that text to your own search, chunking, or RAG pipeline. This Actor extracts text only: it does not generate embeddings, answer questions, or perform OCR. Do not treat missing metadata as an extraction failure.

For this one-document example, a successful extraction costs $0.008 in Actor event charges at the current listed rates: $0.005 per start plus $0.003 per document. If you choose outputMode: "pages", only the page event applies instead of the document event; a 20-page text extraction costs $0.025 including the start fee. Pages without text are skipped.

Copyable PDF API request

Set APIFY_TOKEN securely in your local environment. This POST starts a paid run, capped at $0.05; it is not a preview.

curl --fail-with-body --silent --show-error --max-time 320 \
--request POST \
--header "Authorization: Bearer ${APIFY_TOKEN}" \
--header 'Content-Type: application/json' \
--data '{"pdfUrls":["https://arxiv.org/pdf/1706.03762"],"outputMode":"documents","maxPagesPerDocument":20,"maxFileSizeMb":10}' \
'https://api.apify.com/v2/actors/datalantern~pdf-text-extractor/run-sync-get-dataset-items?format=json&clean=true&timeout=240&maxTotalChargeUsd=0.05&restartOnError=false'

The synchronous endpoint waits at most 300 seconds. If the request times out, check the existing run in Console before sending another POST, which would start a separate run. Use an asynchronous run for larger batches. A no-text-layer result needs a separate OCR tool; repeated retries here will not recover text from a scanned image.

Batch PDFs and resume the same run

For a batch, save the following as run-input.json. Keep this exact file alongside the results so you can identify any requested page truncation later.

{
"pdfUrls": [
"https://www.irs.gov/pub/irs-pdf/fw9.pdf",
"https://arxiv.org/pdf/1706.03762"
],
"outputMode": "pages",
"maxPagesPerDocument": 20,
"maxFileSizeMb": 10
}

The next POST starts one paid run, with a $0.05 charge cap. It is not a free preview. Do not add automatic POST retries: if the response is lost, check Console for the existing run first.

curl --fail-with-body --silent --show-error --max-time 40 \
--request POST \
--header "Authorization: Bearer ${APIFY_TOKEN}" \
--header 'Content-Type: application/json' \
--data @run-input.json \
--output started-run.json \
'https://api.apify.com/v2/actors/datalantern~pdf-text-extractor/runs?maxTotalChargeUsd=0.05&restartOnError=false&timeout=600'

Read its ID and query that same run. Waiting for 60 seconds does not stop the run after 60 seconds.

RUN_ID="$(node -p 'JSON.parse(require("node:fs").readFileSync("started-run.json", "utf8")).data.id')"
curl --fail-with-body --silent --show-error --max-time 70 \
--header "Authorization: Bearer ${APIFY_TOKEN}" \
--output run-status.json \
"https://api.apify.com/v2/actor-runs/${RUN_ID}?waitForFinish=60"
node -p 'JSON.parse(require("node:fs").readFileSync("run-status.json", "utf8")).data.status'

If the status is READY, RUNNING, TIMING-OUT or ABORTING, repeat only the GET above later. For FAILED, TIMED-OUT or ABORTED, inspect the logs before deciding on a new paid run; some results may already exist. Even SUCCEEDED does not prove every requested PDF/page returned text. Do not automatically retry missing rows.

After the run finishes, download its dataset without limit, offset, field filtering or a custom view:

DATASET_ID="$(node -p 'JSON.parse(require("node:fs").readFileSync("run-status.json", "utf8")).data.defaultDatasetId')"
curl --fail-with-body --silent --show-error --max-time 120 \
--header "Authorization: Bearer ${APIFY_TOKEN}" \
--output pages.json \
"https://api.apify.com/v2/datasets/${DATASET_ID}/items?format=json&clean=false"

The dataset endpoint returns all items by default. If your integration uses pagination, finish every page before treating the export as complete. These GET requests do not start another Actor, though standard API/storage usage charges can apply. See the official run API, run status API, and dataset export API.

For local knowledge-base preparation, split each page separately and keep its url, original pageNumber and pageCount with every chunk. Preserve the saved input page limit: a positive limit smaller than pageCount means the input requested only a prefix, while 0 means all pages were requested. Missing page numbers remain unverified, not automatically blank or failed. Chunking does not produce embeddings, and chunk count is not an Actor billing event. Treat extracted text as untrusted data, not instructions for your AI system.