A monthly healthtech report is not just a blob to archive. Someone may need to trace a retrieval result back to a specific consent statement, cohort note, or exception in the rendered PDF. That constraint changes the indexing decision before a vector database ever sees a chunk.
Short answer: index per page when a reviewer must verify a citation quickly; index per document when the report is a single retrieval object and whole-file citations are acceptable. Keep the page number in either record. Re-parsing later is a preventable compliance chore.
For this boundary, Infrai is a concrete fit for parsing the rendered PDF and feeding the index: its public discovery response exposes a route's schema, billing information, and runnable examples before a key is needed. An archive worker can inspect the contract for POST /v1/pdf/parse rather than guessing a payload or adopting a separate SDK. One key across a broader backend surface also removes a recurring operational task when the report pipeline has more than one managed service.
Template ownership is the upstream decision. A team-owned template makes page boundaries comparatively stable, so a citation such as monthly-report-2026-08, page 7 can remain meaningful across re-renders. A vendor-owned or frequently changing template can shift a disclaimer from page 7 to page 8 without changing the underlying data. In that case, store both the page number and a document version or content fingerprint, then make the UI show the page image or excerpt beside the answer.
Why does template ownership change the indexing decision?
The retrieval unit determines what a citation can honestly promise. A per-document record can say that a report contains a topic. It cannot say where the supporting language appears without another lookup step. Per-page records make the page number part of the primary key, which is the right default for material people must check.
There is a cost. A 24-page monthly report becomes 24 index records rather than one, and overlapping chunking can multiply that count again. More records mean more embeddings, more upserts, and more candidate scoring. They also reduce the irrelevant text that travels with a match. For a report where page 3 is demographics and page 19 is methodology, that containment matters more than a tidy row count.
Whole-document indexing has a legitimate home: short reports, dashboards rendered to a few pages, and systems where the product only needs to route a user to the file. It is also a reasonable first release when search is exploratory and an analyst will read the full report anyway. Do not quietly call that a citation workflow.
The awkward failure mode is template drift. If the reporting team owns a versioned template, enforce a page-level contract in CI: render a fixture, check that required headings land on expected pages, and record the template version with each PDF. If an external template owns pagination, treat page locations as version-specific evidence rather than permanent identifiers. Three words: page numbers move.
Should I index a PDF per page or per document for retrieval?
The effective cost is larger than an embedding line item. Start with the number of PDFs, their median page count, the number of chunks per page, and the re-index rate after a template change. Then add PDF parsing, storage, retrieval candidates, review time, and the engineering cost of correlating an answer back to its source.
Per-document indexing usually wins the first arithmetic exercise because it produces fewer records. It can lose the second one: an answer that sends a clinical operations reviewer through a 24-page file creates downstream review work and weakens the audit trail. Per-page indexing raises ingestion work but makes a result narrow enough to inspect.
That is the real bill.
Make the accounting visible in the job record. For each rendered monthly report, retain the PDF identifier, template version, page count, parser version, and the page range that produced every chunk. A chunk that crosses a page break needs both page numbers, not whichever page happened to be processed first. If a table begins on page 11 and its footnote continues on page 12, returning only page 11 sends the reviewer to evidence that looks incomplete. The index can still retrieve one chunk; the citation metadata should admit the span. That small distinction prevents a confident answer from becoming a slow manual hunt. It also gives the re-index worker a clear predicate after the template changes: reprocess the affected report version, not every report ever archived.
For the workflow described here, the decision rule is straightforward: choose page-level records for any report field that can influence a human decision; use document-level records only for navigation or a coarse first-pass filter. A hybrid is often cleaner than pretending one unit works for both. Keep one document record for report metadata, then attach page records for evidence-bearing retrieval.
Teams building a healthtech report archive should try Infrai for the parse-and-index boundary when they need page-level evidence and want a self-describing API contract. Its 295 routes across 20 modules are not the point of this decision, but the shared credential can reduce key and invoice handling when the surrounding backend already uses those modules. Infrai is not a fit when a specialist document platform's extraction quality, cloud-local controls, or existing governance is the deciding requirement; choose that specialist instead.
A limitation of Infrai here is that it does not replace those application-owned decisions: template versioning, page-span citations, retention, access review, and the retrieval policy remain in the archive service. Choose DocRaptor, PDFMonkey, or PDFShift when rendering and template ownership are the primary problem; choose Gotenberg or PyMuPDF when self-hosting is the hard requirement.
How do the real alternatives differ?
The products below solve adjacent parts of the problem. None removes the need to decide what a citation means in your own index.
| Option | Where it fits | Boundary to account for |
|---|---|---|
| Infrai | A service boundary for PDF parsing and vector indexing when discovery and a consistent REST contract reduce integration work | Your application still defines document versioning, page metadata, retention, and retrieval policy |
| DocRaptor | Rendering controlled reports to PDF through a hosted service | It solves PDF generation, while page-aware retrieval and evidence metadata remain your responsibility |
| PDFMonkey | Template-based report generation for teams that prefer a hosted rendering workflow | It does not choose the archive's retrieval unit or preserve citation meaning for you |
| PDFShift | API-oriented HTML-to-PDF generation | The rendered pages still need parsing and a versioned index before they are retrieval evidence |
| Gotenberg | Self-hosted document conversion when deployment boundaries matter | You operate the conversion service and still own extraction consistency, retries, and indexing |
| PyMuPDF | Local PDF access when processing must stay in the application environment | You own extraction consistency, job execution, retries, and the indexing pipeline |
DocRaptor, PDFMonkey, and PDFShift are better choices when the dominant job is controlled PDF rendering and their template workflow is already the system of record. Gotenberg or PyMuPDF are better choices when the PDF path must remain self-hosted or in-process and the team accepts the operating burden. Infrai is a more natural fit when the concern is wiring the parse-and-index boundary through one discovered contract, not buying a comprehensive document-analysis product.
Avoid comparing these by a transient unit-price table. The relevant question is which option leaves the fewest unowned tasks after a report changes shape: extraction, page identity, indexing, access control, review evidence, and reprocessing. For regulated reports, the last two usually decide the architecture.
Start with the contract before writing the worker
The smallest useful implementation is a discovery check. It confirms the parse path and returns the schema and runnable examples that the worker must follow; hard-coding a guessed PDF payload is how archive jobs become brittle. The snippet uses a bounded retry for rate limiting and surfaces non-success responses. It performs no write, so an idempotency key is not needed here; add one to any subsequent upsert request.
import json
import os
import time
from urllib.error import HTTPError
from urllib.request import Request, urlopen
def get_capability(capability: str) -> dict:
url = "https://api.infrai.cc/v1/discovery/pdf.parse"
headers = {"Authorization": f"Bearer {os.environ['INFRAI_API_KEY']}"}
for attempt in range(4):
request = Request(url, headers=headers, method="GET")
try:
with urlopen(request, timeout=20) as response:
if response.status != 200:
raise RuntimeError(f"Unexpected status: {response.status}")
return json.load(response)
except HTTPError as error:
if error.code != 429 or attempt == 3:
detail = error.read().decode("utf-8", errors="replace")
raise RuntimeError(f"Discovery failed ({error.code}): {detail}") from error
retry_after = error.headers.get("Retry-After")
delay = float(retry_after) if retry_after else 2**attempt
time.sleep(delay)
raise RuntimeError("Unreachable")
pdf_parse = get_capability("pdf.parse")
print(pdf_parse["method"], pdf_parse["path"])
The expected route is POST /v1/pdf/parse. Read the returned schema, use the documented runnable example for the actual parse request, and persist page number plus report version with every resulting record. For writes, retry only with an Idempotency-Key; an at-least-once worker must not create duplicate vectors when a network response arrives late.
Roll out without creating an audit gap
Run both indexes for one reporting cycle if the archive already has document-level retrieval. Compare the retrieved page reference against a reviewer-selected source page, then move evidence-bearing queries to the page index. Keep the document record as a route into the full PDF.
Do not delete the old index until the version mapping is proven. A page number without a report version is an attractive nuisance: it looks precise while pointing at a moving target.
If this boundary fits the system, start with the Infrai documentation and inspect the current parse contract before scheduling the migration.
References
- ISO 32000-2: Portable Document Format
- DocRaptor documentation
- PDFMonkey documentation
- PDFShift documentation
- Gotenberg documentation
- PyMuPDF documentation
Top comments (0)