PDF Text Extractor: PDF to Text & Markdown, RAG Chunks
Pricing
from $2.50 / 1,000 pdf converteds
PDF Text Extractor: PDF to Text & Markdown, RAG Chunks
PDF text extractor: PDF URLs or uploads to clean text and markdown, page markers, RAG chunks. $2.50 per 1,000 PDFs. Text PDFs only; scans free.
Pricing
from $2.50 / 1,000 pdf converteds
Rating
0.0
(0)
Developer
Kaizoku Data
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
an hour ago
Last modified
Categories
Share
Turn PDFs you supply (links or uploads) into clean text and markdown: per-page text, page markers, document metadata, word counts and optional chunks with page numbers for search, RAG and LLM pipelines. Keyless, no browser, no proxy.
Price: $2.50 per 1,000 PDFs ($0.0025 per PDF), per PDF, not per page (up to 500 pages each). No start fee on top of Apify's default, and only PDFs that convert are charged.
- Clean text and markdown: de-hyphenated words, paragraphs joined, bullet lists, headings guessed from font size,
<!-- page N -->markers. - RAG-ready chunks: about N characters each, cut at paragraph or sentence boundaries, with overlap and
pageStart/pageEndfor citations. - Your files only: a list of URLs and/or uploads; it never crawls websites. PDFs are processed in memory and not stored.
- Scans cost nothing: this reads the text layer (no OCR). Image-only PDFs come back as
no-text-layerand are not charged, nor are encrypted files or failed downloads. - Quick start: press Start. The prefilled input converts a one-page W3C sample PDF in a few seconds for $0.0025.
Text-layer PDFs only. This Actor reads the text that is already inside a PDF. A scanned PDF or a PDF made of images has no text to read, so it is returned with
status: "no-text-layer"and a clear message, and you are not charged. OCR is not part of v1.
What it does
- Takes a list of PDF URLs and/or files you upload. It does not crawl websites or discover PDFs: you decide which documents it reads.
- Extracts text page by page, repairs words hyphenated across line ends, joins wrapped lines into paragraphs, turns bullets into lists and guesses headings from font size.
- Returns
text(plain),markdown(headings, lists,<!-- page N -->markers and---between pages), optional per-page text and optional chunks. - Reports document properties: title, subject, producer, creation and modification dates, page count and whether the file was encrypted. Author and creator names are never returned.
Input
| Field | What it does |
|---|---|
| PDF URLs | Direct http(s) links, one per line. Default: a small public sample PDF so the default run succeeds. |
| Uploaded PDF files | Upload from your computer to a key-value store (the field offers this), or paste a record URL. See Using your own files below. |
| Page range | 1-5,8 or 3- (page 3 to the end). Applies to every PDF. |
| Max pages per PDF | Only the first maxPages pages of each PDF are converted. Default 100, maximum 500 (a larger value is lowered to 500 with a warning). Longer documents are cut, marked "truncated": true, and pagesProcessed / pageCount show what was converted. Use Page range to pick other pages. The price is per PDF, not per page. |
| Include plain text / markdown / per-page text | Choose which fields you get. Smaller output is faster to store. |
| Chunk size / overlap | 0 = no chunks. Otherwise about N characters per chunk (minimum 200), cut at paragraph, sentence or word boundaries, with overlap. Each chunk has charStart, charEnd, pageStart, pageEnd. |
| Max file size (MB) | Default 50, hard cap 100. Bigger files are skipped, not charged. |
| Download timeout | Per file, default 60 s. |
| Parallel PDFs | 1 to 5, default 3. Lowered automatically if the run memory is small for your file size. |
Example inputs
1. RAG / vector database: chunks with page numbers (markdown off to keep rows small):
{"pdfUrls": ["https://example.com/report.pdf", "https://example.com/manual.pdf"],"chunkSize": 1000,"chunkOverlap": 150,"includeMarkdown": false}
2. Summaries of many reports: only the first 5 pages of each, plain text only:
{"pdfUrls": ["https://example.com/annual-report-2025.pdf", "https://example.com/annual-report-2024.pdf"],"pageRange": "1-5","includeMarkdown": false}
3. Page-by-page text for citations or review (one entry per page in pages, each with its page number and text):
{"pdfUrls": ["https://example.com/contract.pdf"],"includePages": true,"includeMarkdown": false,"maxPages": 300}
Using your own files
The Uploaded PDF files field uses Apify's file-upload editor: it lets you upload a file to a key-value store in your account (new or existing) or paste a URL, and hands the Actor a URL. The Actor then downloads that record like any other URL. If you do it by hand: upload the file as a record to a key-value store in the Apify Console, copy the record URL and paste it into PDF URLs or the upload field. Records of private stores are read with the run's own Apify token, but only when the URL you supplied is itself an api.apify.com key-value-store record URL; the token is never sent to any other host or on a redirect. For other private files use a signed or temporary link.
Output
One dataset item per PDF, in completion order (inputIndex is the position in your input):
{"inputIndex": 0,"url": "https://example.com/report.pdf","fileName": "report.pdf","status": "ok","message": null,"pageCount": 3,"pagesProcessed": 3,"truncated": false,"emptyPages": [],"charCount": 353,"wordCount": 57,"text": "Annual Report 2025\n\nSummary\n\n...","markdown": "<!-- page 1 -->\n\n# Annual Report 2025\n\n## Summary\n\n...\n\n---\n\n<!-- page 2 -->\n\n...","metadata": { "title": "Annual Report", "subject": null, "producer": "LibreOffice", "creationDate": "2025-02-23T15:56:37.000Z", "modDate": null, "isEncrypted": false },"fileSizeBytes": 48211,"processedAt": "2026-10-18T14:00:00.000Z"}
Status values (only ok is charged)
status | Meaning | Charged |
|---|---|---|
ok | Text extracted | Yes, $0.0025 |
no-text-layer | Scan or image-only PDF (fewer than about 3 characters per processed page). Nothing is extracted. | No |
encrypted | The PDF needs a password to open. Passwords are not supported. | No |
error | Download failed, blocked address, not a PDF, too large, damaged file, bad page range, result over the 9 MB dataset item limit, timeout. See message and errorCode. | No |
A PDF with some image-only pages and some text pages is ok; the empty pages are listed in emptyPages.
The run summary (key-value store record RUN_SUMMARY) has counts per status, PDFs charged, bytes read and why the run stopped.
Pricing
Pay per event: $0.0025 per PDF with status ok, plus Apify's standard Actor-start event. There is no additional start fee from this Actor. The price is per PDF, not per page, and covers the first maxPages pages (default 100, max 500). Longer documents are cut at that limit and marked truncated; use Page range (for example in a second run) to convert later pages.
| PDFs converted | Cost |
|---|---|
| 10 | $0.025 |
| 100 | $0.25 |
| 1,000 | $2.50 |
| 10,000 | $25 |
Scans, encrypted files and failed downloads cost nothing. Set Max total charge in the run options to cap spending: the run stops cleanly when the limit is reached and never charges for a PDF it did not deliver. Apify subscription discounts apply on top.
Use it via API, n8n or Make
Get an API token in Apify Console → Settings → API & Integrations, and keep it in an environment variable (APIFY_TOKEN below). Never paste it into shared code.
cURL (starts a run, waits for it and returns one row per PDF; this synchronous endpoint waits up to 5 minutes, fine for a few dozen typical PDFs; for bigger batches use the clients below):
curl -X POST "https://api.apify.com/v2/acts/ataism~pdf-to-text/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"pdfUrls": ["https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"], "chunkSize": 1000, "chunkOverlap": 150}'
Python: PDFs to RAG chunks (pip install "apify-client>=3"; the LangChain lines are optional, pip install langchain-core):
import osfrom apify_client import ApifyClientfrom langchain_core.documents import Documentclient = ApifyClient(os.environ["APIFY_TOKEN"])run = client.actor("ataism/pdf-to-text").call(run_input={"pdfUrls": ["https://example.com/report.pdf"],"chunkSize": 1000,"chunkOverlap": 150,"includeMarkdown": False,})docs = []for pdf in client.dataset(run.default_dataset_id).iterate_items():if pdf["status"] != "ok":print("skipped (not charged):", pdf["url"], pdf["status"], pdf["message"])continuefor chunk in pdf.get("chunks", []):docs.append(Document(page_content=chunk["text"], metadata={"source": pdf["url"], "pageStart": chunk["pageStart"], "pageEnd": chunk["pageEnd"],}))print(len(docs), "chunks ready for your vector store")
(With apify-client versions before 3.0, call() returns a dict: use run["defaultDatasetId"].)
JavaScript (npm install apify-client):
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('ataism/pdf-to-text').call({pdfUrls: ['https://example.com/report.pdf'],chunkSize: 1000,chunkOverlap: 150,});const { items } = await client.dataset(run.defaultDatasetId).listItems();for (const pdf of items) console.log(pdf.url, pdf.status, pdf.pageCount, pdf.chunks?.length ?? 0);
n8n:
- Start with any trigger that gives you PDF links (a form, a sheet row, an email with a link).
- Add the Apify node, operation Run an Actor and get dataset, Actor
ataism/pdf-to-text, input{"pdfUrls": ["{{ $json.pdfUrl }}"], "chunkSize": 1000}. - Use
markdownortextin the next node (an AI summary, a Notion page, a sheet), or split outchunksinto your vector store node.
Make:
- Add the Apify module Run an Actor, choose
ataism/pdf-to-text, put your PDF links in the input JSON and set Run synchronously to Yes. - Add the Apify module Get Dataset Items and map the run's default dataset ID (one bundle per PDF).
- Send
text,markdownorchunksto your next module (OpenAI, Google Docs, Airtable, a webhook).
Privacy: transient processing
- PDFs are downloaded into memory, converted, and discarded. The Actor stores nothing except the dataset items you asked for and the run summary. No copy of the PDF is saved, no temporary files are written, and nothing is kept between runs.
- Document contents are never written to the log. Logs show only the host and path of a URL (not its query string) and the status.
- Metadata is limited to title, subject, producer, dates, page count and encryption flag. Author and creator names are not returned.
- Your dataset belongs to your Apify account and follows your retention settings. The
urlfield echoes what you supplied (including a signed query string if you used one), so treat the dataset as sensitive if your URLs are. - No data is sent to any AI or third-party service. The conversion is plain code running inside the Actor.
Safety
Only http and https URLs to public addresses are fetched. Private, loopback, link-local and internal addresses, and non-http schemes (file:, ftp: and so on), are blocked, including after redirects (at most 5, each re-checked) and at connection time. Files must start with %PDF-.
Limits and honest caveats
- No OCR. Scanned PDFs are not supported (see above).
- Reading order is the order the PDF stores its text in. Single-column text works best. Multi-column layouts, tables, forms, footnotes and right-to-left scripts may come out in a different order than you read them or as run-together lines. Headings are guessed from font size (bold-only headings are not detected). Markdown is a readable approximation, not a layout-faithful conversion.
- Text that is drawn as vector outlines or images is not text to the PDF and is not extracted.
- Paragraphs are not joined across page breaks. De-hyphenation joins a line ending in a letter plus
-with a following lowercase word; a real hyphenated compound at a line break (well-/known) is joined aswellknown. - PDFs that open without a password but carry owner restrictions (no printing, no copying) are processed; only files that require a password to open are
encrypted. - Maximum 100 MB per file, 5,000 PDFs per run, 500 pages converted per PDF (default 100), a 180 s processing limit per PDF, 9 MB per dataset item. Memory: default 1,024 MB (a PDF takes a few times its size in memory while it is parsed, and three run in parallel); raise it for many large files.
- You are responsible for having the right to process the documents you give this Actor (copyright, licences, confidentiality, data-protection law). The Actor does not check this and does not find documents for you.
FAQ
Can it read scanned documents? No. There is no OCR. Run an OCR tool first and then give this Actor the searchable PDF.
Can it find PDFs on a website? No. It only reads the URLs and files you give it.
Why was my PDF not charged? Only ok is charged. Check status and message.
A PDF returned no-text-layer but I can select text in my viewer. Some PDFs draw text as outlines. If the viewer can really select text, send the URL and we will look at it.
How do I get chunks for a vector database? Set chunkSize (for example 1000) and chunkOverlap (for example 150); use chunks[].text with pageStart / pageEnd as metadata.
Does the default run work without setup? Yes. It converts a small public W3C sample PDF (dummy.pdf) in a few seconds.
Open-source components
Text extraction uses pdf.js (pdfjs-dist 6.4.299, Apache-2.0, Mozilla) and the Apify SDK (apify 3.7.2, Apache-2.0). Their dependencies are permissively licensed (MIT, ISC, BSD, Apache-2.0). The optional canvas package of pdf.js (MIT) is not installed because nothing is rendered.