I turn messy websites, PDFs and spreadsheets into clean, verified data, and add AI where it measurably helps.
Client needs: one clean spreadsheet from a trade-fair exhibitor directory — one row per email address, at most one contact per company, country as an ISO 3166-1 alpha-2 code, no duplicate companies, and a confidence score, because the work is paid per valid contact.
Delivered: Measured in this build, on a 300-company sample of the live directory: 248 sites crawled, 507 rows exported (359 with an email, 355 distinct addresses), 353 of 355 on a domain with an MX record, confidence bands 0 High / 88 Medium / 271 Low. On the Frankfurter Buchmesse 2026 predecessor prototype: 3,319 exhibitor entries from the API, 2,667 unique companies after dedupe, 256 companies with at least one email (9.6% of all; 42% of sites with a website), 252 / 256 emails on a domain with an MX record, 0 high-confidence rows without a paid data provider.
Client needs: a folder of PDF invoices, receipts and purchase orders in mixed layouts turned into one Excel workbook and one JSON file per document, on a schedule, without re-keying — and a clear list of which cells to double-check before trusting the numbers.
Delivered: Measured in this build against a labelled ground-truth set, indicative of these documents and not a benchmark: 7 real documents across 4 layouts, 37 fields checked — rules-only accuracy 62.2% (23/37), hybrid rules-plus-LLM 86.5% (32/37), hybrid review rate 52.4%, cost $0.0000/doc on gemini-3.5-flash-lite. On 6 deliberately hard synthetic layouts, 34 fields — rules-only and hybrid both 61.8% (21/34), review rate 30.6%; the LLM fallback added nothing on that set and the flat result is published.
Client needs: thousands of rows of free text — support tickets, reviews, complaints, inbound leads — tagged with a category and a one-line summary, the unsure rows flagged for a human, and a stated accuracy against real labels plus a cost per thousand rows.
Delivered: Measured in this build, one run on 2026-09-06 over a seeded CFPB Consumer Complaint sample with gemini-3.5-flash-lite: accuracy 0.804, macro-F1 0.647, 2,540 of the committed 4,000 rows scored, 1,460 rows left pending when the free tier throttled, 0 errors, measured cost $0.0000, projected cost ~$0.077 / 1,000 rows at OpenAI gpt-4o-mini list prices. The confidence flag is published as a weak filter: at 0.90 it catches 26% of all errors while flagging 11% of rows.
Client needs: a one-time export of every active listing for a list of suburbs from a bot-protected property portal into one CSV the client imports by hand — no automation, no scheduled re-runs, no integration.
Delivered: Fixture-verified in this build (synthetic pages, no live run): 2 search pages, 28 listing rows seen, 26 valid with 26 unique listing_url and 0 agency cards, 2 dropped with a reason, 54-test suite (50 pass + 4 skip without Node). Predecessor prototype, live manual browsing on 2026-08-30: 53 rows on the first pass, then 26 rows / 26 unique URLs / 0 agency cards / 0 dead links after fixes. Live re-verification on 2026-09-08: 37 rows, 33 unique URLs, 0 agency cards, 4 dead links. The Playwright route was not run end to end.
Client needs: a niche brief turned into a ranked, contactable list of 200+ podcasts from public sources — name, website, size, YouTube, email, phone, format and location — US and Canada first, then the EU block, then Australia and New Zealand, with a stated completeness for every column.
Delivered: Measured in this build, one run on 2026-09-07 (--limit 200, free gemini-3.1-flash-lite): 8,163 raw rows narrowed to 2,384 unique feeds, 200 exported; coverage of the 200 rows — website 200 / 200 (100%), email 160 / 200 (80%), phone 14 / 200 (7%), YouTube subscribers 66 / 200 (33%), format 6 / 200 (3%), location 23 / 200 (11.5%); 360 LLM requests, $0.0000 on the free tier. Predecessor prototype: 800 rows exported, coverage website 100%, email 74% (593), phone 6% (48), YouTube 4% (31), format 0%, location 0.25%.
- Every deliverable ships with a confidence or review column, so the client knows exactly which rows to check before trusting the batch.
- Every claim in every README is a measured number carried with its denominator — 32/37 fields, 355 of 355 addresses, 160 / 200 rows — not a rounded headline.
- Each repo runs offline from a fresh clone with one demo command: no API key, no network, synthetic fixtures, deterministic output.
- Where a feature did not help, the number that shows it is published rather than hidden. In pdf-data-extractor the LLM fallback was called on every synthetic document that rules could not read and hybrid accuracy still stayed at 61.8% (21/34), identical to rules-only — that flat result is in the README.
- A free sample on your own data before you commit to the job.
- Delivery in milestones, each one a working piece you can check.
- Async on Upwork, with a written daily update.
- Nothing is guessed: a value the source does not support is left empty and flagged for review, never filled with a plausible-looking number.
Python, Playwright, pandas, pdfplumber, openpyxl, BeautifulSoup, OpenAI-compatible LLM APIs, Chrome extensions (Manifest V3), GitHub Actions.




