How it works
How we check each morning where your product is sold and by which sellers, why you cannot do it yourself, and what arrives. The mechanics come after.
"Read data off the web" sounds simple. The hard part is not the reading. It is that the record you get back can be trusted.
In one sentence
Every morning, we tell you where in the world your product is being sold, by which sellers and at what price, with the record attached. We match each seller against your authorised-seller list and keep the ones not on it for review. Whether a listing is counterfeit or unauthorised is for you to decide — the machine finds and records, and stops there.
Not "lots of shops sell it." Rather: Shop A on a marketplace (on your list), Shop Z (not on your list), a listing shared by several sellers (the shop cannot be identified from the page) — here are the pages, read at 06:00 this morning, at these prices.
We read the listing pages you register (over connections in five countries). On the major Japanese marketplaces, we also search every day for candidate listings you have not registered, and lay them out on screen.
The matches appear on the Authorised sellers screen; each product's countries, sellers, matches and contracted-market status appear on the Where your products stand screen.
Why "around" is not enough
Look into how your product is being sold and what usually comes back is an impression: "it's everywhere", "someone is selling it cheap".
An impression is not wrong. It just does not tell you whom to contact.
- To contact a seller not on your list, you need which shop, and which page
- To find where the discounting starts, you need which seller, since when, and at what price
- To understand distribution abroad, you need who sells it as seen from that country
"It's everywhere" answers none of the three.
Why you cannot just look it up yourself
You can. Once.
Two things get in the way.
1. 100 products × 5 sites, every morning, is not a job for a person
Five hundred pages opened, sellers and prices copied, compared against yesterday. It does not finish in a day. And it is worthless unless done daily — which shops were listing it yesterday is gone forever if nobody wrote it down.
2. From your own country, you can only see your own country's shelves
This is the part that is hardest to convey, so here it is slowly.
The same URL returns different content depending on where you opened it from. Different sellers. Different prices, currency and stock status. Different ranking, shipping, and promotions.
This is not a trick. It is completely ordinary — showing Japanese visitors the Japanese storefront is simply how a shop is built.
Which means: from a computer in Tokyo, who sells your product to an American customer is permanently invisible to you.
We open the page through a server in the country you specify, and can read the same page from several countries at once and set the results side by side.
"Couldn't I just ask a generative AI?"
We get asked this a lot. We tried it, so here is what happened.
| Asked a conversational AI | "The two markets are at broadly similar levels" |
| Measured, same day, same item | The US market was 17% higher |
The interesting part comes next. Both numbers the AI used were real and correct. One was a buy-back price; the other a market index.
It had placed two fundamentally different quantities side by side and subtracted them, and nowhere in the answer was that distinction stated. That difference alone flips the sign of the gap.
This is not a knock on the model. None of the five gaps below close with better prompting.
- You get a market level, not a seller — a correct answer, but not one you can act on
- It sees only from where it stands — there is no prompt for "as seen from São Paulo"
- It does not say where the number came from — so you cannot catch the error
- It has no yesterday — nobody stored it, so it cannot be bought later either
- Ask twice, get two answers — last week's deck and this week's cannot be compared
This is a description of what an AI is for, not a criticism of it. Reading a specific page, from a specific country, at a specific time, and keeping the record — that is a different job.
What arrives each morning
Every row carries all of this.
| Seller | The merchant as shown on the page |
| Price (ex-tax) | $1,180 |
| Change | −$42 |
| vs. your price | +8.6% |
| Read from | Which country's server we went through |
| Page URL | The page actually read |
| Retrieved at | 2026-08-22T02:20:04.756Z |
| FX rate | The rate applied, and the date that rate was published |
These fields do not come apart. Not an add-on, not a higher tier — always all of them.
One reason: a record with no origin cannot be defended in a meeting. Report "a shop not on our list is selling it at $900", get asked "which page?", "as of when?", "tax in or out?", have no answer — and nobody can act on that report.
Three of the ways to receive it carry every field on every row: the console (this morning's table), CSV, and a morning email. The webhook is a "sweep finished" signal that carries counts plus the batch id and time (monitor.sweep_completed: batch_id, sources, answered, not answered, refused by the site, not read on our side, listings seen, added, repriced, gone, pending, and the capture time). It carries no seller, price, URL or exchange rate; on receiving it, read the rows from the console, CSV or email.
The dullest part, and the one that matters most
Say you read, every day, a marketplace where sellers not on your list appear. One morning it is redesigned and the read fails. What comes back is zero rows.
Written naively, that gets reported as: "every listing not on the list disappeared overnight."
In the data, "we failed to fetch" and "everything disappeared" are the same shape. And it is exactly the kind of record that makes people decide the matter is closed.
What we do instead:
- We check first whether that source answered today at all. Listings from a source that did not answer are excluded from any disappearance judgement
- Missing once is not "disappeared". We wait until it is missing twice in a row. Listings drop off a page because the ordering changed — that is a daily occurrence
- A refusal is recorded as a refusal. "Too many requests" never becomes an empty success
And any source we could not read is named — on screen, in the CSV, in the email. Drop it silently and a shorter list reads as "fewer shops not on the list".
From here on, it is the mechanics
If the above is enough, go to what people use it for or pricing.
1. Where you look from
A page changes shape depending on who is asking.
The same product page can show yen and in-stock to a visitor in Japan, and dollars and sold-out to one in the United States. Search rankings differ. So does the consent banner.
So the request has to leave from an address in that country. You name the country per request, and that is where it is read from.
2. How we read — cheapest tier first
| Tier | What it does | Cost |
|---|---|---|
css | Reads the selectors you supply | none |
structured_data | Reads the JSON-LD and OpenGraph the site already publishes | none |
llm | Describes the page to a model | tokens |
A later tier fills only what earlier ones left empty, and if you do not ask for llm, no model is called. Most commerce pages are satisfied by the first two.
That is a cost argument and also a reproducibility argument: selectors and structured data return the same answer for the same page.
Lists come back as rows
We do not return one array per column. One card missing a price shortens that array alone, every later pair is off by one, and it does not raise an error — you get a table of real names against real prices belonging to different products. Nothing downstream can detect it.
With rows, a missing field is a null inside its own record and the rows either side stay aligned.
3. What comes back with it
Data alone cannot be defended in a meeting. Every row carries:
seller | the seller as published |
url | the page it was read from |
retrieved_at | when it was read |
fx_rate / fx_published_on | the rate used, and the date that rate was published |
price_ex_tax_jpy | price normalised to tax-exclusive |
level / attempts | which tier produced it, and every tier tried |
Tax normalisation happens once, at collection, and comparisons read only that column. A tax-inclusive Japanese price and a tax-exclusive Hong Kong price are compared on the same basis.
4. Four habits that keep a comparison like for like
Every figure is put together through four habits.
- Taxes aligned before comparing — tax-inclusive and tax-exclusive prices are settled at collection, and comparisons read the tax-exclusive column
- Accessories kept apart from the product — an accessory is counted on its own and never enters the price gap of the product it fits
- Model numbers read to the last suffix — a single trailing character makes a different product
- Absence confirmed twice — a listing is recorded as gone only after two reads in a row without it
The habits stay the same whatever the product category.
5. Where we read from — connections in 5 countries
The same product shows a different price, stock and currency depending on which country the request comes from. Shops fork on the visitor's country.
Kansoku reads from a real connection in each of 5 countries — not a VPN, not a translation.
| Country | Status |
|---|---|
| Japan | Reads |
| United States | Reads |
| South Korea | Reads |
| Taiwan | Reads |
| Singapore | Reads |
A free check before you sign up
You can confirm first that the pages you want can be read. Give us the URLs you care about and we return whether each one can be read, refused, or readable from another country. Decide after you know.
If you want to watch other countries too, tell us at sales@kansoku.ai. We add countries to match what customers ask for.
6. Read every day, told when something moves
| Frequency | Once a day (varies by plan) |
| Delivery | Next-morning email. The webhook sends your systems only a "sweep finished" signal with counts plus the batch id and time |
| Contents | What moved, and by how much |
| Sources that did not answer | We separate "they refused us" from "our side failed" |
| Every row carries | Shop, listing URL, capture time, FX rate used (dated by the rate itself) |
| The page as served | We keep the page we read, so it can be checked later |
You can also set conditions — below this price, moved by more than N%, cheaper than our own price, or the gap between two countries exceeds N%. The last one is only possible because we choose the country we read from.
How we fetch
- We read under our own name, without disguising the client
- There is no code for getting past a CAPTCHA (what we read is the public page anyone can open)
- We read only what a visitor can see without an account
- We keep within the rate a site has stated
Keeping personal data out of what we collect from the pages we read is an operating policy we hold to (see the collection policy).
There is a commercial reason as well as a principled one: a source that refuses us once is a source nobody gets back, and the loss falls on every customer who needed it.
What the same mechanism can be pointed at is under what it can do; how it differs from the alternatives is under choosing a tool.
Last updated 2026-10-09
Last updated