Pricing Finder
Indiquez l'URL de votre produit ou sa description. L'outil trouve 5 concurrents, récupère leurs pages de tarification réelles et retourne un rapport complet d'intelligence tarifaire : le modèle dominant dans votre secteur, un tableau de référence des prix, une analyse des fonctionnalités différenciatrices, une carte de positionnement concurrentiel, et une recommandation de prix concrète pour votre produit.
Aucune clé API requise. Fonctionne entièrement avec des dépendances pip gratuites. Des clés API optionnelles améliorent la qualité.
Politique zéro hallucination : Chaque prix, nom de palier et fonctionnalité différenciatrice dans le résultat doit provenir du contenu téléchargé de la page de tarification ou d'un extrait de recherche DuckDuckGo. Cela s'applique à :
- Prix des concurrents : extraits textuellement du contenu de la page téléchargée uniquement
- « Contactez le service commercial » : enregistré tel quel, jamais estimé ou remplacé par un nombre
- Noms des paliers : copiés exactement depuis la page, sans paraphrase
- Listes de fonctionnalités : extraites du contenu de la page, non déduites de la connaissance du produit
- Observations de positionnement : dérivées uniquement des données du tableau de référence
Erreurs courantes
| L'agent voudra... | Pourquoi c'est faux |
|---|---|
| Remplacer "Contactez le service commercial" par un prix estimé | N'estimez jamais les prix des entreprises. Enregistrez-le comme "Contactez le service commercial" exactement. |
| Utiliser sa connaissance pour les prix des concurrents | Chaque prix doit provenir du contenu téléchargé ou d'un extrait de recherche. |
| Sauter l'étape de confirmation des concurrents | Affichez toujours les concurrents découverts et attendez la confirmation. Des concurrents erronés = des références erronées. |
| Recommander un prix sans référence aux données du tableau | Chaque recommandation de prix doit citer un nombre spécifique du tableau de référence. |
| Marquer une page comme de haute qualité alors que le contenu < 500 caractères | < 500 caractères signifie que la page n'a pas été téléchargée — marquez data_quality comme 'low' et utilisez l'extrait de recherche en solution de repli. |
| Utiliser des tirets cadratins dans le résultat | Remplacez tous les tirets cadratins par des traits d'union. |
Lire les fichiers de référence avant chaque exécution
cat references/pricing-models.md
cat references/extraction-guide.md
cat references/positioning-guide.md
Étape 1 : Vérification de configuration
echo "TAVILY_API_KEY: ${TAVILY_API_KEY:+définie (qualité de recherche améliorée)}${TAVILY_API_KEY:-non définie, DuckDuckGo sera utilisé (gratuit)}"
echo "FIRECRAWL_API_KEY: ${FIRECRAWL_API_KEY:+définie (rendu JS amélioré)}${FIRECRAWL_API_KEY:-non définie, requests+BS4 seront utilisées (gratuit)}"
echo ""
python3 -c "from ddgs import DDGS; import requests, bs4, html2text; print('Dépendances OK')" 2>/dev/null \
|| echo "ERREUR : Dépendances manquantes. Exécutez : pip install ddgs requests beautifulsoup4 html2text"
Si les dépendances sont manquantes : Arrêtez-vous immédiatement. Dites à l'utilisateur : « Dépendances Python manquantes. Exécutez cette commande pour les installer : pip install ddgs requests beautifulsoup4 html2text -- tout est gratuit, aucun compte nécessaire. Puis réessayez. »
Si seules les clés API sont manquantes : Continuez. DuckDuckGo et requests+BS4 sont les options gratuites par défaut.
Dériver le slug du produit :
PRODUCT_SLUG=$(python3 -c "
from urllib.parse import urlparse
import sys, re
url = 'URL_ICI'
if url.startswith('http'):
host = urlparse(url).netloc.replace('www.', '')
print(host.split('.')[0])
else:
print(re.sub(r'[^a-z0-9]', '-', url[:30].lower()).strip('-'))
")
echo "Slug du produit : $PRODUCT_SLUG"
Étape 2 : Analyser l'entrée
Collectez depuis la conversation :
product_url: l'URL à télécharger (obligatoire, sauf si l'utilisateur colle directement une description)geography: optionnel -- États-Unis / Europe / Inde / global. Défaut : États-Unis
Si l'utilisateur fournit uniquement une description collée (pas d'URL) : Ignorez les étapes 3 et 4. Allez directement à l'étape 4 (analyse du produit) en utilisant le texte collé comme product_content. Définissez page_source à user_description et notez-le dans data_quality_flags.
Si ni URL ni description : Demandez : « Quelle est l'URL de votre produit ou startup ? Ou collez une courte description : ce qu'il fait, pour qui, et ce qui le rend différent. »
Étape 3 : Télécharger la page produit
Principal : Firecrawl (si FIRECRAWL_API_KEY est définie)
curl -s -X POST https://api.firecrawl.dev/v1/scrape \
-H "Authorization: Bearer $FIRECRAWL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "URL_ICI", "formats": ["markdown"], "onlyMainContent": true}' \
| python3 -c "
import sys, json
d = json.load(sys.stdin)
content = d.get('data', {}).get('markdown', '') or d.get('markdown', '')
print(f'Téléchargé via Firecrawl : {len(content)} caractères')
open('/tmp/pf-product-raw.md', 'w').write(content)
"
Solution de repli : requests + BS4 (gratuit, toujours disponible)
python3 << 'PYEOF'
import requests, html2text, random
USER_AGENTS = [
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
]
headers = {"User-Agent": random.choice(USER_AGENTS), "Accept": "text/html,application/xhtml+xml;q=0.9,*/*;q=0.8"}
resp = requests.get("URL_ICI", headers=headers, timeout=20, allow_redirects=True)
converter = html2text.HTML2Text()
converter.ignore_images = True
converter.body_width = 0
content = converter.handle(resp.text)[:8000]
print(f'Téléchargé via requests+BS4 : {len(content)} caractères')
open('/tmp/pf-product-raw.md', 'w').write(content)
PYEOF
Point de contrôle :
python3 -c "
content = open('/tmp/pf-product-raw.md').read()
if len(content) < 200:
print('ERREUR : moins de 200 caractères téléchargés -- la page est peut-être rendue via JS')
else:
print(f'Contenu OK : {len(content)} caractères')
"
Si le contenu < 200 caractères : Dites à l'utilisateur : « La page produit a retourné trop peu de contenu -- le site utilise peut-être un rendu JavaScript. Veuillez coller une courte description : ce que fait votre produit, pour qui, et ce qui le rend différent des concurrents. »
Étape 4 : Analyse du produit (IA)
Affichez le contenu de la page :
python3 -c "
content = open('/tmp/pf-product-raw.md').read()[:5000]
print('=== PAGE PRODUIT (5000 premiers caractères) ===')
print(content)
"
Instructions pour l'IA : Analysez la page produit ci-dessus et extrayez :
product_name: le nom du produit ou de l'entrepriseone_line_description: ce qu'il fait, pour qui, proposition de valeur principale. Moins de 20 mots. Aucun langage marketing.industry_taxonomy:l1(haut niveau : outils développeurs / fintech / santé / consommation / etc.),l2(secteur : devops / paiements / RH / etc.),l3(niche spécifique : automatisation CI/CD / paiements intégrés / vidéo asynchrone / etc.)differentiators: exactement 2-3 choses spécifiques qui distinguent ce produit. Ils alimentent la recommandation — soyez précis. Les réponses génériques comme « facile à utiliser » ne sont pas acceptables.icp:buyer_persona(intitulé),company_type,company_sizegeography_bias: États-Unis / Europe / Inde / globalpage_source: "live_page" ou "user_description"
Écrire dans /tmp/pf-product-analysis.json :
python3 << 'PYEOF'
import json
analysis = {
# REMPLISSEZ à partir de votre analyse ci-dessus
"product_name": "",
"one_line_description": "",
"industry_taxonomy": {"l1": "", "l2": "", "l3": ""},
"differentiators": [],
"icp": {"buyer_persona": "", "company_type": "", "company_size": ""},
"geography_bias": "États-Unis",
"page_source": "live_page"
}
json.dump(analysis, open('/tmp/pf-product-analysis.json', 'w'), indent=2)
print('Analyse produit écrite.')
PYEOF
Vérifiez :
python3 -c "
import json
a = json.load(open('/tmp/pf-product-analysis.json'))
print('Produit :', a['product_name'])
print('Secteur :', a['industry_taxonomy']['l1'], '>', a['industry_taxonomy']['l2'], '>', a['industry_taxonomy']['l3'])
print('Différenciateurs :')
for d in a['differentiators']:
print(f' - {d}')
"
Étape 4b : Phase 1 -- Découverte des concurrents
ls scripts/research.py 2>/dev/null && echo "script trouvé" || echo "ERREUR : scripts/research.py introuvable -- impossible de continuer"
python3 scripts/research.py \
--phase discover \
--product-analysis /tmp/pf-product-analysis.json \
--output /tmp/pf-competitors-raw.json
Affichez les résultats pour examen par l'IA :
python3 -c "
import json
data = json.load(open('/tmp/pf-competitors-raw.json'))
print(f'Recherches effectuées : {len(data[\"competitor_searches\"])}')
for s in data['competitor_searches']:
print(f'\nRequête : {s[\"query\"]}')
for r in s.get('results', [])[:6]:
print(f' - {r[\"title\"]} | {r[\"url\"]')
print(f' {r.get(\"snippet\",\"\")[:150]}')
"
Instructions pour l'IA : Lisez les résultats de recherche ci-dessus. Choisissez exactement 5 sociétés concurrentes qui :
- Sont nommées dans les titres ou extraits des résultats de recherche
- Sont dans la même niche L3 que le produit analysé
- Sont des produits logiciels réels (pas des agences, des listes d'articles ou des sites d'avis)
- Sont distinctes les unes des autres
Pour chaque concurrent, écrivez : name, url, pricing_url (leur page de tarification -- déduisez-la comme [url]/pricing si elle n'est pas trouvée dans les extraits), description (une phrase de l'extrait), source_url.
Étape 5 : Confirmation des concurrents
python3 << 'PYEOF'
import json
analysis = json.load(open('/tmp/pf-product-analysis.json'))
# REMPLISSEZ : 5 concurrents provenant des résultats de recherche ci-dessus
candidates = [
# {"name": str, "url": str, "pricing_url": str, "description": str, "source_url": str}
]
print(f"\n5 concurrents trouvés pour {analysis['product_name']} dans {analysis['industry_taxonomy']['l3']} :\n")
for i, c in enumerate(candidates, 1):
print(f" {i}. {c['name']} -- {c['description']}")
print(f" Produit : {c['url']}")
print(f" Tarification : {c['pricing_url']}")
data = json.load(open('/tmp/pf-competitors-raw.json'))
data['competitor_candidates'] = candidates
json.dump(data, open('/tmp/pf-competitors-raw.json', 'w'), indent=2)
PYEOF
Dites à l'utilisateur : « Voici les 5 concurrents dont je vais récupérer les données de tarification. Ajoutez, supprimez ou échangez-en -- ou dites 'ça a l'air bon' pour continuer. »
Attendez la confirmation. Si l'utilisateur modifie la liste, mettez à jour les candidats en conséquence. Ensuite, écrivez la liste confirmée :
python3 << 'PYEOF'
import json
# REMPLISSEZ : liste des concurrents confirmée (après révision par l'utilisateur)
confirmed = [
# {"name": str, "url": str, "pricing_url": str}
]
json.dump({"confirmed_competitors": confirmed}, open('/tmp/pf-competitors-confirmed.json', 'w'), indent=2)
print(f"{len(confirmed)} concurrents confirmés pour la recherche tarifaire.")
for c in confirmed:
print(f" - {c['name']} | tarification : {c['pricing_url']}")
PYEOF
Étape 6 : Phase 2 -- Récupération des pages de tarification
python3 scripts/research.py \
--phase fetch-pricing \
--competitors /tmp/pf-competitors-confirmed.json \
--output /tmp/pf-pricing-raw.json
Ceci récupère la page de tarification de chaque concurrent en utilisant une solution de repli à 3 niveaux :
- Téléchargement direct :
requests+beautifulsoup4+html2text - Cache Google :
webcache.googleusercontent.com/search?q=cache:[url] - Recherche DuckDuckGo :
"[concurrent]" tarification forfaits coût par mois(extrait de repli)
Affichez le résumé des téléchargements :
python3 -c "
import json
data = json.load(open('/tmp/pf-pricing-raw.json'))
print(f'Concurrents téléchargés : {data[\"competitors_fetched\"]}')
print()
for r in data['results']:
quality_label = {'high': 'BON', 'medium': 'OK', 'low': 'EXTRAIT SEULEMENT'}.get(r['data_quality'], r['data_quality'])
print(f' {r[\"name\"]:20} {r[\"source\"]:15} {r[\"content_length\"]:5} car. [{quality_label}]')
"
Si un concurrent a data_quality: low : Cela signifie que la page de tarification a été bloquée ou rendue via JS. L'analyse se poursuivra en utilisant les extraits de recherche, mais la confiance pour ce concurrent sera notée comme faible.
Étape 7 : Extraction des tarifs (IA)
Affichez tout le contenu brut des tarifs :
python3 -c "
import json
data = json.load(open('/tmp/pf-pricing-raw.json'))
for r in data['results']:
print(f'\n=== {r[\"name\"]} (source : {r[\"source\"]}, qualité : {r[\"data_quality\"]}) ===')
print(f'URL de tarification : {r[\"pricing_url\"]}')
print(r['content'][:4000])
print('---')
"
Instructions pour l'IA : Pour chaque concurrent, extrayez les données tarifaires structurées du contenu ci-dessus. Suivez references/extraction-guide.md pour savoir comment identifier les paliers, les prix, les limites et les appels à l'action.
Règles zéro hallucination :
- Extrayez les prix textuellement du contenu uniquement. Si un prix n'est pas dans le contenu, écrivez
null. - Enregistrez "Contactez le service commercial" exactement tel quel. Ne le remplacez jamais par un nombre estimé.
data_quality: lowsignifie que les données proviennent d'extraits de recherche -- extrayez ce qui est présent mais ne comblez pas les lacunes avec la connaissance du modèle.- Pour tout champ non présent dans le contenu : écrivez
"non trouvé dans les données de la page". - Prix annuels : enregistrez toujours l'équivalent mensuel avec le total annuel.
Écrire dans /tmp/pf-pricing-extracted.json :
python3 << 'PYEOF'
import json
# REMPLISSEZ : un objet par concurrent, suivant le schéma ci-dessous
extracted = [
# {
# "competitor": str,
# "pricing_url": str,
# "data_quality": "high" | "medium" | "low",
# "pricing_model": "per-seat" | "flat-rate" | "usage-based" | "freemium" | "tiered-flat" | "hybrid",
# "billing_cadence": ["monthly"] | ["annual"] | ["monthly", "annual"],
# "annual_discount": str, # ex. "20%" ou "non trouvé dans les données de la page"
# "free_tier": true | false,
# "free_trial": true | false,
# "free_trial_days": int | null,
# "tiers": [
# {
# "name": str,
# "price_monthly": float | null, # null si Contactez le service commercial
# "price_annual_monthly": float | null, # équivalent mensuel en facturation annuelle
# "price_note": str, # "Contactez le service commercial", "Gratuit", ou vide
# "seats": str, # "par siège", "illimité", "jusqu'à 5", etc.
# "key_limits": [str], # stockage, appels API, projets, etc.
# "key_features": [str] # 3 à 5 fonctionnalités principales de ce palier
# }
# ],
# "enterprise_tier": true | false,
# "enterprise_pricing": str, # "Contactez le service commercial" ou prix réel
# "regional_pricing": str | null # ex. "Inde : 999 ₹/mois" ou null
# }
]
json.dump(extracted, open('/tmp/pf-pricing-extracted.json', 'w'), indent=2, ensure_ascii=False)
print('Données tarifaires extraites.')
PYEOF
Vérifiez :
python3 -c "
import json
data = json.load(open('/tmp/pf-pricing-extracted.json'))
for c in data:
print(f'{c[\"competitor\"]:20} {c[\"pricing_model\"]:15} {len(c[\"tiers\"])} paliers | qualité : {c[\"data_quality\"]}')
for t in c['tiers']:
pm = t['price_monthly']
pf = f'{pm} €' if pm else t['price_note']
print(f' {t[\"name\"]:20} {pf:>10}')
"
Étape 8 : Analyse des fonctionnalités différenciatrices
Lisez les fichiers de référence :
cat references/pricing-models.md
Affichez les paliers extraits :
python3 -c "
import json
data = json.load(open('/tmp/pf-pricing-extracted.json'))
for c in data:
print(f'=== {c[\"competitor\"]} ===')
print(f'Modèle : {c[\"pricing_model\"]}')
for t in c['tiers']:
print(f' Palier : {t[\"name\"]}')
print(f' Prix mensuel : {t[\"price_monthly\"]}')
print(f' Sièges : {t.get(\"seats\", \"\")}')
print(f' Limites clés : {t.get(\"key_limits\", [])}')
print(f' Fonctionnalités clés : {t.get(\"key_features\", [])}')
print()
"
Instructions pour l'IA : Pour chaque concurrent, identifiez les fonctionnalités différenciatrices (fonctionnalités qui passent d'un palier payant à un palier supérieur, ou qui bloquent les utilisateurs gratuits). Suivez references/extraction-guide.md pour les règles de classification.
Écrire dans /tmp/pf-feature-gates.json :
python3 << 'PYEOF'
import json
pricing_data = json.load(open('/tmp/pf-pricing-extracted.json'))
# REMPLISSEZ : fonctionnalités différenciatrices par concurrent
feature_gates = {
# "competitor_name": [
# {
# "gate": str, # nom de la fonctionnalité différenciatrice
# "introduced_at": str, # "Free", "Starter", "Pro", "Enterprise", ou "not found"
# "category": str # usage/performance/features/support/customization
# }
# ]
}
json.dump(feature_gates, open('/tmp/pf-feature-gates.json', 'w'), indent=2)
print('Analyse des fonctionnalités différenciatrices écrite.')
PYEOF
Étape 9 : Construction et analyse du tableau de référence
python3 << 'PYEOF'
import json
pricing_data = json.load(open('/tmp/pf-pricing-extracted.json'))
analysis = json.load(open('/tmp/pf-product-analysis.json'))
# REMPLISSEZ : construisez le tableau de référence
# Incluez : le modèle dominant, le tableau des prix, les fonctionnalités différenciatrices et les observations de positionnement
benchmark = {
"dominant_model": {
"frequency": {}, # combien de concurrents utilisent chaque modèle
"most_common": "",
"identity": "" # nom du modèle le plus courant
},
"price_table": [
# {
# "competitor": str,
# "entry_price": float | "Freemium" | "Contactez le service commercial",
# "mid_price": float | "Contactez le service commercial",
# "enterprise_price": float | "Contactez le service commercial",
# "free_tier": bool,
# "free_trial": bool
# }
],
"feature_gates": {}, # copié de l'étape 8
"positioning_observations": [
# "Observation fondée sur les données uniquement — ex. : \"Seulement 2 concurrents sur 5 proposent un niveau gratuit avec des limites de stockage.\""
]
}
json.dump(benchmark, open('/tmp/pf-benchmark.json', 'w'), indent=2)
print('Tableau de référence construit.')
PYEOF
Étape 10 : Synthèse du rapport
Lisez la carte de positionnement :
cat references/positioning-guide.md
Instructions pour l'IA : Synthétisez un rapport final en français. Le rapport DOIT contenir ces sections exactement dans cet ordre :
1. Modèle de tarification dominant dans ce secteur
Expliquez le modèle le plus courant parmi les 5 concurrents et pourquoi il domine. Citez les données.
2. Tableau de référence des prix
Montrez clairement chaque concurrent, son prix d'entrée, son prix intermédiaire, son prix entreprise, et tout niveau gratuit ou essai gratuit. Utilisez un tableau bien formaté.
3. Analyse des fonctionnalités différenciatrices
Montrez quelles fonctionnalités sont verrouillées derrière des paliers plus élevés et chez quels concurrents. Expliquez les barrières à l'entrée et les opportunités de promotion.
4. Carte de positionnement concurrentiel
Positionnez chaque concurrent sur une matrice 2D de prix (axe Y) vs. fonctionnalités/évolutivité (axe X). Utilisez des notations comme [Haut-Milieu], [Bas-Bas] pour décrire la position. Expliquez clairement où se trouve votre produit sur cette carte.
5. Recommandation de prix stratégique
Recommandez une position de prix (gamme de prix) ET un modèle de tarification (ex. : par siège, forfait fixe, basé sur l'utilisation) pour votre produit. Votre recommandation DOIT :
- Citer une ou plusieurs données du tableau de référence (section 2)
- Faire référence aux fonctionnalités différenciatrices (section 3)
- Tenir compte de votre positionnement sur la carte (section 4)
- Proposer un prix mensuel d'entrée en USD, un prix intermédiaire, un prix entreprise, et si un niveau gratuit ou un essai gratuit doit être proposé
Règles de qualité :
- Chaque nombre dans la recommandation doit renvoyer à une ligne spécifique du tableau de référence
- Utilisez des guillemets pour marquer les citations directes des données
- Paraphrasez le langage marketing des concurrents en termes factuels
- Supprimez tout langage vague ou générique sur la "croissance" ou la "valeur" — soyez précis et entraînable
- Si des données d'un concurrent sont marquées
data_quality: low, notez-le et expliquez pourquoi la confiance est réduite - Ne présupposez jamais l'intention de l'utilisateur ; si les données contredisent une hypothèse, dites-le et demandez des éclaircissements