GET /api/work/internal-erp/repricing
Repricing every minute without a race to the bottom
Live · · sole engineer
Prices used to be a spreadsheet someone updated every morning. Now a robot checks every product once a minute, and knows better than to start a price war.Replaced a morning pricing spreadsheet with an engine that reprices every minute, with cooldowns, floors, and a rule to never chase the lowest price.
- TypeScript
- Node.js
- PostgreSQL
- Python
- Flask
- Web scraping
- Net32 API
- price pushes logged
- 138K+
- sweep interval
- 60s
- cooldown per product
- 12h
- pricing engines
- 1

Parameters
Request
curl "https://portfolio.dylansg0318.workers.dev/api/ work/ internal-erp/ repricing"
Response200 OK · 3.3 KB
{ "id": "internal-erp/repricing", "title": "Repricing every minute without a race to the bottom", "status": "live", "year": 2026, "category": "work", "role": "sole engineer", "summary": "Replaced a morning pricing spreadsheet with an engine that reprices every minute, with cooldowns, floors, and a rule to never chase the lowest price.", "summary_plain": "Prices used to be a spreadsheet someone updated every morning. Now a robot checks every product once a minute, and knows better than to start a price war.", "stack": [ "TypeScript", "Node.js", "PostgreSQL", "Python", "Flask", "Web scraping", "Net32 API" ], "metrics": { "price_pushes_logged": "138K+", "sweep_interval": "60s", "cooldown_per_product": "12h", "pricing_engines": "1" }, "parent": { "id": "internal-erp", "title": "The internal ERP that runs a dental supply company", "page": "https://portfolio.dylansg0318.workers.dev/projects/internal-erp" }, "problem": "Prices on Net32 were set from a spreadsheet someone updated every morning by looking at competitors. By noon it was stale. By the next morning we'd either lost the buy box or given away margin overnight. The first automation, a Python scraper, helped, but it created a second problem: two pricing paths that could disagree, so the \"optimal price\" the dashboard showed wasn't always the price the job pushed.", "unique": "Two decisions carry the whole thing. First, never chase the lowest price: the tiered strategy targets the buy-box winner in the chosen mode, holds when undercutting would breach the floor, and pushes only when a competitor has actually moved and the 12-hour cooldown has cleared. Second, one engine everywhere, the manual button, the bulk push, the auto mode, the post-scrape recompute and the \"next run\" preview all call the same function, so the preview can never lie about what the job will do.", "ai": "The first version was hand-written Python and XPath. The rewrite into the ERP was agent-assisted, but the strategy, what counts as a competitor, when to hold, why one engine, was worked out on paper with the owner first. The agent's most useful contribution was the diagnostic that proves the preview and the push agree.", "learned": [ "Two engines that \"should\" agree will diverge, and you find out from a customer. The fix isn't syncing them. It's deleting one.", "A cooldown is a feature, not a limitation. Repricing on every scrape turns two sellers into a price war with a one-minute tick. A per-product anchor with a 12-hour hold made prices stable enough for the buyer to trust.", "Log every attempt, including the ones that decided not to push. When margin drops, the question is always \"why didn39;t it move?\", and \"it ran and chose to hold\" is an answer only if you wrote it down." ], "cover": { "url": "https://portfolio.dylansg0318.workers.dev/_astro/repricer-dashboard.BbPNiDYu.png", "width": 1920, "height": 1114, "alt": "The repricer dashboard: buy-box winning, sibling-hold, losing and unwinnable counts, push success rates, a breakdown of why the engine isn't undercutting, per-storefront buy-box share, and the safety rails including a 15% margin floor. A second storefront's name is blurred." }, "page": "https://portfolio.dylansg0318.workers.dev/projects/internal-erp/repricing" }
Live: this is the answer from this site’s API, not a mock. Send re-runs the request with your parameters; if the answer names a page, it opens.
The problem
Prices on Net32 were set from a spreadsheet someone updated every morning by looking at competitors. By noon it was stale. By the next morning we'd either lost the buy box or given away margin overnight. The first automation, a Python scraper, helped, but it created a second problem: two pricing paths that could disagree, so the "optimal price" the dashboard showed wasn't always the price the job pushed.
What was unique
Two decisions carry the whole thing. First, never chase the lowest price: the tiered strategy targets the buy-box winner in the chosen mode, holds when undercutting would breach the floor, and pushes only when a competitor has actually moved and the 12-hour cooldown has cleared. Second, one engine everywhere, the manual button, the bulk push, the auto mode, the post-scrape recompute and the "next run" preview all call the same function, so the preview can never lie about what the job will do.
Where AI fit in
The first version was hand-written Python and XPath. The rewrite into the ERP was agent-assisted, but the strategy, what counts as a competitor, when to hold, why one engine, was worked out on paper with the owner first. The agent's most useful contribution was the diagnostic that proves the preview and the push agree.
Parameters
Request
curl "https://portfolio.dylansg0318.workers.dev/api/ work/ internal-erp/ repricing? fields=problem%2Cunique%2Cai"
Response200 OK · 1.3 KB
{ "problem": "Prices on Net32 were set from a spreadsheet someone updated every morning by looking at competitors. By noon it was stale. By the next morning we'd either lost the buy box or given away margin overnight. The first automation, a Python scraper, helped, but it created a second problem: two pricing paths that could disagree, so the \"optimal price\" the dashboard showed wasn't always the price the job pushed.", "unique": "Two decisions carry the whole thing. First, never chase the lowest price: the tiered strategy targets the buy-box winner in the chosen mode, holds when undercutting would breach the floor, and pushes only when a competitor has actually moved and the 12-hour cooldown has cleared. Second, one engine everywhere, the manual button, the bulk push, the auto mode, the post-scrape recompute and the \"next run\" preview all call the same function, so the preview can never lie about what the job will do.", "ai": "The first version was hand-written Python and XPath. The rewrite into the ERP was agent-assisted, but the strategy, what counts as a competitor, when to hold, why one engine, was worked out on paper with the owner first. The agent's most useful contribution was the diagnostic that proves the preview and the push agree.", "page": "https://portfolio.dylansg0318.workers.dev/projects/internal-erp/repricing" }
Live: this is the answer from this site’s API, not a mock. Send re-runs the request with your parameters; if the answer names a page, it opens.
The story
On a marketplace like Net32 the seller with the right price wins the sale, and competitors change theirs all day. Ours were set from a spreadsheet someone updated every morning by eyeballing the competition. By lunch it was stale. By the next morning we’d either lost sales overnight or quietly given margin away.
So I built the robot version of that morning routine. Every minute it looks at the products due for a check, sees what competitors are charging right now, and decides: move or hold. It never drops below the floor price a person set, it waits twelve hours before touching the same product twice, and it never plays match-the-lowest, because two robots doing that race each other to zero profit in an afternoon.
It’s made over 138,000 price changes so far, every one written down with the reason, so “why is this listed at $41.20?” takes one look, not an argument.
Where it started
The first thing I built at RMH3 was a Python and Flask tool that scraped competitor prices with XPath and pushed updates through the marketplace API. It replaced the spreadsheet and it worked, but it lived beside the business instead of inside it, and as the ERP grew it became the second place prices got decided.
The schedule
Every product/vendor pair gets a row in a schedule table: an anchor time, a 12-hour cooldown, and the last price we pushed. A sweep runs every minute and only touches the rows that are due. For each one it scrapes the live market, runs the strategy, and pushes only if the answer changed and the cooldown has cleared. Every attempt, push or hold, lands in a log with the reason and the competitor prices the engine saw. That log is the most-read table in the system.
The strategy
“Match the lowest” is what everyone asks for and nobody actually wants; two sellers running it converge on zero margin in an afternoon. The tiered strategy instead:
- Finds the buy-box winner in the mode we care about (authorised sellers by default).
- Undercuts them by a configured step, but only down to the product’s floor.
- Holds if the floor would be breached, if the winner is us, or if nothing has moved since the last push.
Floors and ceilings are per product and set by a person. The engine never invents a floor.
One engine
The rule that prevents the most bugs is boring: exactly one function computes a price, and every path calls it. The manual Execute button, the bulk push, the ⚡ Auto toggle, the recompute after a scrape, and the “next run” preview on the listing page all go through the same code. If the preview says $41.20, the job pushes $41.20. Before this the dashboard showed an “optimal price” computed one way and the job pushed a price computed another. They agreed most of the time, which is the worst amount: often enough to be trusted, rarely enough to be caught.
Health, not just price
The same engine feeds a buy-box analysis every five minutes, so the intelligence feed can say which listings lost the box, which competitors are new, and where our own floor is what’s holding us back. Overrides, contract pricing and promos stay manual on purpose. Those are judgment calls; the engine’s job is to make them visible, not make them.
Every push, with its reason

Nothing moves a price without writing down why. That’s what turns a wrong price into a five-minute question instead of an afternoon.
What I learned
- Two engines that "should" agree will diverge, and you find out from a customer. The fix isn't syncing them. It's deleting one.
- A cooldown is a feature, not a limitation. Repricing on every scrape turns two sellers into a price war with a one-minute tick. A per-product anchor with a 12-hour hold made prices stable enough for the buyer to trust.
- Log every attempt, including the ones that decided not to push. When margin drops, the question is always "why didn't it move?", and "it ran and chose to hold" is an answer only if you wrote it down.
Parameters
Request
curl "https://portfolio.dylansg0318.workers.dev/api/ work/ internal-erp/ repricing? fields=learned"
Response200 OK · 681 B
{ "learned": [ "Two engines that \"should\" agree will diverge, and you find out from a customer. The fix isn't syncing them. It's deleting one.", "A cooldown is a feature, not a limitation. Repricing on every scrape turns two sellers into a price war with a one-minute tick. A per-product anchor with a 12-hour hold made prices stable enough for the buyer to trust.", "Log every attempt, including the ones that decided not to push. When margin drops, the question is always \"why didn39;t it move?\", and \"it ran and chose to hold\" is an answer only if you wrote it down." ], "page": "https://portfolio.dylansg0318.workers.dev/projects/internal-erp/repricing" }
Live: this is the answer from this site’s API, not a mock. Send re-runs the request with your parameters; if the answer names a page, it opens.