Price monitoring
Register products and the pages to read, then receive every morning who is selling them and for how much — and what happens to a source that did not answer.
The job on the front of this site: not a market rate — which company, at what price. This is how to receive it every morning, with its provenance.
Early access. Public sign-up is not open yet. Get in touch if you want in.
Three words
| Product | Your reference or SKU. Add your own tax-exclusive price and the gap is computed for you |
| Source | One page to read for it, and which country to read from |
| Sweep | One pass over every due source. Daily by default |
A source is product × page × country because the same URL served to Berlin and to Tokyo does not return the same price.
Saying what to read
Per page, choose which of price, seller, title, availability and currency to take. CSS selectors are optional — left empty, the free structured-data tier looks for the value in the machine-readable markup most shops already publish. Reach for a selector when that fails, not before.
A source with nothing chosen is read every morning and returns nothing — which is indistinguishable from a page with nothing on it. The console warns about this.
The morning sweep
POST /v1/monitor/sweep
{ "limit": 25 }Takes the sources that are due and records what came back. Sources are claimed before they are read, so your own cron and ours cannot sweep the same one twice.
A double sweep is not a duplicate-row problem. The second pass does not find what the first one recorded and counts an absence against every listing it saw. Two sweeps in one morning is how a listing reaches the disappearance threshold in a day.
Reading goes through POST /v1/extract. A sweep is billed, rate limited and policy-checked exactly as the equivalent hand-written calls would be. There is no separate price and no separate path.
What comes back
{
"batch_id": "mrun_...",
"sources": 3, "answered": 2,
"seen": 7, "added": 1, "repriced": 2, "gone": 0, "pending": 0,
"excluded": [{ "sourceId": "msrc_...", "status": "refused", "error": "rate limited" }]
}answered is how many actually returned a page, and only those take part in deciding what disappeared — because a source that failed and a source whose stock cleared both return nothing.
What happens to a source that did not answer
This is the product.
- Its listings are left alone. Nothing is counted as sold or withdrawn
- One absence is not a sale. Listings drop off a page because the sort changed or the paging moved, every day
- A 403 or a 429 is recorded as a refusal, never as an empty success
An empty result reads as "there is nothing there" and sends you to check your selectors — when the actual message was "slow down".
How it reaches you
The screen — this morning's table. Per row: seller, country, price ex tax, change since the last reading, gap against yours, retrieval time, the FX rate *and its own publication date*, and the listing URL. Rows not read today are marked and say last known — not read today.
CSV — the same rows, plus a row for every source that could not be read (price blank, read_status = not_read). Dropping them silently is how a shorter list than last week reads as a market that shrank.
Email — after a sweep, only on a morning where something moved or something could not be read. Once, to everyone in the workspace.
The sources that failed come before the prices, so that somebody who scans the numbers and stops has already passed the sentence that changes what those numbers mean.
1 source did not answer this morning. Their listings are unchanged. Nothing from them is counted as sold or withdrawn.
>
REFUSED https://... (DE) — rate limited
>
1 price moved:
>
SKU-4471 Retailer B — New York (US) ¥14,600 → ¥13,900 −¥700 https://... read 2026-08-22T02:20:04.000Z · FX 1.0000 (2026-08-20)
A morning where everything was read and nothing moved is not sent. A message that is empty four days in five is a message people filter — and then miss on the day it says something. A failure is still sent, even with no price movement.
Webhook — monitor.sweep_completed, once per sweep, carrying not_answered. A re-run of the same batch does not fire it again.
What the numbers mean
- Comparisons read only the tax-exclusive, JPY-converted column. Subtracting a tax-inclusive price from a tax-exclusive one reverses the sign
- Tax treatment is declared per source. There is no default — a default here is the route by which the same mistake happens again
- The FX record carries the rate applied and the rate's own publication date. Over a weekend those are different days
Last updated