Home / Work / Part Query System
CLIENT SYSTEM · PARTS & PROCUREMENT

The supplier had
no API. So we
built one.

A parts business quoting on heavy equipment jobs had to look up every part number, one at a time, in an OEM web catalogue — a hundred lookups per quotation, done by hand. The Part Query System turns that list into a single submitted job: parts are queried concurrently, failures retry with backoff, progress is live, and every part carries its own log entry so a missing result is explainable rather than mysterious.

Client
Equipment parts & procurement business
Problem
Manual portal lookup at quotation scale
Approach
Headless browser automation as an integration layer
Throughput
Parallel lookups, tunable per deployment
Resilience
Retries with exponential backoff
Surface
Role-based web dashboard · SQLite
1
Submission replaces a day of lookups
100s
Of parts per submitted job
100%
Per-part audit logging
/ 01
The Problem

Quoting on a parts order means confirming, for every line, whether the part exists, what it supersedes, and what it costs. The authoritative source is the manufacturer's own web catalogue — which is designed for a human looking up one part, and offers no bulk export and no API to the dealer network.

So a quotation with a hundred lines was a hundred manual searches: type, wait, read, copy, paste, repeat. It took most of a day, it was error-prone in exactly the way repetitive transcription always is, and it scaled linearly with the size of the order — meaning the biggest, most valuable enquiries were the slowest to answer.

Being slow to quote loses work. In parts supply the first credible quotation frequently wins, and a day of turnaround is often a day too many.

/ 02
The Approach

When a system has no API, the interface is the API. We built a controlled headless-browser layer that performs the same lookup a staff member would — but many at once, deterministically, and with a record of what it did.

A job is a list of part numbers. The runner drives multiple catalogue sessions in parallel with a tunable concurrency ceiling, blocks unnecessary resource loading to cut page weight, and uses a smaller viewport so pages render faster. Navigation, operation and protocol timeouts are all separately configurable, because a portal that is fine at 10am can be slow at 4pm and one global timeout serves neither case.

Failure is expected rather than exceptional. Each part retries with exponential backoff up to a configured limit, and a part that ultimately fails is recorded as failed with its log rather than silently omitted. A job tracks total, completed and errored counts separately, so an operator always knows whether a run is trustworthy.

Around the runner sits an ordinary business application: role-based access, users, job history, per-job structured logs, and settings — so operating it does not require a developer.

/ 03
The Hard Parts
  1. Concurrency against a system that was never designed for it Too few parallel sessions and the job takes hours; too many and the portal degrades or starts refusing. The ceiling is configurable per deployment precisely because the right number is an empirical property of the target, not a constant.
  2. Distinguishing "not found" from "failed to look up" These look identical in a naive scraper and mean completely different things in a quotation — one is information, the other is a gap. Result states are modelled explicitly and surfaced separately in the job summary.
  3. Surviving portal changes The extraction layer is isolated so that when the catalogue's markup changes, one module is updated rather than the automation being rewritten. This is the ongoing cost of integrating with a system that has no contract with you, and the architecture assumes it.
  4. Making a long-running job legible A job that takes twenty minutes needs live progress, not a spinner. Counts update as parts complete, and structured logs are attached per job so an operator can see exactly where a run went wrong without reading a server log.
/ 04
The Outcome

A quotation that consumed most of a day is submitted in one action and returns a completed result set while the estimator does something else. Turnaround on large enquiries — exactly the ones worth winning — stopped being the constraint.

Because every job is stored with its results and logs, the business also accumulated something it never had: a searchable history of what was looked up, when, and what came back. That is the beginning of a pricing and demand dataset that did not previously exist.

This is the pattern we apply whenever a critical supplier, government portal or legacy internal system offers no integration path. The absence of an API is a constraint, not a blocker.

Stack Next.js Bun TypeScript Puppeteer Cheerio SQLite Radix UI Tailwind CSS Concurrency control Exponential backoff Structured logging Docker

"There's no API" is a statement about
the vendor, not about what's possible.

WORK THAT SHOULDN'T BE MANUAL

If a skilled person spends a day
copying between two screens, that's a system.

Contact Us →