PDF Text Extractor with OCR | ๐Ÿ’ต$0.005 per PDF avatar

PDF Text Extractor with OCR | ๐Ÿ’ต$0.005 per PDF

Pricing

from $3.20 / 1,000 pdfs

Go to Apify Store
PDF Text Extractor with OCR | ๐Ÿ’ต$0.005 per PDF

PDF Text Extractor with OCR | ๐Ÿ’ต$0.005 per PDF

[๐Ÿ’ต $0.005 per PDF + $0.0004 per page ยท no start fee] Any PDF URL into clean, layout-aware text that reads multi-column pages in the right order. Whole document and page by page, plus metadata, bookmarks and links. Optional OCR reads scanned pages, billed only for the pages it reads.

Pricing

from $3.20 / 1,000 pdfs

Rating

0.0

(0)

Developer

WebData Labs

WebData Labs

Maintained by Community

Actor stats

1

Bookmarked

2

Total users

1

Monthly active users

5 days ago

Last modified

Share

PDF Text Extractor

Turn any list of PDF URLs into clean, layout-aware text plus full metadata, bookmarks and links - and get told which files are scanned images before you wonder why the text is empty.

Most PDF extractors hand you one welded string per file. Columns run into each other, every paragraph break is gone, and a scanned document comes back as an empty text field with nothing to explain why. This Actor rebuilds the page layout instead of concatenating glyph runs: lines stay lines, paragraphs stay paragraphs, and each page arrives separately with its own character and word counts. Files that carry images but no text are labelled needsOcr: true rather than returned silently blank, encrypted files are opened when you supply the password and reported honestly when you do not, and anything that failed to download comes back as an error row you are never charged for. Scanned pages can be read too: turn ocr on and only the pages that actually needed it are recognised and billed. No API key, no browser, no proxy.

โœ… What you get / โŒ what this isn't

โœ… What you getโŒ What this isn't
Layout-aware text with real line and paragraph breaksNot items.join(" "), which welds every line into one blob
Multi-column pages in reading order, one column finished before the nextNot two columns spliced into each other line by line
pages[] with the text, charCount, wordCount and size of each pageNot a single document string you have to re-split yourself
needsOcr set when unreadable pages actually carry imagesNot an empty text field with no explanation
Optional OCR that reads scanned pages, billed only for pages it actually readNot a scan you have to route to a second tool and pay for twice
passwords tried against encrypted files, with wasDecrypted recordedNot a hard failure on every protected PDF
outline[] - the bookmark tree with the page each entry points toNot metadata only, with the document structure thrown away
links[] - every clickable link annotation with its pageNot URLs you have to regex back out of the text
chunks[] - page-tagged chunks sized for embeddings, on requestNot a raw dump you must split before it reaches a vector store
Priced per PDF and per page, so short files stay cheapNot one flat fee that makes a 1-page invoice subsidise a 300-page manual
No start fee, and failed downloads are never chargedNot a per-run fee on top of the per-file price

๐Ÿ”Ž Why use this Actor

  • The layout is rebuilt, not guessed. PDF stores positioned glyph runs, not lines. The page is first cut into blocks: a vertical gutter separates columns, a full-width horizontal band separates a banner headline from the body beneath it. Only inside a block are runs bucketed into lines, ordered left to right, and their vertical gaps turned back into paragraph breaks. That is the difference between text an LLM can read and text it has to untangle.
  • Two-column papers come out readable. Grouping lines by baseline alone, which is what almost every extractor does, splices the left column's line into the right column's sentence. Here each column is finished before the next one starts, and words broken across a line break are rejoined.
  • Scanned files are named as scanned. A page under 40 characters is probed for image operators, and needsOcr is only set when those pages really do carry images. A blank cover sheet is not flagged, and a 300-page scan is not delivered as a successful empty run.
  • Password-protected PDFs are supported. Put candidate passwords into passwords and each file is opened with the first one that works. isEncrypted and wasDecrypted are recorded either way, so you can tell "protected and opened" from "protected and skipped".
  • Every failure is a row, and a free one. A 404, a timeout, an HTML login page served instead of a file, a corrupt document: each returns a row with status: "error" and a specific errorType, the run keeps going, and no charge event fires. You pay for PDFs that were actually read.
  • Ready for RAG without a second step. Set chunkChars and the text comes back pre-split on paragraph boundaries, never across a page, with the page number on every chunk so your citations survive.
  • Competitor input JSON just runs. urls, startUrls, pdfUrls, fileUrls, links, maxResults, maxItems and maxUrls are all accepted, so a config copied from another PDF Actor's docs does not fail validation on the first run.
  • Nothing to tune. No proxy, concurrency, retry or timeout fields. Pacing, retries with backoff and download limits are handled internally and included in the price. It runs on plain HTTP with no browser, which is why it is fast and cheap.

๐Ÿ‘ฅ Who it's for

AI and RAG engineers, legal and compliance teams, financial and procurement analysts, researchers and developers who have a list of PDF links and need the text behind them in a structured, machine-readable shape.

  • Load contracts, filings, tenders or manuals into a vector store with page-level citations intact.
  • Feed report text straight into an LLM for summarisation, classification or extraction.
  • Screen a document set, separate the digital-native files from the scans, and read the scans in the same run when you want them.
  • Build a searchable index of every PDF a website publishes, with title, author and creation date attached.
  • Pull the bookmark tree out of long documents to map their structure before reading any of them.
  • Extract every outbound link from a document set for reference or citation analysis.

Example tasks

Ready-made configurations, each set up for one job:

โš™๏ธ How to extract text from a PDF

  1. Open the Actor and paste your PDF links into urls. Any public HTTP or HTTPS link works, including files on S3, Google Cloud Storage, an Apify key-value store or your own server. Redirects are followed.
  2. Leave maxPdfs at 100 for a first run. It is a budget guard, not a hard ceiling: raise it for a big batch, or set 0 to process the whole list.
  3. Leave includePageText on if you want each page separately. Turn it off for smaller rows when the whole-document text is all you need.
  4. If any of your files are protected, add the candidate passwords to passwords.
  5. Click Start. Results appear in the Documents view on the Output tab, with the full text in the Extracted text view, and export as JSON, CSV, Excel, XML or RSS.

Load a document set into a vector store

Set chunkChars to your embedding model's window, for example 1200. Each row then carries a chunks array where every chunk is a paragraph-aligned slice of a single page, tagged with page and index. Feed chunks straight into your embedding step: the page number travels with the text, so answers can cite a page rather than a file.

Process a folder of PDFs on a schedule

Create a Saved Task with your URL list, then add a schedule on the Actor's Schedules tab. Because sha256 is returned for every file, you can detect a document that changed between runs without diffing its text.

๐Ÿ“ฅ Input

{
"urls": [
"https://arxiv.org/pdf/1706.03762",
"https://www.irs.gov/pub/irs-pdf/fw9.pdf"
],
"maxPdfs": 100,
"includePageText": true,
"includeOutline": true,
"includeLinks": true,
"chunkChars": 1200,
"ocr": false,
"ocrLanguage": "eng",
"maxOcrPages": 50,
"passwords": []
}
  • urls - the PDF links to read. Accepts full URLs and bare domains with a path. Aliases startUrls (plain strings or { "url": ... } objects), pdfUrls, fileUrls and links are merged into the same list and deduplicated.
  • maxPdfs - budget cap on how many PDFs this run processes. Default 100. Set 0 for no cap. Aliases: maxResults, maxItems, maxUrls.
  • includePageText - add the pages array with per-page text and counts. Default true.
  • includeOutline - extract the bookmark tree into outline. Default true.
  • includeLinks - extract clickable link annotations into links. Default true.
  • chunkChars - optional. Split the text into page-tagged chunks of about this many characters for embeddings. Default 0, which skips chunking. Values below 200 are raised to 200.
  • ocr - read scanned pages by recognising the page image. Default false, because OCR costs extra and most PDFs do not need it. Only pages with no text layer that actually contain a scan are processed and billed.
  • ocrLanguage - language of the scanned text: eng, deu, fra, spa, ita, por, nld, pol, ukr or rus. Language names (English, German) and two-letter codes (en, de) are accepted too. Default eng. Only used when ocr is on.
  • maxOcrPages - cap on scanned pages read per file, so one long scan cannot surprise you. Default 50. Set 0 for no cap. When the cap stops the work the row carries ocrTruncated: true.
  • passwords - optional list of passwords tried in order against encrypted files. Default [].

๐Ÿ“ค Output

One row per input URL. A short sample of the most-scanned fields:

urlfileNamepageCounttitlecharCountneedsOcrisEncryptedstatus
https://arxiv.org/pdf/1706.037621706.03762v7.pdf15null39720falsefalseok
https://www.irs.gov/pub/irs-pdf/fw9.pdffw9.pdf6Form W-9 (Rev. March 2024)37704falsefalseok
https://example.com/scan.pdfscan.pdf6imagemagick-images0truefalseok
https://example.com/missing.pdfnullnullnullnullnullnullerror

Every row carries the full field set below.

FieldTypeWhat it is
recordTypestringAlways pdf.
urlstringThe URL exactly as you supplied it.
finalUrlstringThe URL the download landed on after redirects.
statusstringok or error.
httpStatusintegerHTTP status of the download.
fileNamestringFrom Content-Disposition if the server sent one, otherwise from the URL path.
fileSizeBytesintegerSize of the downloaded file.
contentTypestringContent type the server declared.
sha256stringHex digest of the file, for change detection and deduplication.
pageCountintegerPages in the document.
processedPagesintegerPages actually read. Equals pageCount unless the per-file time budget was hit.
truncatedAtPageintegerThe page reading stopped at, or null when the whole file was read.
pdfVersionstringPDF format version, for example 1.7.
isEncryptedbooleanThe file uses PDF encryption.
wasDecryptedbooleanOne of your passwords was needed to open it.
isLinearizedbooleanThe file is optimised for fast web viewing.
hasAcroFormbooleanThe file contains form fields (AcroForm or XFA).
hasSignaturebooleanThe file carries a digital signature.
title, author, subject, keywordsstringDocument information dictionary, null when absent.
creator, producerstringThe application that authored the content and the one that wrote the PDF.
languagestringDocument language tag, for example en-US.
createdAt, modifiedAtstringCreation and modification timestamps, normalised to ISO 8601 UTC.
textstringThe whole document as layout-aware text, pages joined by a blank line.
textTruncatedbooleanThe text hit the 3,000,000 character row limit and was cut.
charCountintegerCharacters extracted across all pages.
wordCountintegerWhitespace-separated tokens in text.
avgCharsPerPageintegercharCount divided by processedPages.
emptyTextPagesintegerPages that yielded no text at all.
needsOcrbooleanThe document has unreadable pages that carry images and were not read. With ocr on it means "still unread", so a fully OCR'd scan comes back false.
ocrUsedbooleanAt least one page was read by OCR.
ocrPagesintegerPages OCR actually read, and exactly what you are charged for.
ocrCharCountintegerCharacters that came from OCR rather than from the file's own text layer.
ocrConfidenceintegerAverage confidence over the OCR'd pages, 0-100. Low values mean a poor scan - see the caveats.
ocrLanguagestringThe language OCR ran with, or null when it was not used.
ocrTruncatedbooleanmaxOcrPages stopped the work with scanned pages left over.
pagesarray{ page, text, charCount, wordCount, width, height, rotation, ocrApplied, ocrConfidence } per page, when includePageText is on.
outlinearray{ level, title, page, url } per bookmark, nested depth flattened into level.
linksarray{ page, url } per clickable link annotation, deduplicated.
linkCountintegerNumber of entries in links.
chunksarray{ index, page, charCount, text } per chunk, when chunkChars is set.
chunkCountintegerNumber of entries in chunks.
errorstringHuman-readable failure message, null on success.
errorTypestringOne of not_found, http_error, timeout, network_error, not_a_pdf, file_too_large, password_required, invalid_pdf, parse_error.
durationMsintegerDownload plus extraction time for this file.
extractedAtstringISO 8601 timestamp of when the row was produced.

The run also writes a summary record to the key-value store with requested, extracted, failed, pagesRead, charactersExtracted, needsOcr, encrypted and ocrPages counts.

๐Ÿ’ต How much does it cost?

Charges drop with your Apify plan and there is no start fee: a small amount per PDF opened, plus a per-page amount for what is actually inside it. A one-page invoice costs a fraction of a 300-page manual, which a flat per-file price cannot express. OCR is charged separately and only when you turn it on.

FREEBRONZESILVERGOLDPLATINUMDIAMOND
per PDF$0.005$0.0044$0.0038$0.0032$0.0026$0.002
per page$0.0004$0.00035$0.0003$0.00025$0.0002$0.00016
per OCR page$0.010$0.0088$0.0076$0.0064$0.0052$0.004

The OCR charge applies only to pages OCR actually read: a page that already had text, a blank page, and a page the engine could not read cost you nothing extra. With ocr off it never applies at all.

What that works out to on the free tier:

DocumentHereAn Actor at $0.01 per PDF + $0.0005 per page
1-page invoice$0.0054$0.0105
26-page report$0.0154$0.0230
142-page manual$0.0618$0.0810

Rows that failed to download or parse carry status: "error" and are not charged - neither the file nor its pages - so a stale link list costs you nothing beyond the platform compute. Pages that could not be read inside the per-file time budget are not charged either.

Apify platform usage (compute and data transfer) is billed separately by Apify and is small: there is no browser and no proxy, and a 15-page research paper is read in well under a second.

๐Ÿ” Run it on the Apify platform

Schedule the Actor for a recurring document sweep, call it from the Apify API or any Apify client, and export results as JSON, CSV, Excel, XML or RSS. Datasets connect to Make, Zapier, Google Sheets, Airtable, Slack and webhooks, so extracted text can land in a spreadsheet, a warehouse or a vector store without glue code.

Use with AI agents via MCP

The Actor is exposed over Apify's MCP server, so an agent can call it directly and read a PDF on its own.

Claude Code

$claude mcp add apify --transport http https://mcp.apify.com --header "Authorization: Bearer <YOUR_APIFY_TOKEN>"

Cursor or Claude Desktop - add to your MCP config:

{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com",
"headers": { "Authorization": "Bearer <YOUR_APIFY_TOKEN>" }
}
}
}

Then ask the agent to run webdata_labs/pdf-text-extractor with the PDF URL. Structured parameters exist precisely so an agent can fill urls: ["https://..."] without constructing anything.

Use this Actor in n8n

n8n ships an Apify node. Add it, authenticate with your Apify API token, choose Run Actor, select webdata_labs/pdf-text-extractor, and paste the input JSON above. Use Run Actor and get dataset to get rows back in the same step, then feed them into an embeddings node, a Sheets node or a database. Pair it with an n8n Schedule Trigger for a recurring document sweep.

โš ๏ธ Limits and caveats

Written so these do not surprise you later.

  • OCR accuracy depends on the scan, and the row tells you which you got. On a clean 150 DPI scan expect around 95% of words read correctly. A skewed, low-resolution or heavily compressed scan does measurably worse - in our own testing a 1.5 degree skew at 110 DPI dropped a dense form to 56%, while ordinary body text held 87%. ocrConfidence moves with it (88 versus 60 in those two cases), so check that field before trusting the text downstream. OCR also does not reconstruct table structure.
  • Stamped scans are handled. Court ECF headers, fax banners and Bates numbers leave a scanned page with a little real text over an image of the actual content. Those pages are OCR'd too, and the result is only kept when it is substantially richer than the stamp, so a page whose text layer was already fine is never charged for.
  • OCR is slow compared with reading a text layer. Roughly 5-10 seconds per scanned page, against a fraction of a second for a normal page. maxOcrPages (default 50) keeps a single long scan from taking over the run.
  • With ocr off, scanned pages are flagged, not read. You get needsOcr: true, emptyTextPages and charCount: 0, and you are still charged for the document, because it was opened and analysed.
  • Column detection is geometric, not perfect. Columns are found from the whitespace gutter between them, so a page whose gutter is filled edge to edge by a wide figure or a full-width table can still fall back to reading across. Ordinary two- and three-column layouts, forms and reports come out in reading order.
  • Words broken across a line break are rejoined without the hyphen. informa- plus tion becomes information, which is what a search or embedding step needs. The cost is that a genuine compound broken at exactly the line end loses its hyphen too: position-wise can come back as positionwise. Line-break hyphenation is far commoner than line-final compounds, so this is the better default, but it is a trade-off rather than a free win.
  • Tables come out as text, not as a grid. Cell contents appear in reading order with spacing preserved. There is no table structure detection and no cell array, because a wrong table shape is worse than none.
  • With ocr off, only the first 5 low-text pages are probed for images. That is what feeds needsOcr. A 300-page scan whose first five pages are genuinely blank covers would not be flagged. With ocr on the probe reaches as far as maxOcrPages, so the flag is exact over everything OCR was allowed to consider. emptyTextPages and avgCharsPerPage are exact for every page and are the fields to check if that edge case matters to you.
  • Encrypted files need your password. Without a working entry in passwords the row comes back errorType: "password_required" and is not charged. There is no password cracking and none will be added.
  • Files are capped at 50 MB and 4 minutes of reading each, and the time budget grows when OCR is on, because recognition is the slow part. A file over the size limit returns file_too_large. A document too long to finish inside the time budget returns the pages it read, with truncatedAtPage set and processedPages below pageCount, rather than failing the whole row.
  • A row's text is capped at 3,000,000 characters by Apify's dataset item size limit. textTruncated tells you when that happened; pages[] still carries what was read.
  • title is often null, and that is the file's fault. Many PDFs, including most LaTeX output, carry no document title. The Actor reports what the file contains and never invents a title from the first line.
  • XFA-only forms give little text. Some form PDFs, particularly government ones, keep their content in an XFA layer. hasAcroForm will be true while the text is thin. The static page text is what comes back.
  • No login-gated files. A URL behind a session or a paywall returns whatever the server sends an anonymous client, usually an HTML page, which is reported as not_a_pdf and not charged.

This Actor downloads files from URLs you supply, using the same anonymous HTTP request a browser makes, and reads the text layer the file already contains. It does not log in, does not bypass access controls, and does not crack encryption: a protected document is opened only with a password you provide yourself.

Reading a document you are entitled to access is not in itself a legal problem, and in the US the Ninth Circuit's decision in hiQ Labs v. LinkedIn held that accessing publicly available pages does not violate the Computer Fraud and Abuse Act. What you do next is regulated separately. Copyright stays with whoever wrote the document, so extracted text is licensed material, not free content: quoting, redistributing or training on it may need permission. If the documents contain personal data, GDPR and similar laws apply to your processing of it. Where the files sit behind a site's terms of service, those terms govern your relationship with that site. None of this is legal advice - if your use case is redistribution or model training rather than internal analysis, talk to a lawyer.

Part of a document and public-record stack. Each one produces the links or the context this Actor turns into text.

  • EU Tenders API - TED procurement notices come with PDF specification documents attached. Pull the notices, then run their document URLs through this Actor to search the specifications themselves.
  • US Building Permits API - permit records that reference filed plan sets and notices as PDFs.
  • SEC Financials API - EDGAR filings, where the narrative sections you actually want to read are the attached documents.
  • Spain BOE Subastas API - auction announcements published as official PDF bulletins.
  • Bulk URL Status Checker - run a document link list through it first to drop the dead links, so every URL you pay to extract actually resolves.
  • Website Contact Extractor - the same sweep across a site's HTML pages, for the contact details that live outside its documents.
  • Sanctions Screening API - screen the names and entities you pull out of contracts and filings against consolidated sanctions lists.

โ“ FAQ

Do I need an API key or an account with anyone?

No. The Actor fetches the file over plain HTTPS and reads it locally. There is no third-party PDF service, no API key and no per-page conversion fee.

Am I charged for PDFs that fail?

No. A charge event fires only when a document was opened and its text delivered. Dead links, timeouts, HTML pages served instead of a file, corrupt documents and password-protected files you could not open all return a row with status: "error" and cost nothing.

Can it read scanned documents?

Yes, with ocr turned on. Pages that have no text layer but do carry a scan are recognised from the page image, and ocrPages, ocrConfidence and the per-page ocrApplied flag tell you exactly what was read and how well. Only those pages are charged for. Leave ocr off and the Actor simply labels them instead: needsOcr: true with charCount: 0, so you can route them elsewhere.

Why is text empty when the PDF clearly has words in it?

Check needsOcr. If it is true, the words are pixels, not text, which is the case for anything produced by a scanner or a photo - turn ocr on to read them. If it is false and the text is still empty, the file may use a broken or non-embedded font encoding; send us the URL and we will look.

How does it handle password-protected PDFs?

Put the candidates in passwords. Every encrypted file is tried against each in order and opened with the first that works, with wasDecrypted: true recorded on the row. Files that none of them open return password_required and are not charged.

How big and how long can a PDF be?

Up to 50 MB per file, and up to 4 minutes of reading per file. Long documents that exceed the time budget return the pages read so far with truncatedAtPage set, instead of failing.

Is the text good enough for an LLM or a vector store?

That is what the layout reconstruction is for. Lines and paragraphs survive, each page is available separately, and setting chunkChars returns chunks that never cross a page boundary and carry the page number for citations.

Does it keep the tables?

Table text comes back in reading order with spacing preserved, but there is no cell structure. If you need real tables, extract the text here and run a table parser over the page you care about.

Can I extract only the metadata and skip the text?

The metadata always ships. Setting includePageText: false drops the per-page array and keeps the whole-document text plus every counter, which is the smallest useful row.

What happens to a URL that redirects?

Redirects are followed and the landing URL is recorded in finalUrl, so you can see when a permalink resolved somewhere unexpected.

๐Ÿ› ๏ธ Support

Something wrong or missing? Open an issue on the Actor's Issues tab with the run URL, the input you used and what you expected. Include the PDF URL when a specific file misbehaves; that is almost always enough to reproduce it.

โญ Rate this Actor

If this saved you an afternoon, please leave a rating on the Reviews tab. Review count is the main trust signal buyers use on the Store, and the feedback decides what gets built next. If something is broken, please open an issue first so it can be fixed rather than rated low: bugs get fixed faster than they get argued about.

Changelog

0.2 - 2026-08-07

  • Priced per PDF plus per page instead of a flat per-file fee, so short documents cost less and long ones pay their way.
  • Multi-column pages are now segmented into blocks and read column by column instead of across the gutter. Words broken across a line break are rejoined.

0.1 - 2026-08-07

  • First release. Layout-aware text extraction, per-page output, document metadata with ISO 8601 dates, bookmark outline with page numbers, link annotations, scanned-document detection, password support, optional RAG chunking, and per-row error handling with no charge on failure.