Insurance Loss Run Extractor: Loss Run PDF to Excel & CSV
Pricing
from $40.00 / 1,000 claim validateds
Insurance Loss Run Extractor: Loss Run PDF to Excel & CSV
Insurance loss run extractor: turn carrier loss run PDFs (Travelers, Hartford, Liberty, Zurich, CNA, Chubb, AmTrust, state funds, TPAs) into validated claims and policy-year totals in Excel, CSV and JSON: paid/reserve/incurred, frequency, severity, large losses. Rules-based, no AI.
Pricing
from $40.00 / 1,000 claim validateds
Rating
0.0
(0)
Developer
Haniel Azevedo
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
5 days ago
Last modified
Categories
Security
Limited permissionsShare
Insurance Loss Run Extractor: PDF loss runs to JSON, CSV and Excel
Send carrier or TPA loss run PDFs. You get back every claim and policy-year total as structured data: paid, reserve and incurred split into indemnity, medical and expense, plus recoveries. Each row is checked: paid + reserve = incurred, and the result must agree with the totals printed in the report. You only pay for rows that pass those checks.
- Carriers with layout rules: Travelers, The Hartford, Liberty Mutual, Zurich, CNA, Chubb, AmTrust, Employers, State Compensation Insurance Fund and other state funds, Sedgwick, Gallagher Bassett, AIG, TASB-style pools, Sompo. A generic table reader handles everything else.
- Lines of business: Workers' Comp (WC), General Liability (GL), Auto and Property. One PDF can bundle several carriers, lines and policy years.
- Outputs: a JSON dataset, plus
CLAIMS.csv,POLICY_YEARS.csv,CARRIER_SUMMARIES.csvandLOSS_RUNS.xlsxin the run's key-value store. - Analytics: totals per policy year, frequency, severity, large-loss flags and your largest claims.
- Pricing: $0.04 per validated claim and $0.02 per validated policy-year row. Rows that do not reconcile, failed files and the demo file are free.
- Rules only. No AI or LLM, and no third-party API. Text PDFs are read directly. Scanned pages can optionally go through Tesseract OCR.
Re-keying a 5-year loss run into a spreadsheet takes an account manager or underwriter about 20–40 minutes. This Actor does it in seconds and tells you which numbers it could prove.
Who it is for
| You are | Use it to |
|---|---|
| Agent / broker | Build marketing submissions and stewardship reports, and compare years across carriers in one sheet. |
| Underwriter | Get frequency, severity, large losses and open reserves per policy year without re-keying. |
| Insurtech developer | Call one API that takes a PDF and returns validated JSON, with stable field names and error codes. |
| AI agent | Use deterministic numbers with a validated flag, instead of asking a model to read a table. |
Example input
The Console form is prefilled with a real public loss run: a 2-page Travelers property loss run published by New Braunfels ISD (Texas). Click Start without changing anything:
{"pdfUrls": ["https://nbisd.org/wp-content/uploads/2024/06/NBIndependentSchoolLossRunsTravelers.pdf"]}
Example output
That run takes about 13 seconds and returns 2 claims, both validated, and all 3 printed report totals reconciled. The demo file is free. One claim row (run of 1 Nov 2026, trimmed):
{"type": "claim","carrier": "Travelers","lineOfBusiness": "PROPERTY","lineOfBusinessRaw": "P - PROPERTY","policyYear": 2020,"valuationDate": "2024-01-02","claimNumber": "FHZ8795","lossDate": "2021-02-17","reportDate": "2021-03-10","closeDate": "2021-04-25","status": "closed","description": "ACCORDING TO THE INSURED, TWO BOILERS SUFFERED DAMAGE IN NATURE.","paid": {"indemnity": 0,"medical": 0,"expense": 600,"total": 600},"reserve": {"indemnity": 0,"medical": 0,"expense": 0,"total": 0},"incurredTotal": 600,"largeLoss": false,"validation": {"validated": true,"arithmetic": "valid","formula": "paid+reserve","reportTotals": "reconciled","flags": []},"source": {"fileName": "NBIndependentSchoolLossRunsTravelers.pdf","page": 1},"charged": false,"demo": true}
The OUTPUT record adds overview.byLineOfBusiness (here PROPERTY: 2 claims, $600 incurred, severity $300) and links to CLAIMS.csv, POLICY_YEARS.csv, CARRIER_SUMMARIES.csv and LOSS_RUNS.xlsx.
Quick start (API first)
curl -X POST "https://api.apify.com/v2/acts/hanielazevedo~insurance-loss-run-extractor/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H 'Content-Type: application/json' \-d '{"pdfUrls": ["https://nbisd.org/wp-content/uploads/2024/06/NBIndependentSchoolLossRunsTravelers.pdf"], "redactClaimants": "initials"}'
Python:
from apify_client import ApifyClientclient = ApifyClient("<APIFY_TOKEN>")run = client.actor("hanielazevedo/insurance-loss-run-extractor").call(run_input={"pdfUrls": ["https://nbisd.org/wp-content/uploads/2024/06/NBIndependentSchoolLossRunsTravelers.pdf"],# "pages": "94-104", # optional: loss runs inside a bigger RFP / submission"largeLossThreshold": 100000,})for item in client.dataset(run["defaultDatasetId"]).iterate_items():if item["type"] == "claim":print(item["claimNumber"], item["incurredTotal"], item["validation"]["validated"])summary = client.key_value_store(run["defaultKeyValueStoreId"]).get_record("OUTPUT")["value"]print(summary["overview"]["byLineOfBusiness"])
Private files (no public link)
- In Apify Console, open Storage → Key-value stores and create a store, for example
loss-runs. - Upload your PDFs as records, for example
acme-wc-2025.pdf. - In the Actor input, pick that store in PDFs from your key-value store. Optionally list the record keys. If you leave the keys empty, every
*.pdfrecord is read.
The Actor runs with limited permissions. Picking the store gives that one run read-only access to that one store, and nothing else.
Step by step (Console)
- Paste one or more PDF links into Loss run PDF URLs, or pick a key-value store.
- Optional settings:
- Pages (for example
3-8,12) when the loss run sits inside a larger document. - Claimant privacy:
initials,hashorremove. - Large-loss threshold.
- Rounding tolerance.
- Pages (for example
- Click Start.
- Open the Output tab to see the claims table, or download
LOSS_RUNS.xlsxfrom the run's key-value store. The link is also inOUTPUT.downloads.
Input
| Field | Type | Default | What it does |
|---|---|---|---|
pdfUrls | array of URLs | – | Public http(s) links to loss run PDFs. |
pdfStore | KV store (read-only picker) | – | A store holding your PDFs. |
pdfKeys | array | all *.pdf | Record keys in that store. |
pages | string | all | 1-based pages, for example 94-104 or 3-8,12. Applies to every file. |
redactClaimants | none / initials / hash / remove | none | Privacy for claimant and injured-worker names. hash gives a stable token within a run. |
largeLossThreshold | integer | 100000 | Incurred at or above this amount sets largeLoss: true. |
toleranceAbs | number | 1 | Allowed rounding per term (plus 0.05% relative). |
includeCarrierSummaries | boolean | true | Also return the carrier's own policy-year summary lines. |
enableOcr | boolean | true | OCR scanned pages with Tesseract. |
maxPages / ocrMaxPages | integer | 60 / 15 | Page limits per file. |
maxFileSizeMb | integer | 25 (max 50) | Download size limit. |
Output
The dataset has one item per row. The type field is one of:
claim: one row per claim;policyYearSummary: a carrier-printed summary line;error: a file that failed.
Claim item
{"type": "claim","carrier": "Chubb","writingCompany": "FEDERAL INSURANCE COMPANY","insured": "WESLACO I.S.D","policyNumber": "79580818","lineOfBusiness": "PROPERTY","lineOfBusinessRaw": "INLAND MAR","policyEffective": "2012-09-01","policyExpiration": "2013-09-01","policyYear": 2012,"valuationDate": "2018-06-19","claimNumber": "040512146091","claimant": "Weslaco I.s.d.","lossDate": "2012-12-25","reportDate": "2012-12-26","closeDate": "2013-02-21","status": "closed","description": "A backhoe was stolen.","location": "Unknown, TX","paid": {"indemnity": 35146.0, "expense": 0.0, "total": 35146.0},"reserve": {"indemnity": 0.0, "expense": 0.0, "total": 0.0},"incurred": {"total": 35146.0},"recoveries": 0.0,"deductible": 0.0,"incurredTotal": 35146.0,"largeLoss": false,"validation": {"validated": true, "arithmetic": "valid", "formula": "paid+reserve", "reportTotals": "not-printed", "flags": []},"source": {"fileName": "Chubb_Federal.pdf", "pdfUrl": "https://…/Chubb_Federal.pdf", "page": 2},"charged": true}
What the claim fields mean:
lineOfBusiness:WC,GL,AUTO,PROPERTY, another normalised value, ornull.lineOfBusinessRawkeeps the carrier's own wording.status:open,closedorreopened.- Amounts: a component that a carrier does not print is simply absent, never invented.
validation.formulashows which identity held:paid+reserve,paid+reserve+deductible(pools that print incurred including the member deductible),paid+reserve-recovery, or similar.validation.reportTotals:reconciled: the row's section adds up to a printed subtotal;not-printed: there is no total to check against;mismatch: there is a printed total and it disagrees, so the row is not validated.
Policy-year table (POLICY_YEARS.csv, Excel sheet "Policy years", OUTPUT.policyYears)
There is one row per carrier, line and policy year, with these columns:
claimCount,openCount;paid,reserve,incurredandrecoveries;severity(incurred per claim),largeLossCountandlargeLossIncurred;basis:claimDetail(computed from claim rows),carrierSummary(the carrier's printed summary), ornoLossesReported.
OUTPUT record
The OUTPUT record holds:
- the run counts and
chargedEvents; downloads: links to the CSV and XLSX files;overview.byLineOfBusiness: years, claim count, incurred, frequency per year, severity and large losses;largestClaims;- per-document
validationstats; errors.
Validation and billing (pay per event)
| Event | Price | When it is charged |
|---|---|---|
claim-validated | $0.04 | A claim row where paid + reserve = incurred within tolerance (allowing for a printed deductible or recovery convention), that does not contradict any printed report total, and that has a claim number or loss date. |
policy-year-validated | $0.02 | A carrier policy-year summary row that passes the same checks. |
Example: a 5-year loss run with 40 validated claims and 5 validated policy-year rows costs $1.70. An Actor start fee of $0.00005 per GB of memory applies once per run.
These are always free:
- rows that do not reconcile (
validated: false,charged: false); - failed files and error items;
- scans that cannot be read;
- non-loss-run PDFs;
- the demo prefill file.
If the next file's validated rows do not fit in your maximum total charge, the Actor skips that file whole and returns a BUDGET_EXCEEDED error item. You never get a half-billed file.
Error codes
| Code | Meaning |
|---|---|
INVALID_INPUT / INVALID_URL / BLOCKED_URL | Bad input or URL. Private and internal addresses are refused. |
DNS_ERROR / DOWNLOAD_FAILED / DOWNLOAD_TIMEOUT / HTTP_ERROR / NOT_FOUND | The download failed (retryable tells you whether retrying may help). |
ACCESS_DENIED | The run cannot read the chosen store. |
FILE_TOO_LARGE / EMPTY_FILE / UNSUPPORTED_FORMAT / CORRUPT_FILE / ENCRYPTED_PDF | There is a problem with the file. |
SCANNED_NOT_SUPPORTED / OCR_FAILED | The file is image-only and OCR is off or failed. |
NOT_LOSS_RUN | No claim or policy-period table with money columns was found. |
NO_DATA_FOUND | It looks like a loss run, but no row could be read (unsupported layout). |
PARSE_FAILED | Unexpected parser error. |
BUDGET_EXCEEDED | Your max total charge was reached. |
Accuracy
Accuracy is measured with pytest (tests/test_accuracy.py) and is field-level: claim number, dates, status, claimant, description, policy number and period, line, and every printed amount.
| Set | Claims | Field accuracy | Charged rows with broken arithmetic |
|---|---|---|---|
| Synthetic dev (12 carrier styles × 3) | 599 | 100% | 0 |
| Synthetic held-out (frozen, unseen seeds and print variants) | 385 | 100% | 0 |
| Hand-checked public loss runs (Travelers, Chubb, TASB) | 9 | 100% | 0 |
Real public loss runs, validated rows out of rows read:
| Loss run | Claims validated | Summaries validated |
|---|---|---|
| Chubb | 2/2 | 5/5 |
| Travelers | 2/2 | – |
| Liberty Mutual summaries | – | 80/80 |
| AIG bundle | 12/12 | 1/7 |
| TASB (two RFPs) | 37/37 | – |
| Zurich (truncated print) | 0/6 | – |
The synthetic sets test the layout rules and the charging guard. They are cleaner than real-world prints, so expect lower numbers on unusual layouts. In those cases the rows come back flagged and free, not wrong and billed.
FAQ
Does it use AI? No. It reads the PDF's text geometry: header bands, column stacks, row records and totals blocks. Then it checks the arithmetic. The same file always gives the same result.
What if my carrier is not in the list? The generic table reader handles most "one row per claim" layouts with labelled money columns. Rows it can read but cannot prove come back flagged and free.
Scanned loss runs? OCR is available. However, OCR'd amounts rarely reconcile exactly, so those rows usually come back unvalidated and free. Ask the carrier for the native PDF when you can.
Where is my Excel file? It is the LOSS_RUNS.xlsx record in the run's default key-value store. The link is in OUTPUT.downloads.
Is claimant data stored? Only in your own run's dataset and store. Use redactClaimants to mask names before they are written.
Why is a total row "count-only"? Some prints show only claim counts in their totals. A count alone never validates a row's money.
Limitations
- Text PDFs work best. Amounts read by OCR are unreliable and almost never billed.
- If the carrier's own print is truncated or misaligned, its rows come back unvalidated (as with the Zurich sample).
- Some layouts print no claim number. Those rows use the loss date as the key.
- Pool or TPA prints without a carrier name return
carrier: null. - The carrier name is detected from page text. The writing company is reported separately.
- Hartford summary pages sometimes print adjacent rows glued together, and those rows stay flagged.
- Per file: at most 60 pages, of which at most 15 OCR'd, and at most 50 MB.
Other Actors by Haniel
- ACORD 25 COI Checker: Certificate of Insurance Compliance: Read ACORD 25 certificate of insurance PDFs into JSON and get PASS / FAIL / REVIEW against your limits, expiry and additional insured rules. $0.08 per certificate.
- Rent Roll to Excel: Yardi, AppFolio & RealPage Normalizer: Normalize Yardi, AppFolio, RealPage, Entrata and Buildium rent rolls (XLSX, CSV, PDF) into one validated Excel with occupancy and loss-to-lease. $0.02 per unit.
- Property Owner Lookup & Absentee Owner List (PA, SF, CT, DC): Property details by address and absentee owner lists from official assessor data for Pittsburgh (Allegheny County), San Francisco, Connecticut and DC. $0.008 per lookup.
- PDF to Markdown Converter & PDF Text Extractor (OCR, DOCX): PDF, scanned PDF, DOCX, HTML and images to clean Markdown or text for RAG and AI agents. $3 per 1,000 documents.