Milk at Lidl, salmon at Aldi, olive oil at Rewe — sounds like a plan. The receipt will probably even agree. It's the honest math that doesn't.
Supermarket flyers are excellent at selling savings. What they consistently omit: the travel costs, the parking hunt, the twenty minutes in the second store, the detour that quietly ate your discount. The receipt shows the savings. The full bill is nowhere — because nobody wants to write it down.
offerhopper.ai writes it down anyway. Not as a rule of thumb, but as a complete cost function computed for every route before you even grab your jacket.
The Question You're Actually Asking
When you wonder whether the detour to Aldi for that deal is worth it, you're solving — whether you like it or not — an optimization problem:
Is the product savings greater than the cost the detour creates?
For one product and one store, still manageable. For three stops, two possible transport modes, and a list with twelve items? Good luck doing that math in the dairy aisle. offerhopper.ai handles it — for the entire route at once, in under a second.
The Equation That Scores Every Route
For every possible combination of stores, products, and orderings, offerhopper.ai calculates a true total cost — including all the expenses that stay invisible on the receipt:
$$\text{Cost}_{\text{trip}} = \sum_{j \in S} \left[ \left( \sum_{i \in I_j} P_i \cdot \alpha_i \right) + \gamma_j \cdot \left( T_j(I_j) \cdot \text{Rate}_{\text{hour}} \right) \right] + \sum_{k \in \text{Legs}} \text{Cost}_{\text{route}}(k)$$
Looks worse than it is. Here's what's inside.
$\sum_{i \in I_j} P_i \cdot \alpha_i$ — What You Pay at the Register (But Not Quite That Simple)
The base price of each product $P_i$ is multiplied by a product scoring factor $\alpha_i$. This factor is the algorithm's quiet quality control — it measures how well a database hit actually matches what you searched for: match accuracy, unit deviation, data completeness.
This prevents the classic mistake of cheap comparison tools: recommending the cheapest product that completely misses what you asked for. A 200g salmon fillet when you searched for a 500g portion isn't cheaper. It's just not enough.
$\gamma_j \cdot T_j(I_j) \cdot \text{Rate}_{\text{hour}}$ — What the Store Itself Costs
Here lies the most dishonest gap in traditional price comparison: it treats all stores as equal. Walk in, pay, walk out, next line. The algorithm doesn't.
The stop scoring factor $\gamma_j$ is not a comfort bonus or a soft exception to the math. It's a real cost factor in the scoring space — the formalization of an experience everyone knows but nobody writes down: some stores simply cost more than what's on the price tag. Stores that are genuinely pleasant to visit — easy access, fast checkout, a layout that doesn't feel like a deliberate psychological experiment — get a $\gamma_j < 1$. Their visit is effectively cheaper in the scoring space. Stores that are the opposite get $\gamma_j > 1$. You know the one. This factor isn't user input — it's an editorial assessment working in the background.
The result is sometimes surprising: Product X is 10 cents cheaper at Store A than Store B. But Store A is unpleasant enough that its effective visit cost in the scoring space is 20% higher. The algorithm runs the numbers — and recommends the 10-cent-more-expensive product at Store B. Not as a mistake. Because visiting Store A under these conditions is genuinely more expensive. Mathematically correct in the scoring space, and at the same time exactly what feels right.
Time per stop $T_j$ consists of a fixed base time (walk in, pay, walk out — default: 15 minutes) plus the product switching time $t_{\text{switch}, i}$ per item on the list:
$$T_j(I_j) = t_{\text{store}, j} + \sum_{i \in I_j} t_{\text{switch}, i}$$
Both multiplied by $\gamma_j$ and your personal hourly rate $\text{Rate}_{\text{hour}}$. Default: €12/h. If you're freelancing or burning vacation hours, set this higher — and watch a second stop suddenly barely pencil out.
$\sum_{k \in \text{Legs}} \text{Cost}_{\text{route}}(k)$ — What the Trip Costs
For each leg between stores, a separate cost calculation runs depending on how you travel:
| Transport Mode | Cost per km | Average Speed |
|---|---|---|
| Car | ~€0.30–0.38/km (fuel + wear, per ADAC) | 40–50 km/h (urban) |
| Bicycle | €0.00/km | 12–15 km/h |
| Walking | €0.00/km | 4–5 km/h |
The bicycle is structurally favored in this equation: zero per-km costs, only time. Driving a car, a 3 km detour already costs roughly one euro in pure travel costs — before anything is in the basket. The model also supports custom values for high-consumption vehicles, EVs, or car-sharing with per-minute billing.
A Concrete Example (With an Uncomfortable Truth)
Shopping plan in Berlin. List: olive oil, yogurt, pasta, chicken breast.
Option A: Everything at the nearby Rewe. Basket price: €18.40. Trip: 1.2 km by bike. Total time: 20 minutes.
Option B: Olive oil at Aldi (on sale!), rest at Rewe. Basket price: €16.80. Trip: 3.6 km, two stops. Total time: 45 minutes.
Savings from the Aldi detour: €1.60. Additional time cost (25 extra minutes × €12/h): ~€5.00. Net balance for Option B: –€3.40. You overpaid by €3.40. The receipt had no idea.
This is exactly the calculation offerhopper.ai runs for every possible route combination before recommending one. The result isn't a ranking by cheapest unit price, but by lowest true total cost.
Why Your Transport Mode Matters More Than You Think
The bicycle beats the car on short distances almost every time — not because it's faster, but because it generates zero per-km costs. This creates a pattern that runs against intuition:
Car: Only pays off on long distances with a large basket. For a quick milk run, it's economically hard to justify — even if it feels more convenient. Bicycle: The optimum for distances up to ~5 km. Zero per-km costs, manageable ride time, and you got exercise. Walking: Free per kilometer, but 4 km/h is a hard physical limit. Only worthwhile when the store is genuinely around the corner and you're not hauling a crate of water.
What This Means for Deal-Hunting
The most practical question this equation answers: is the detour for the deal worth it?
How large is the savings in absolute terms? 20% off yogurt sounds like a lot. At 89 cents base price, that's 18 cents. That doesn't even cover the time cost of a two-minute walk — yet millions of people make exactly this detour every day.
How many units are you buying? Savings scale linearly. Ten yogurts instead of one, and the detour suddenly looks different on paper. As long as you actually need ten and aren't tossing a moldy cup three weeks later.
What does the rest of the shop cost there? Switching stores for one deal usually means buying more there. If the general price level at the alternate store is higher, it eats the savings from behind — silently, receipt-invisibly, as always.
The Parameters That Tune the Model to You
The system runs with sensible defaults. If you want to dig deeper, offerhopper.ai exposes three parameters you can adjust:
Your personal time value per hour (default: €12/h) is the most important lever. Raising it makes routes shorter and more direct. Lowering it makes additional stops more attractive when real savings exist. A freelancer billing €80/h gets consistently sent to the nearest store. Retiree with time and a bike? Different math, different route.
Your travel cost per kilometer is automatically 0 for cycling and walking. For cars, it defaults to the ADAC benchmark (€0.30/km). If you drive an old diesel at 12L/100km or count car-sharing minutes, push it up and watch how quickly the car becomes the expensive option.
Your time per store (default: 15 min): If you know your regular store blindfolded and can be through in eight minutes, lower it. If you need to search in an unfamiliar store, raise it. The effect: the higher the value, the more expensive each additional stop becomes in the model.
What the Algorithm Actually Solves
The cost function is the objective. The actual problem behind it is a different category.
Eight supermarkets nearby, twelve items on the list, not every store carries every item, prices vary, every stop costs time and travel: which combination is optimal, in what order? The number of possible answers explodes quickly — eight stores yield 256 possible subsets, each with multiple orderings.
But that's only half the equation. For each item on the list, there isn't one product but potentially dozens: different brands, package sizes, deals, store brands. The solver evaluates not just store combinations, but for each combination also the optimal product selection across all items and stops. Hundreds of products, eight stores, twelve requirements — the search space stops being large and starts being a problem. Classically NP-hard if you open the textbook, and in practice exactly what every person gives up on at the shelf, replacing it by grabbing whatever's in front of them.
offerhopper.ai solves this exactly — not with an approximation, not with the first heuristic shortcut. Dynamic programming ensures intermediate results are reused rather than computed twice, and the global optimum is guaranteed. Six stores, eight items: under one second.
The result: not the cheapest individual hit per item, but the cheapest route overall — including all costs that nobody else writes down.
A grocery trip that looks good on the receipt but caused an 8 km detour isn't a savings win. It's an arithmetic error you only notice when you think about it — and most of the time, you don't think about it afterwards.
(If you want to use this optimization programmatically — via Claude, ChatGPT, or a Python agent — all the details are in the MCP Developer Guide. General questions are answered in the FAQ.)