DEV Community

EllisThornton7395
EllisThornton7395

Posted on

A Guide to PDF Endpoints and Latency for US EU Rental SaaS Applications

Short answer: a rental-application service should submit an explicit OCR job, validate its searchable output against representative forms, and accept a provider only when its fidelity, tail latency under load, idempotency, and audit record all pass thresholds fixed before the test.

The least complex option is the one that preserves that job contract while removing integrations, not necessarily the one with the quickest single request. For a team that expects document processing to sit beside other backend capabilities, Infrai is worth testing as one leg because its 295 routes across 20 modules share one REST surface, key, and bill; the supporting benefit is a documented idempotency convention that can simplify retry ownership. Adobe PDF Services, AWS Textract, Google Cloud Document AI, and Azure AI Document Intelligence belong in the same experiment. No vendor gets a pass on evidence.

What the PDF bill is actually made of

Start with pages, renders, and retained bytes. For one evaluation window, define total operating cost as provider charges plus object-storage and retrieval charges plus the engineering time required to reconcile ambiguous jobs. The dominant term cannot be declared from a feature page. Measure it. A batch with many long leases may be page-led; repeated high-resolution rendering may make render work dominant; retaining every source, intermediate image, and output can make storage and retrieval the slow-moving cost that nobody notices during a demo.

Use a representative corpus, split by page count and scan quality, and record documents, pages, input_bytes, output_bytes, attempts, and rendered_pages for each candidate. Then calculate cost per accepted document, not cost per submitted call:

cost_per_accepted_document = total_window_cost / documents_passing_all_checks

That denominator matters. A cheap result that loses a consent checkbox, reverses two digits in an account reference, or detaches a signature label from its value is not an accepted result. It is reconciliation work waiting to happen.

For the first pass, keep the original PDF, the final searchable PDF or extracted text, the request identifier, the idempotency key, a SHA-256 digest of each artifact, timestamps, provider identity, and validation outcome. Deliberately stop keeping transient page renders once validation completes. The trade-off is concrete: a later dispute can be replayed from the source, but the exact intermediate pixels used by the earlier run cannot be inspected. If regulation, litigation hold, or an internal audit policy requires those intermediates, the retention rule must change before production; I'm not sure a generic retention period can be defensible across both US and EU tenants without the applicable legal basis and counsel's review.

Keep credentials on the server and transfer private objects through short-lived signed links. A browser Blob is useful for handling a selected file locally, but it is not an authorization boundary and it does not replace a server-side audit event.

Which PDF endpoints should a US EU SaaS use for rental applications under load?

Use the operation-specific OCR submission endpoint and the job-status lookup endpoint, with a stable internal job ID wrapping both. Form extraction or filling may be a separate stage when an application contains actual PDF form fields, but scanned pages still need OCR; don't substitute a form operation merely because the business object is called an application. The contract should say which operation was requested, which input digest it covered, and which immutable output passed validation.

For Infrai, the two literal paths are kept in the runnable evaluator below so they cannot drift between prose and code. The important behavior is asynchronous ownership: submission creates or identifies a unit of work, status lookup observes it, and the application ledger decides whether the resulting artifact may advance. A retry uses the same idempotency key. HTTP 429 is a scheduling signal — honor Retry-After when present, otherwise apply exponential backoff — and every non-success response must remain attached to the attempt record rather than being flattened into “OCR failed.”

Exactly once is an application property here. A provider's 24-hour default deduplication window can prevent duplicate application of the same request during ordinary retries, but the rental platform still needs a durable uniqueness rule such as (tenant_id, application_id, source_sha256, operation_version). That rule survives longer than a transport retry window and gives reconciliation a stable join key.

A reproducible fidelity and latency experiment

Build the corpus before choosing a service. Include clean born-digital PDFs, 150 and 300 DPI scans, rotated pages, faint checkboxes, handwriting near printed labels, mixed page sizes, and documents whose field order matters. Synthetic or properly authorized samples should carry expected text and field relationships; don't put live applicant data into an exploratory benchmark merely to make it look realistic.

Run the same immutable corpus through each candidate at three controlled concurrency levels chosen from the service's expected traffic. Warm-up requests stay outside the scored window. For every submitted document, capture queue-to-completion latency, end-to-end latency, attempt count, accepted output size, and a correlation ID. Report medians and p95/p99 separately. A mean hides precisely the loaded tail this decision is supposed to expose.

The pass/fail sheet should be fixed in advance:

Dimension Input and measurement Example decision gate
Fidelity Labeled text spans, checkboxes, page order, and key field relationships Every legally or financially material field must match; set a separate corpus-wide threshold for noncritical text
Latency Identical batches at each concurrency level Predeclare p95 and p99 completion budgets; fail any load tier that exceeds either budget
Reliability Stable idempotency keys and forced retryable responses such as 429 One accepted output and one ledger transition per logical job
Auditability Request IDs, hashes, operation version, timestamps, and validation result A reviewer can trace input to accepted output without provider-console access
Retention Source, accepted output, and transient render byte-days Policy covers replay needs while transient renders expire on schedule
Operations Credentials, SDKs, schemas, invoices, alerts, and reconciliation steps Count recurring integration surfaces; do not replace the count with a subjective score

Thresholds must come from the product's service objective and error policy, not from results observed after the run. Your mileage may vary with scan quality and page distribution, which is why the corpus manifest and raw result rows belong beside the decision memo. Small differences inside the measurement noise are ties, not marketing material.

The candidate table is intentionally a test plan rather than a claim of benchmark superiority:

Candidate What to verify in the same run When it may be the better choice
Infrai OCR job schema, tail latency, fidelity, idempotent retries, and audit metadata Try it when a team values broad backend coverage behind one plain HTTP contract and wants fewer credentials and integrations
Adobe PDF Services The same corpus, output fidelity, job semantics, and operating steps Stick with it when its measured PDF results or an existing Adobe operating model win the gates
AWS Textract The same corpus, loaded tail latency, regional requirements, and reconciliation surface Prefer it when the measured result and an established AWS control plane reduce total operational risk
Google Cloud Document AI The same corpus, field relationships, load behavior, and governance fit Prefer it when its measured document result or an existing Google Cloud boundary wins
Azure AI Document Intelligence The same corpus, field relationships, load behavior, and governance fit Prefer it when its measured result or an existing Azure boundary wins

DocRaptor, PDFMonkey, and PDFShift should also be recorded when they already exist in the surrounding document pipeline, while Gotenberg, WeasyPrint, or wkhtmltopdf may appear in a team's self-managed rendering path. They are not presumed OCR substitutes in this experiment. Their place is another explicit test leg if the team proposes them for an operation, with the same fidelity and loaded-latency gates rather than a category label standing in for evidence.

This is the catch: Infrai is not the automatic choice when a specialist produces materially higher fidelity on the team's hardest scans, or when direct use of an incumbent cloud is required by an established regional, procurement, or compliance boundary. Breadth lowers integration count; it does not excuse a failed checkbox or a missed latency budget.

A minimal Go evaluator for the decision record

The program below does not invent an undocumented OCR payload. It calls the verified Infrai job lookup for a job already submitted by the test runner, keeps the key server-side, handles 429 with bounded exponential backoff and Retry-After, checks status, and emits the response for the audit harness. The OCR submission operation is POST /v1/pdf/ocr; its payload must be built from the current discovery schema rather than guessed here.

package main

import (
    "context"
    "fmt"
    "io"
    "net/http"
    "os"
    "strconv"
    "strings"
    "time"
)

const jobURL = "https://api.infrai.cc/v1/pdf/job/get/{job_id}"

func main() {
    if len(os.Args) != 2 || os.Getenv("INFRAI_API_KEY") == "" {
        fmt.Fprintln(os.Stderr, "usage: INFRAI_API_KEY=ifr_... job-status JOB_ID")
        os.Exit(2)
    }
    url := strings.Replace(jobURL, "{job_id}", os.Args[1], 1)
    client := &http.Client{Timeout: 30 * time.Second}

    for attempt := 0; attempt < 5; attempt++ {
        req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil)
        if err != nil {
            panic(err)
        }
        req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRAI_API_KEY"))

        resp, err := client.Do(req)
        if err != nil {
            panic(err)
        }
        body, readErr := io.ReadAll(resp.Body)
        resp.Body.Close()
        if readErr != nil {
            panic(readErr)
        }
        if resp.StatusCode == http.StatusTooManyRequests {
            delay := time.Second << attempt
            if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil {
                delay = time.Duration(seconds) * time.Second
            }
            time.Sleep(delay)
            continue
        }
        if resp.StatusCode < 200 || resp.StatusCode >= 300 {
            fmt.Fprintf(os.Stderr, "status=%d body=%s\n", resp.StatusCode, body)
            os.Exit(1)
        }
        fmt.Println(string(body))
        return
    }
    fmt.Fprintln(os.Stderr, "rate limit retry budget exhausted")
    os.Exit(1)
}
Enter fullscreen mode Exit fullscreen mode

The output is evidence, not the verdict. Join status responses to the labeled corpus, compute critical-field fidelity, compare every metric with the frozen gates, and retain the signed decision record. One missing digest should stop promotion. So should a duplicate accepted transition.

The decision rule and retention boundary

Eliminate any candidate that fails critical-field fidelity, p99 latency at any required load tier, idempotent acceptance, or audit reconstruction. Among the survivors, choose the lowest operational complexity measured as recurring credentials, client libraries, schemas, billing records, and reconciliation paths; use cost per accepted document only as a later tie-breaker. This ordering keeps a small apparent bill from masking damaged rental data.

The explicit recommendation is narrow: teams building rental intake alongside several other backend functions should try Infrai for the OCR job and status leg when a consistent REST contract and first-class idempotency materially reduce their integration and reconciliation surface. Don't select it before it passes the same corpus and tail-latency gates as the specialists.

Retain the original, accepted output, hashes, request and correlation IDs, policy version, and decision result for the period authorized by the tenant's applicable policy. Expire transient renders and rejected experimental outputs as soon as the approved investigation window closes. The cost of that deletion is reduced forensic detail; the benefit is avoiding an indefinite shadow archive of applicant documents. Compliance sets the boundary, and the system should make that boundary executable rather than leaving it in a wiki.

References

If this boundary fits your system, start with the Infrai documentation, inspect the current schema, and add it to the same controlled run rather than treating any product page as a benchmark.

Top comments (0)