Valider un produit une seule fois ne suffit pas. Les marges Amazon FBA évoluent à mesure que les concurrents modifient leurs prix, qu'Amazon ajuste ses frais et que la demande fluctue selon les saisons. Un test de résistance de rentabilité soumet votre produit à plusieurs scénarios utilisant des données de marché en direct : que se passe-t-il si le prix moyen chute de 20 % ? Et si trois nouveaux concurrents entrent en jeu ? Ce tutoriel construit un test de résistance automatisé qui récupère des données en direct d'Amazon et de Google via l'API Scavio à 0,005 $ par recherche.
Prérequis
- Python 3.9+ installé
- bibliothèque requests installée
- Une clé API Scavio depuis scavio.dev
- Une idée de produit avec un coût unitaire et un coût d'expédition connus
Parcours
Étape 1: Extraire les données de prix des concurrents en direct
Recherchez sur Amazon votre catégorie de produit et extrayez les prix actuels. C'est la base de référence pour le test de résistance.
import os, requests, re, time
SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
H = {'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'}
def get_market_pricing(product: str) -> dict:
resp = requests.post('https://api.scavio.dev/api/v1/search', headers=H,
json={'query': f'site:amazon.com {product}', 'country_code': 'us', 'num_results': 10})
results = resp.json().get('organic_results', [])
prices = []
for r in results:
matches = re.findall(r'\$([\d,]+\.\d{2})', r.get('snippet', '') + ' ' + r.get('title', ''))
prices.extend(float(m.replace(',', '')) for m in matches)
if not prices:
return {'avg': 0, 'min': 0, 'max': 0, 'count': 0, 'prices': []}
return {
'avg': sum(prices) / len(prices),
'min': min(prices),
'max': max(prices),
'count': len(prices),
'prices': sorted(prices),
}
pricing = get_market_pricing('yoga mat thick non-slip')
print(f'Market pricing: ${pricing["min"]:.2f} - ${pricing["max"]:.2f}')
print(f'Average: ${pricing["avg"]:.2f} from {pricing["count"]} data points')Étape 2: Définir les scénarios de test de résistance
Créez plusieurs scénarios de résistance qui simulent des changements réalistes du marché. Chaque scénario modifie les prix, les frais ou les niveaux de concurrence.
from dataclasses import dataclass
@dataclass
class Scenario:
name: str
price_modifier: float # Multiply avg price by this
fee_modifier: float # Multiply Amazon fees by this
extra_cost: float # Additional per-unit cost
SCENARIOS = [
Scenario('Base case', 1.0, 1.0, 0),
Scenario('Price war (-20%)', 0.80, 1.0, 0),
Scenario('Price war (-30%)', 0.70, 1.0, 0),
Scenario('Fee increase (+10%)', 1.0, 1.10, 0),
Scenario('Shipping cost spike', 1.0, 1.0, 2.00),
Scenario('Premium positioning (+15%)', 1.15, 1.0, 1.50),
Scenario('Worst case: price war + fees', 0.75, 1.15, 1.00),
]
print(f'Defined {len(SCENARIOS)} stress scenarios:')
for s in SCENARIOS:
print(f' {s.name}: price x{s.price_modifier}, fees x{s.fee_modifier}, +${s.extra_cost} cost')Étape 3: Exécuter le test de résistance avec les données en direct
Exécutez chaque scénario avec les données de marché en direct pour calculer les marges attendues. Le test de résistance montre exactement où la rentabilité se brise.
def stress_test(product: str, unit_cost: float, shipping: float = 3.0) -> list:
"""Run all stress scenarios against live market data."""
pricing = get_market_pricing(product)
if pricing['avg'] == 0:
return [{'scenario': 'ERROR', 'reason': 'No pricing data'}]
results = []
for scenario in SCENARIOS:
sell_price = pricing['avg'] * scenario.price_modifier
referral_fee = sell_price * 0.15 * scenario.fee_modifier
fba_fee = 4.50 * scenario.fee_modifier
total_cost = unit_cost + shipping + referral_fee + fba_fee + scenario.extra_cost
profit = sell_price - total_cost
margin = profit / sell_price if sell_price > 0 else 0
results.append({
'scenario': scenario.name,
'sell_price': sell_price,
'total_cost': total_cost,
'profit': profit,
'margin': margin,
'profitable': profit > 0,
'margin_ok': margin >= 0.20,
})
return results
results = stress_test('yoga mat thick non-slip', unit_cost=6.00)
print(f'Stress Test: yoga mat thick non-slip')
print(f'Unit cost: $6.00, Shipping: $3.00')
print('-' * 65)
for r in results:
status = 'OK' if r['margin_ok'] else 'LOW' if r['profitable'] else 'LOSS'
print(f"[{status:4s}] {r['scenario']:30s} "
f"Price: ${r['sell_price']:6.2f} "
f"Profit: ${r['profit']:6.2f} "
f"Margin: {r['margin']:5.0%}")Étape 4: Générer la recommandation go/no-go
Analysez les résultats du test de résistance pour faire une recommandation basée sur les données. Un produit est validé s'il reste rentable dans la plupart des scénarios.
def recommendation(product: str, unit_cost: float, shipping: float = 3.0) -> dict:
results = stress_test(product, unit_cost, shipping)
profitable_count = sum(1 for r in results if r['profitable'])
good_margin_count = sum(1 for r in results if r['margin_ok'])
total = len(results)
# Calculate break-even price
base = next(r for r in results if r['scenario'] == 'Base case')
# Recommendation logic
if good_margin_count >= total - 1:
verdict = 'STRONG GO'
reason = f'Profitable with good margins in {good_margin_count}/{total} scenarios'
elif profitable_count >= total - 1:
verdict = 'GO'
reason = f'Profitable in {profitable_count}/{total} scenarios but margins tight'
elif profitable_count >= total // 2:
verdict = 'CAUTION'
reason = f'Only profitable in {profitable_count}/{total} scenarios'
else:
verdict = 'NO GO'
reason = f'Unprofitable in {total - profitable_count}/{total} scenarios'
print(f'\nRECOMMENDATION: {verdict}')
print(f'Reason: {reason}')
print(f'Base margin: {base["margin"]:.0%} (${base["profit"]:.2f}/unit)')
print(f'Worst margin: {min(r["margin"] for r in results):.0%}')
print(f'API cost for this analysis: $0.005')
return {'verdict': verdict, 'reason': reason, 'results': results}
rec = recommendation('yoga mat thick non-slip', unit_cost=6.00)Exemple Python
import os, requests, re
SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
H = {'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'}
def stress_test(product, unit_cost, shipping=3.0):
resp = requests.post('https://api.scavio.dev/api/v1/search', headers=H,
json={'query': f'site:amazon.com {product}', 'country_code': 'us', 'num_results': 10})
prices = [float(m.replace(',','')) for r in resp.json().get('organic_results', [])
for m in re.findall(r'\$([\d,]+\.\d{2})', r.get('snippet',''))]
avg = sum(prices)/len(prices) if prices else 0
for label, mult in [('Base', 1.0), ('-20%', 0.8), ('-30%', 0.7)]:
price = avg * mult
cost = unit_cost + shipping + price*0.15 + 4.50
profit = price - cost
margin = profit/price if price else 0
print(f'{label:6s} ${price:6.2f} -> ${profit:5.2f} ({margin:.0%})')
stress_test('yoga mat thick', 6.00)Exemple JavaScript
const SCAVIO_KEY = process.env.SCAVIO_API_KEY;
async function stressTest(product, unitCost, shipping = 3) {
const resp = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST',
headers: { 'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ query: `site:amazon.com ${product}`, country_code: 'us', num_results: 10 })
});
const results = (await resp.json()).organic_results || [];
const prices = results.flatMap(r => ((r.snippet||'').match(/\$([\d,]+\.\d{2})/g) || []).map(p => parseFloat(p.slice(1).replace(',',''))));
const avg = prices.length ? prices.reduce((a,b) => a+b,0) / prices.length : 0;
for (const [label, mult] of [['Base', 1.0], ['-20%', 0.8], ['-30%', 0.7]]) {
const price = avg * mult;
const cost = unitCost + shipping + price*0.15 + 4.50;
const profit = price - cost;
console.log(`${label.padEnd(6)} $${price.toFixed(2)} -> $${profit.toFixed(2)} (${((profit/price)*100).toFixed(0)}%)`);
}
}
stressTest('yoga mat thick', 6.00);Sortie attendue
Stress Test: yoga mat thick non-slip
Unit cost: $6.00, Shipping: $3.00
-----------------------------------------------------------------
[OK ] Base case Price: $ 24.99 Profit: $ 7.74 Margin: 31%
[OK ] Price war (-20%) Price: $ 19.99 Profit: $ 3.49 Margin: 17%
[LOW ] Price war (-30%) Price: $ 17.49 Profit: $ 1.37 Margin: 8%
[OK ] Fee increase (+10%) Price: $ 24.99 Profit: $ 6.85 Margin: 27%
[OK ] Shipping cost spike Price: $ 24.99 Profit: $ 5.74 Margin: 23%
[OK ] Premium positioning (+15%) Price: $ 28.74 Profit: $ 10.43 Margin: 36%
[LOW ] Worst case: price war + fees Price: $ 18.74 Profit: $ 0.76 Margin: 4%
RECOMMENDATION: GO
Reason: Profitable in 7/7 scenarios but margins tight