AI Roadmap · Step 04

Building with AI APIs

← Back to upskilling path

The ceiling of other people's products

Everything so far has been about using tools someone else built. Those tools are good, and they have a hard ceiling: they do what their designer imagined. The moment you want AI inside your thing — triaging your support queue, tagging your photos, summarising your database rows at 3am — no product will do it, because your thing is yours.

That's what the API is for. It's the same model you've been talking to, minus the interface, addressable from code.

A chat window is one application built on the API. You're about to build the second one.

The key, and the one mistake that matters

Access starts with an API key — a long secret string you generate in your provider's console. Every request carries it, and the bill follows it.

Which means the key is your credit card. And there's exactly one mistake that accounts for most leaked keys: putting it somewhere the browser can see.

Anything in frontend JavaScript is public. Not obscured, not hard to find — public. "View source" public. If your key is in the browser bundle, strangers will spend your money with it, usually within days.

So: where does the key go?

Placement 1 of 3
const key = "sk-ant-..."  // in your React component
Never. This ships to every visitor's browser. Bundlers don't hide it, minifying doesn't hide it, and an environment variable read at build time still gets baked into the bundle. If you've done this, rotate the key now — assume it's already scraped.
Placement 2 of 3
process.env.ANTHROPIC_API_KEY  // in your server route
Safe. The key lives on your server, set as an environment variable in your hosting dashboard — never in the repo. Your browser code calls your endpoint; your server calls the model. This one indirection is the whole security model.
Placement 3 of 3
.env committed to the repo  // "it's a private repo"
Never. Private repos get forked, cloned to laptops, and made public by accident — and git remembers forever, so deleting the file later doesn't remove it from history. Add .env to .gitignore before you create it, not after.
💡 Tip: Set a spend limit in your provider's console on day one. It's the difference between a leaked key costing you $20 and costing you $2,000 — and it takes about thirty seconds.

Anatomy of a call

Underneath every AI product is one HTTP POST. Here's a complete request. Click any highlighted part to see what it does.

POST https://api.anthropic.com/v1/messages x-api-key: $ANTHROPIC_API_KEY anthropic-version: 2023-06-01 { "model": "claude-sonnet-5", "max_tokens": 1024, "stream": false, "messages": [ { "role": "user", "content": "Summarise this in one line: ..." } ] }
← Click a highlighted part above

The response comes back as JSON. Two fields matter immediately — the text, and what it cost you:

{ "content": [ { "type": "text", "text": "The answer..." } ], "stop_reason": "end_turn", "usage": { "input_tokens": 82, "output_tokens": 14 } }
📝 Note: content is an array of blocks, not a string. Today it might hold one text block; with tools or thinking enabled it holds several of different types. Code that does content[0].text works until the day it doesn't — filter by type instead.

What it actually costs

You're billed per token, separately for what you send and what you get back — and output costs several times more than input. Tokens are chunks of text; roughly one token ≈ 4 characters, so ~750 words is about 1,000 tokens.

The surprise for most people isn't the per-request price, it's the multiplication. Pennies times a thousand users a day is a real number. Try it:

—per request
—per day
—per month

Three levers, in the order worth pulling them. Send less — most prompts carry padding nobody needs. Cache the repeated part — if every request shares the same long instructions, prompt caching makes that prefix far cheaper on repeat. Then consider a smaller model, measuring quality rather than assuming.

Why streaming matters

A model generates one token at a time. You can either wait for all of them and show the finished answer, or forward each one as it arrives. The total time is identical. The felt time is not.

Run both and watch the timers:

Buffered—
idle
Streaming—
idle

Same model, same answer, same finish time. One of them felt broken for two seconds. That gap is why nearly every chat interface streams — and it's a one-word change:

"stream": true

The response arrives as a sequence of server-sent events rather than one JSON body, so your code reads it as a stream and appends each fragment as it lands. There's a second, unglamorous reason to stream: long responses on a non-streaming request can hit HTTP timeouts. Streaming avoids that entirely.

Your first feature

Put the pieces together. A server route that takes text and returns a one-line summary — the smallest complete AI feature, and the shape of almost every one you'll build after it:

// server side — the key never leaves this file export async function POST(request) { const { text } = await request.json(); const res = await fetch('https://api.anthropic.com/v1/messages', { method: 'POST', headers: { 'content-type': 'application/json', 'x-api-key': process.env.ANTHROPIC_API_KEY, 'anthropic-version': '2023-06-01' }, body: JSON.stringify({ model: 'claude-sonnet-5', max_tokens: 100, messages: [{ role: 'user', content: `Summarise in one sentence. No preamble.\n\n${text}` }] }) }); const data = await res.json(); const summary = data.content .filter(b => b.type === 'text') .map(b => b.text) .join(''); return Response.json({ summary }); }

That's the whole thing. Note what it does not do: no key in the browser, no content[0], and a max_tokens low enough that a runaway response can't quietly cost you.

💡 Tip: In production, use your provider's official SDK rather than raw fetch — it handles retries, timeouts, streaming and typed errors for you. Raw HTTP is shown here because it makes the shape of the request visible; the SDK is the same call with the sharp edges filed off.

What you can build now

You can now put a model inside anything that can make an HTTP request — a cron job, a webhook, a form handler, a CLI. That's a genuinely different position than picking between other people's products.

What you can't do yet is give the model access to your information. It knows what you paste into it and nothing else. That's the next problem.

The whole lesson, in five lines
  1. The key is a credit card. Server-side only, never in the browser, never in git. Set a spend limit today.
  2. One POST, four fields. Model, max_tokens, messages, and your key in a header.
  3. Read the response properly. content is an array of typed blocks.
  4. Cost multiplies. Send less, cache the repeated prefix, then consider a smaller model.
  5. Stream anything a human waits for. Same total time, completely different experience.

Up next

RAG & Knowledge Bases →

Give the model access to your own documents — embeddings, chunking, and retrieval.

Start lesson →