API Anthropic in Next.js: streaming, cache e controllo dei costi
Come collegare Claude a un'app Next.js: route handler lato server, streaming SSE, prefisso in cache al 10% del costo di input e retry che reggono un 429.
Alla fine di questa guida hai un route handler Next.js che manda in streaming la risposta di Claude mentre viene generata, un system prompt in cache che sui giri successivi costa un decimo del prezzo di input, una gestione dei retry che regge un rate limit e un costo per chiamata che puoi mettere in un foglio di calcolo prima di andare online.
È lo schema che usiamo quando un cliente chiede l'AI dentro al prodotto. Un riassuntore di ticket. Un generatore di bozze. Un classificatore che legge i dati del cliente stesso. Superficie piccola, traffico alto, soldi veri a ogni chiamata. I framework per agenti meritano un altro articolo. Qui parliamo dell'impianto che ci sta sotto.
Cosa serve prima di scrivere codice
Tre cose. La terza è quella che quasi tutti saltano.
- Una chiave API dalla Console Anthropic, tenuta lato server.
- Una scelta sul runtime su cui gira l'handler.
- Tre numeri: chiamate al giorno, token di input medi, token di output medi.
Senza il terzo punto il costo diventa una sorpresa che arriva a fine mese. Una funzione da 2.000 chiamate al giorno con un system prompt da 4.000 token si comporta in modo del tutto diverso dalla stessa funzione a 200 chiamate con un prompt da 400 token, e la soluzione cambia nei due casi. Gli ordini di grandezza per tipo di funzione stanno nella nostra analisi sul costo di integrare l'AI in un SaaS.
SDK Anthropic o AI SDK di Vercel?
Entrambe le risposte si difendono. La scelta riguarda quanto ti vuoi legare a un fornitore.
@anthropic-ai/sdk è il client ufficiale. Hai ogni parametro specifico di Claude il giorno stesso in cui esce: i punti di rottura della cache, il ragionamento esteso, la forma completa degli eventi di streaming. Lo usiamo quando la funzione AI è il prodotto.
L'AI SDK di Vercel mette un'interfaccia sola sopra molti fornitori, ciascuno in un pacchetto separato (@ai-sdk/anthropic, @ai-sdk/openai). Cambiare modello diventa una modifica da due righe, e useChat lato client risparmia una giornata di parsing dello stream. Il prezzo è il ritardo: un'astrazione che copre una dozzina di fornitori arriva sempre dopo sulla novità di ognuno. Lo usiamo quando la libertà di cambiare fornitore è scritta nel contratto.
Passo 1: la chiave resta sul server
La chiave sta in ANTHROPIC_API_KEY, mai in una variabile NEXT_PUBLIC_, e la chiamata avviene dentro un Route Handler. Lo scrive ogni guida introduttiva. In produzione capita comunque di trovarla altrove. Una chiave dentro al bundle del browser è una chiave sulla bolletta di qualcun altro.
import Anthropic from '@anthropic-ai/sdk'
const client = new Anthropic()
export async function POST(req: Request) {
const { question } = await req.json()
const stream = client.messages.stream({
model: 'claude-sonnet-5',
max_tokens: 1024,
system: SYSTEM_PROMPT,
messages: [{ role: 'user', content: question }],
})
return new Response(stream.toReadableStream())
}Anche le Server Action funzionerebbero, ma qui la forma giusta è il Route Handler: stai restituendo uno stream, non modificando uno stato. La distinzione l'abbiamo già tracciata in type-safe RPC con le Server Action.
Passo 2: streaming, perché l'attesa sembra un guasto
Una risposta da 1.000 token richiede diversi secondi di generazione. Renderizzata tutta insieme diventa uno spinner e un utente che ricarica la pagina. In streaming le prime parole arrivano in meno di un secondo e lo stesso tempo reale sembra breve.
La Messages API trasmette via server-sent event. Nell'SDK TypeScript .stream() tiene aperta la connessione ed emette eventi tipizzati, mentre .finalMessage() li accumula nel messaggio completo, utile quando la risposta intera ti serve anche lato server per il log o per scriverla a database.
Due dettagli contano davvero. Registra il messaggio finale sul server e non nel browser, altrimenti le statistiche perdono in silenzio ogni richiesta abbandonata. E decidi che cosa significa una risposta a metà nel tuo modello dati. Noi scriviamo la riga su message_stop e segniamo come fallito tutto ciò che si interrompe prima: così le generazioni troncate restano fuori dallo storico del prodotto.
Passo 3: metti in cache il prefisso statico
Quasi tutte le funzioni AI di prodotto mandano gli stessi 2.000-8.000 token a ogni chiamata: system prompt, definizione dei tool, schema dei dati del cliente, qualche esempio. Pagarli a prezzo pieno di input ogni volta è lo spreco più frequente che troviamo nelle funzioni AI andate online senza revisione.
Il prompt caching lo elimina. Segna la fine della parte statica con un punto di rottura della cache. Scrivere la cache costa 1,25 volte la tariffa base di input con TTL a cinque minuti, e ogni lettura successiva di quel prefisso ne costa il 10%, come indica la pagina prezzi di Anthropic. Il pareggio arriva alla seconda chiamata.
Una regola sola tiene in piedi tutto: ordina la richiesta partendo dallo statico. Il prefisso in cache deve corrispondere esattamente, quindi tutto ciò che cambia a ogni richiesta (la domanda, un timestamp, il nome del tenant) va dopo il punto di rottura. Basta un valore dinamico in mezzo al system prompt e la percentuale di hit crolla a zero, senza nessun errore che te lo segnali. Controlla cache_read_input_tokens nel blocco usage della risposta: se alla seconda chiamata identica è 0, il prefisso si è spostato.
Passo 4: 429 e 529 sono traffico normale
Arriveranno entrambi. Un 429 significa rate limit superato e porta con sé l'header retry-after che dice quanto aspettare. Un 529 significa API temporaneamente sovraccarica: riguarda la capacità del servizio, non il tuo account.
Gli SDK ufficiali riprovano già due volte in automatico sugli errori transitori, con backoff esponenziale e rispettando retry-after. Questo copre i casi noiosi. Non copre quello interessante: un errore dopo che i primi byte sono già arrivati al browser. Riprovare lato server a quel punto significa spedire una risposta doppia. Noi tratteniamo la risposta finché lo stream non è stabilito, poi mostriamo qualsiasi interruzione successiva come errore visibile con un comando per riprovare, invece di cucire insieme due generazioni sperando che nessuno legga con attenzione.
Imposta un timeout tuo, più basso di quello della piattaforma, così l'utente legge il tuo messaggio di errore e non una pagina 504.
Passo 5: togli dal percorso della richiesta ciò che non è interattivo
Riassunti notturni, classificazioni di massa, rietichettatura di 40.000 righe vecchie: niente di tutto questo ha bisogno di una connessione aperta. La Message Batches API accetta fino a 10.000 richieste per lotto, risponde entro 24 ore e costa il 50% in meno delle stesse chiamate fatte in sincrono. Lo sconto si somma al prompt caching.
La nostra regola: se nessuna persona sta aspettando la risposta, va in batch. In un prodotto maturo una fetta consistente della spesa AI sta in lavori finiti sul percorso interattivo per abitudine, non per necessità.
Passo 6: scegli runtime e budget di durata
Un handler AI in streaming è una richiesta di lunga durata, e questo rende la scelta del runtime una decisione vera invece di un valore predefinito. Su Vercel una funzione che sfora il budget restituisce un 504 FUNCTION_INVOCATION_TIMEOUT a metà stream, e l'utente si ritrova una risposta troncata.
Il runtime Edge trasmette fino a 300 secondi ma deve iniziare a rispondere entro 25 secondi. Va benissimo per una chat, va male quando il primo token aspetta una query lenta. Le funzioni Node sui piani Pro ed Enterprise ora arrivano a 30 minuti, il che copre generazioni lunghe e orchestrazione dei batch. Il nostro default è Node ogni volta che l'handler tocca il database prima di chiamare Claude, ed Edge per i prompt senza stato dove conta solo la latenza percepita. Il confronto completo sta in Edge runtime contro Node runtime.
Cosa questo schema non ti dà
La distanza fra una demo che funziona e una funzione che puoi far pagare sta quasi tutta qui.
- Nessuna valutazione. Niente di quanto sopra ti dice se le risposte sono buone. Un insieme di 30-50 esempi giudicati a mano, rieseguito a ogni modifica del prompt, è il controllo qualità più economico che esista nel lavoro con l'AI ed è la prima cosa che si rimanda.
- Nessun budget per utente. I rate limit di Anthropic valgono per la tua organizzazione, non per i tuoi clienti. Un solo utente in loop affama tutti gli altri tenant.
- Nessuna difesa dalla prompt injection. Qualsiasi testo scritto da un utente e passato al modello può provare a riscriverne le istruzioni. Tratta l'output del modello come input non fidato per il resto del sistema, soprattutto se i tool possono scrivere.
- Nessun contratto sull'output. Il testo libero va bene per un riassunto e non serve a niente per un campo del database. Vincola la forma con i tool o con gli structured output prima di fare il parsing.
L'ordine conta più di ogni singola voce dell'elenco. Chiave sul server, poi streaming, poi cache, poi retry, poi batch, poi runtime. Chi parte dalla cache prima di avere i numeri di traffico ottimizza il prefisso sbagliato. Chi parte dal runtime riscrive l'handler due volte.
Domande frequenti
Come stimo il costo mensile di una funzione AI prima di costruirla?
Parti da tre numeri: chiamate al giorno, token di input medi, token di output medi. Moltiplica per le tariffe per token del modello scelto e per 30. Poi applica due correzioni. I token del prefisso in cache si pagano al 10% della tariffa di input dopo la prima scrittura, e tutto ciò che sposti sulla Message Batches API si paga al 50%. Aggiungi un 30% di margine per i retry e per le risposte più lunghe dei prompt di prova. Prima di fidarti della stima, manda 20 prompt reali all'API e confronta i token misurati con quelli previsti.
Cosa succede al prodotto quando l'API di Anthropic è giù?
Ricevi errori 529 di sovraccarico, e la decisione è di prodotto più che tecnica. Tre strade: mettere la richiesta in coda e consegnare la risposta dopo via email o notifica, ripiegare su un secondo fornitore dietro la stessa interfaccia, oppure fallire in modo visibile e lasciare che l'utente riprovi. La coda funziona per tutto ciò che è asincrono. Il secondo fornitore costa un secondo prompt da mantenere, perché nella pratica i prompt non sono portabili. Fallire in modo visibile è la scelta onesta per le funzioni interattive e richiede uno stato di errore vero nell'interfaccia, disegnato prima del lancio e non dopo il primo disservizio.
Anthropic addestra i suoi modelli sui dati inviati tramite API?
No. Nei termini commerciali, input e output dell'API non vengono usati per addestrare i modelli, salvo che il cliente scelga esplicitamente di aderire, per esempio a un programma partner. Vale per l'API, per Claude for Work e per i modelli serviti tramite Amazon Bedrock e Google Cloud Vertex AI. Non risponde però a tutto quello che chiederà una revisione di conformità: tempi di conservazione, sub-responsabili, residenza dei dati e un DPA firmato sono questioni distinte, e per un prodotto sanitario o finanziario vanno messe nel contratto prima della prima richiesta.
Serve un rate limiting mio oltre a quello di Anthropic?
Sì, in qualsiasi prodotto multi-tenant. I limiti di Anthropic valgono per l'organizzazione nel suo insieme, quindi un solo cliente bloccato in un ciclo di retry consuma la quota di tutti gli altri e a quel punto i 429 li vede chiunque. Un contatore su Redis con chiave per utente o per tenant, controllato prima della chiamata, si risolve in un pomeriggio. Il tetto per utente va deciso a partire dal listino, non dal limite dell'API: se un piano include 200 generazioni al mese, quel numero sta nel codice, e superarlo deve leggersi come limite di piano e non come errore del server.
Articoli correlati
Studio
Inizia un progetto.
Un partner unico per il prodotto digitale che devi costruire. Produzione più veloce, tecnologie moderne, costi ridotti. Un team, una fattura.