name: external-search-apis-strategy description: Strategia cost-proof + uso intelligente di Tavily (web-trends) e Perplexity Sonar (Reach · Visibilità AI) prima di abilitarle in prod last_update: 2026-06-06 owner: gregorio
Tavily + Perplexity Sonar · strategia costi, guardrail e uso iper-personalizzato
Vincolo: niente abilitato in prod finché cap + monitoraggio non sono in piedi (lo sono, vedi §4). I prezzi sono verificati dal web (giugno 2026), non dal training.
0. TL;DR
- Costo unitario reale: Tavily $0 su Free (o $0.016/ricerca PAYG) · Perplexity ~$0.0055/check (il request-fee
lowpesa il 90%). - Costo a 500 clienti: Tavily ~$0/mese (condiviso, sublineare nei combo) · Perplexity ~$8/mese atteso, ≤$50–165/mese anche al cap rigido. < 0,4% del ricavo/cliente in ogni scenario.
- Il rischio non è il margine, è l'abuso non limitato. I guardrail tagliano la coda di rischio + danno osservabilità.
- Spedito (PR1): cap mensile Perplexity (Pro 30 / Business 100, hard), gating su route + hook publish, pin
model=sonar+context=low, costo reale loggato, ledgerexternal_api_usage+ guard Sentry, budget-guard Tavily, grana-città (Tavily iperlocale). - Compliance: Perplexity → sub-processor
secondary(preavviso 30gg, efficace 2026-07-10) · Tavily → non sub-processor (fonte dati, zero PII). Ratifica audit 08/06. - Founder checklist: §8.
- Stato 2026-06-06 sera · TUTTO CHIUSO: #277 (guardrail + città + compliance) MERGED + deployato + verificato live (probe
/api/reach/visibility-checkdal workspace founder →404 no_published_site= oltre il check-key → key Perplexity attiva + tier-gate + cap enforcement deployati, zero costo perché 404 prima della call Sonar). Key Tavily + Perplexity inserite + redeploy (founder). #282 (flywheel competitor→Content) MERGED + deployato (CI verde dopo lo sblocco billing). Grana-città live. Billing GitHub Actions risolto (budget Actions era già $40 · le fail erano run pre-fix con budget $0 · ri-lanciata la CI → verde). Primo uso reale: Sonar al primo sito pubblicato · Tavily al cron del lunedì (rigaexternal_api_usageprovider=tavily). - Dogfooding 2026-06-06 sera · PRIMO CHECK PERPLEXITY REALE eseguito (workspace Multiverbe · sito-test minimale creato→archiviato per sbloccare la route):
200in 6,6s, costo reale $0,00572 (costIsReal:true, dal billing del provider, = stima $0,0055), 9 competitor citati, flywheel alimentato. Confronto con la competitive-analysis BOS: ✅ bexio citato (conferma il #1 documentato) · 🔴 GAP: KLARA (klara.ch) è la PRIMA raccomandazione dell'AI per il positioning Multiverbe (svizzero all-in-one PMI) ma manca dal BOS (+ Giobby) → la feature ha trovato un competitor di testa mancante. Tasktask_c2b7d487per aggiungerlo. Pipeline verificata end-to-end dal vivo.
1. Assunzioni esplicite
| Parametro | Valore | Fonte |
|---|---|---|
| Tavily search_depth | advanced = 2 crediti/ricerca |
codice web-trends.ts + docs |
| Tavily frequenza | 1 ricerca per combo (settore×zona[×città]), cron settimanale (Lun 05:00) ×4,33/mese | content-market-context.ts |
| Tavily Free | 1.000 crediti/mese, no carta | docs.tavily.com |
| Tavily PAYG | $0.008/credito | docs.tavily.com |
| Perplexity modello | sonar, search_context_size: low (pin) |
perplexity-sonar.ts |
| Perplexity token | ~100 in + ~400 out (system IT + risposta breve) | stima da payload max_tokens 600 |
| Perplexity prezzi | token $1/$1 per 1M (in/out) · request-fee $5/1k a low |
docs.perplexity.ai |
| Perplexity trigger | 1 al publish (raro) + check manuali on-demand · solo Pro/Business | publish.ts + visibility-check.ts |
| Cambio | $1 ≈ CHF 0,90 | corrente |
| Eleggibili Perplexity | ~60% dei clienti (Starter escluso) | pricing tier |
2. Prezzi verificati dal web (giugno 2026)
- Tavily — docs.tavily.com/documentation/api-credits: basic 1 cr · advanced 2 cr · Free 1.000 cr/mese (no carta) · PAYG $0.008/cr · piani $30/4k → $500/100k.
- Perplexity Sonar — docs.perplexity.ai/docs/getting-started/pricing:
sonartoken $1/$1 per 1M + request-fee $5/$8/$12 per 1k (low/med/high, low = default);sonar-pro$3/$15 per 1M + $6/$10/$14 per 1k.
Costo reale per chiamata:
| Calcolo | Totale | |
|---|---|---|
| Tavily advanced | 2 cr × $0.008 | $0.016 ($0 su Free) |
Perplexity sonar low |
100/1M·$1 + 400/1M·$1 + $0.005 fee = $0.0001 + $0.0004 + $0.005 | ≈ $0.0055 ≈ CHF 0,005 |
Il codice precedente stimava
sonar = $0.003(–45%, non contava il request-fee). Corretto + ora il costo reale è letto dausage.cost.total_costquando il provider lo riporta.
3. Modello costo a 5 / 50 / 500 clienti
Tavily — costo CONDIVISO (non per-cliente), sublineare nei combo
| Clienti | Combo stimati¹ | Crediti/mese | Costo TOTALE/mese |
|---|---|---|---|
| 5 | ~4 | ~35 | $0 (Free) |
| 50 | ~20 | ~175 | $0 (Free) |
| 500 | ~70 | ~610 | $0 (Free) |
| 500 patologico (196 combo) | 196 | ~1.700 | ~$5,6 totali |
¹ I combo sono limitati dalle coppie (settore × zona × città) REALI dei clienti, non dal numero di clienti: molti ristoranti a Lugano = 1 combo. Tetto $ vero = il piano Tavily: tenuto Free (no carta) = tetto rigido $0; esauriti i crediti l'adapter fail-open sul baseline.
⚠️ La grana-città (§5) alza i combo da ~per-zona a ~per-città: a 500 clienti potrebbe sfiorare/superare i 1.000 crediti free (~$2–3/mese PAYG). Resta trascurabile; il budget-guard + Free lo coprono.
Perplexity — per-cliente (Pro/Business), atteso vs cap
| Clienti | Eleggibili | Atteso $/mese² | CHF/cliente atteso | Worst-case al cap |
|---|---|---|---|---|
| 5 | 3 | $0,08 | ~CHF 0,02 | $0,50 |
| 50 | 30 | $0,83 | ~CHF 0,025 | ~$5 |
| 500 | 300 | $8,25 | ~CHF 0,025 | ~$50 (Pro) / ~$165 (tutti Business) |
² ~5 check/mese per cliente eleggibile (1 publish + 4 manuali).
Impatto a margine
| Tier | Prezzo/mese | Perplexity atteso | Perplexity al cap | % ricavo (cap) |
|---|---|---|---|---|
| Pro | CHF 149 | ~CHF 0,02 | CHF 0,15 (30 check) | 0,1% |
| Business | CHF 249 | ~CHF 0,05 | CHF 0,50 (100 check) | 0,2% |
Tavily + Perplexity combinate: < 0,2% del ricavo/cliente a regime, < 0,4% ai cap rigidi. Il moat (zero-commissioni, hosting CH) non è toccato.
Conclusione strategica: il rischio non è l'economia unitaria — è (a) abuso non limitato di /visibility-check, (b) hook publish senza cap, (c) flip a sonar-pro/high (×3–10), (d) bug che esplode i combo Tavily, (e) zero osservabilità. I guardrail (§4) chiudono tutti e cinque.
4. Guardrail spediti (PR1) — defense-in-depth
| # | Guardrail | Dove |
|---|---|---|
| 1 | Cap mensile ai_visibility_checks (Starter 0 / Pro 30 / Business 100, hard_cap) |
migration 2026-06-06_external_search_apis_guardrails.sql (applicata prod) |
| 2 | Enforcement server-side (402 al superamento), enforced-always ma fail-open | api/_lib/tier-cap-check.ts |
| 3 | Cap pre-Sonar sulla route manuale + sull'hook di publish | visibility-check.ts · publish.ts |
| 4 | Pin model='sonar' + search_context_size='low' esplicito |
perplexity-sonar.ts |
| 5 | Costo reale loggato (usage.cost.total_cost, fallback calcolato) |
perplexity-sonar.ts · engine.ts |
| 6 | Ledger unico external_api_usage (Perplexity per check, Tavily per combo) |
api/_lib/external-api-usage.ts |
| 7 | Guard spesa mensile per provider → Sentry + email founder | api/cron/tier-caps-alerts.ts |
| 8 | Tavily budget-guard: combo cap/run (400) + Free = tetto $0 | content-market-context.ts |
Soglie anomalia (env-tunabili): Perplexity $25/mese · Tavily $8/mese (≈ valore dei 1.000 crediti free → superarlo = "Free esaurito"). Default alti vs atteso → scattano solo su vera anomalia.
Tier-gating Perplexity confermato: route visibility-check.ts 403 per Starter/tier sconosguto (least-privilege); hook publish già gated Pro/Business. + cap ora su entrambi.
5. 🧠 Uso iper-intelligente — architettura "Ambient + Sharp"
Layer 1 · Ambient (condiviso, ~gratis, cron): trend settore×zona via Tavily→Vertex EU. Già live. Ogni tenant riceve il "rumore di fondo" tempestivo.
Layer 2 · Sharp (per-cliente, ~$0 incrementale perché riusa cron o dati già catturati):
- (A) Grana-città — SPEDITO. La chiave del cron passa da (settore, zona) a (settore, zona, città) quando la città è nota (da
sites.brand_dna.locality). Un palestra di Lugano e una di Bellinzona non condividono più i trend. Query iperlocale "a Lugano" invece di "in Ticino", localizzata per lingua zona (de→"in", fr→"à"). Fail-soft: città assente → grana-zona (comportamento storico). Costo: bounded dalle città reali dei clienti, dentro il Free. - (B) Flywheel competitor → Content — DA FARE (dopo Perplexity live). Perplexity ci dice chi cita l'AI per "miglior {settore} a {città}" = i veri concorrenti locali (
ai_visibility_results.cited_domain). Oggi quel dato muore in un cruscotto Reach. Rimandarlo nel Content planet come angoli di differenziazione: "Le AI consigliano [3 concorrenti] a {città}. Ecco 3 contenuti dove sono deboli." Zero call extra. Richiede che Perplexity abbia già girato e catturato dati (per questo è post-attivazione). - (C) Strategia query — DA FARE. Ruotare i 3 intent (discovery→category→branded "{nome azienda}", il gap più temuto da una PMI) sui check schedulati → quadro 360° allo stesso costo/check. (D) Locale Perplexity dalla zona (cliente di Zurigo interroga in DE, non IT) → citazioni più pertinenti.
Sequenza onesta: A + guardrail ora (Tavily non dipende da Perplexity). B/C/D dopo l'attivazione di Perplexity (il flywheel non ha carburante finché Perplexity non gira).
6. Classificazione sub-processor (allineata all'audit 08/06)
- Perplexity → sub-processor
secondary(già deciso founder/codice). Aggiunto asubprocessors.ts(v2026-06-09) +SUBPROCESSOR_CHANGELOG. Preavviso 30gg revFADP art. 9: annuncio 2026-06-09, efficace 2026-07-10. Query non-PII ma check per-cliente + risultati per-workspace → disclosure conservativa. ⚠️announcedOn= data reale di deploy. DPA URL omessa finché non verificata dal web. - Tavily → NON sub-processor (fonte dati). Riceve solo settore+zona+stagione, zero PII/dato cliente, indice pubblico condiviso. Non tratta dati personali per conto del titolare → fuori da art. 9/28. Disclosure come "fonte dati" in
sub-processors.md. Ratifica audit 08/06.
Conseguenza chiave: l'esposizione di Perplexity a clienti esterni parte solo dopo il preavviso (~2026-07-10). Il workspace interno del founder (nessun titolare terzo da notificare) può dogfoodare subito. Tavily non ha questo vincolo (non è sub-processor) → live appena PR1 in master + key inserita + Tavily su Free.
7. Cosa è stato spedito / deferito
PR1 (cost-safety + città) — code repo, CI-verde, NON auto-merge:
- migration applicata prod (additiva, RLS deny-by-default sul ledger; advisor: solo INFO
rls_enabled_no_policy, stessa postura voluta diidempotency_keys). - 9 file codice + 3 test (40 test verdi: cost-math, aggregatore spesa, grana-città, compliance changelog).
PR2 (compliance) — subprocessors.ts + questo doc + sub-processors.md. Ratifica date all'audit 08/06.
Deferito (post-attivazione Perplexity): flywheel competitor→Content (B), intent rotation (C), locale-by-zone (D). Regen supabase.ts types: trivial follow-up (nessun gate di drift, tabella non referenziata dal client tipato).
8. 🔴 DA FARE — founder
✅ RISOLTO #0 — Billing GitHub Actions (2026-06-06 sera): il budget Actions era già a $40 (Settings → Billing → Budgets and alerts · le fail erano run pre-fix con budget $0/minuti inclusi esauriti). Ri-lanciata la CI di #282 → verde → mergiato. GitHub a posto, CI ripartita repo-wide (sblocca anche le PR growth/sales delle altre sessioni). Era il collo v88.
- Tavily (#277 già in master → può andare live):
- Tenere l'account Tavily su Free (NESSUNA carta) = tetto rigido $0.
- Inserire
TAVILY_API_KEY(da app.tavily.com) su Vercel multiverbe-app → Production + Preview, Sensitive → Redeploy. (SEARCH_PROVIDER=tavilygià presente.) - Verifica: lunedì prossimo il cron scrive righe
tavilyinexternal_api_usage.
- Perplexity (gate compliance):
- Attivare billing pay-as-you-go su console.perplexity.ai.
- Inserire
PERPLEXITY_API_KEYsu Vercel multiverbe-app (Prod+Preview, Sensitive) → Redeploy. - Esterni solo dal 2026-07-10 (preavviso 30gg). Interno/founder: dogfooding subito OK.
- Verificare URL DPA Perplexity dal web → aggiungere
dpaUrlinsubprocessors.ts.
- Compliance: ratificare le classificazioni (Perplexity sub-processor / Tavily fonte dati) al subprocessor-completeness-audit di lun 08/06; confermare
announcedOn= data reale di deploy (eeffectiveFrom= +31gg). Aggiornare iubenda 92115618 + DPA cliente template. - Review PR1 + PR2 (non auto-merge). Post-merge: 1 check manuale di visibilità sul workspace founder → confermare riga ledger + costo reale.
- (Opz.) Tarare le soglie Sentry via env
EXTERNAL_SPEND_ALERT_PERPLEXITY_USD/_TAVILY_USD.
Ref: subprocessors.ts · sub-processors.md · tier-cap-check.ts · external-api-usage.ts · migration 2026-06-06_external_search_apis_guardrails.sql · skill multiverbe-ch-compliance.