Tutorials
Tutorial: Add Anthropic API Calls to Your CONTENTFORGER App
Add Claude-powered features to any CONTENTFORGER output in under ten minutes. With streaming.
By Thomas — 2026-04-04, last updated 2026-04-04
CONTENTFORGER generates apps. Sometimes you want those apps to have their own AI features. Here is how to add Claude API calls to any CONTENTFORGER-generated project.
What you are building
A simple endpoint that takes a prompt, calls Claude, streams the response back to the frontend. Works for chatbots, content generators, analysis tools, anything.
Step one: install the SDK
In your CONTENTFORGER project:
pnpm add @anthropic-ai/sdk
Step two: get an API key
Go to console.anthropic.com. Sign up if you have not. Navigate to API Keys. Create a new key.
Add to .env.local:
ANTHROPIC_API_KEY=sk-ant-api03-your-key-here
Step three: set a monthly budget
This is the step people skip and regret. In the Anthropic console, go to Plans and Billing. Set a monthly spending limit. Start with fifty dollars.
If a bug sends your API into a loop, this cap saves you. Do not skip this step.
Step four: create the API route
Create app/api/claude/route.ts:
import Anthropic from '@anthropic-ai/sdk'
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY! })
export async function POST(req: Request) { const { prompt } = await req.json()
const encoder = new TextEncoder() const stream = new ReadableStream({ async start(controller) { const response = await client.messages.stream({ model: 'claude-sonnet-4-6', max_tokens: 2000, messages: [{ role: 'user', content: prompt }] })
for await (const chunk of response) { if (chunk.type === 'content_block_delta' && chunk.delta.type === 'text_delta') { controller.enqueue(encoder.encode(chunk.delta.text)) } } controller.close() } })
return new Response(stream, { headers: { 'Content-Type': 'text/plain; charset=utf-8', 'Transfer-Encoding': 'chunked' } }) }
Step five: add rate limiting
Do not skip this. A public API endpoint that calls Anthropic will be abused within days.
Simple in-memory rate limit. Add to the top of route.ts:
const counts = new Map<string, { count: number; reset: number }>()
function rateLimit(ip: string, max = 20, window = 3600000) { const now = Date.now() const record = counts.get(ip) if (!record || record.reset < now) { counts.set(ip, { count: 1, reset: now + window }) return true } if (record.count >= max) return false record.count++ return true }
In your POST handler:
const ip = req.headers.get('x-forwarded-for') ?? 'anonymous' if (!rateLimit(ip)) { return new Response('Rate limit exceeded', { status: 429 }) }
This allows twenty requests per hour per IP. Adjust to taste.
Step six: the frontend
Create a page or component that calls the endpoint and displays the streaming response:
'use client' import { useState } from 'react'
export default function ClaudePage() { const [prompt, setPrompt] = useState('') const [response, setResponse] = useState('') const [loading, setLoading] = useState(false)
async function handleSubmit() { setLoading(true) setResponse('')
const res = await fetch('/api/claude', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt }) })
const reader = res.body!.getReader() const decoder = new TextDecoder()
while (true) { const { done, value } = await reader.read() if (done) break setResponse(prev => prev + decoder.decode(value)) }
setLoading(false) }
return ( <div> <textarea value={prompt} onChange={e => setPrompt(e.target.value)} /> <button onClick={handleSubmit} disabled={loading}> {loading ? 'Thinking...' : 'Send'} </button> <div>{response}</div> </div> ) }
Step seven: add authentication if needed
If you want only logged-in users to call this endpoint, add Clerk middleware.
At the top of your route:
import { auth } from '@clerk/nextjs/server'
export async function POST(req: Request) { const { userId } = await auth() if (!userId) return new Response('Unauthorized', { status: 401 }) // ... rest of your handler }
Model choice
Haiku is fastest and cheapest. Use for simple tasks, short responses, high volume. Sonnet is the sensible default. Good quality, reasonable cost. Opus is most capable but costly. Use for complex reasoning or when output quality justifies the spend.
For a user-facing chatbot, start with Sonnet. Switch down to Haiku for features where quality matters less. Switch to Opus only for premium tier features.
Cost tracking
Log input and output tokens for every call. Cheap to do, saves you from billing surprises.
Most serious AI apps maintain a per-user token counter in the database and enforce monthly caps. If you are building a paid product, do this from day one.
What you just built
A streaming Claude endpoint in a CONTENTFORGER app. Same pattern works for any Claude-powered feature. Chat interfaces, content generation, summarisation, analysis. Swap the system prompt and the UI.
Total time: under ten minutes if you move quickly. The pattern is the same for every AI feature you will ever add.