Build a Startup Cheatsheet
AI and ML (OpenAI, RAG, Vector Search)
Use this Build a Startup reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
What You Can Build With AI APIs Today
Modern AI APIs let you add powerful features without training a model. The most useful capabilities for startups:
- Text generation / chat — answer questions, draft content, summarize, classify, extract structured data
- Image generation — create images from text prompts
- Embeddings / semantic search — find content by meaning, not just keywords
- Audio — transcribe speech, generate speech
- Code assistance — review, generate, explain code
The most important distinction: AI APIs (call a hosted model, pay per token) vs. running your own models (self-host, GPU infrastructure required). For 99% of startups, AI APIs are the right choice — you get state-of-the-art models without the operational overhead.
OpenAI
OpenAI offers the most widely-used AI API. Key models (per-1M-token prices shift — verify on the pricing page before budgeting):
| Model | Best for | Speed | Cost (per 1M tokens) |
|---|---|---|---|
| gpt-5.1 | Flagship: complex reasoning, coding, multimodal; adjustable reasoning effort | Medium | $1.25 in / $10 out |
| gpt-5-mini | Fast, cheap; most product tasks | Fast | $0.25 in / $2 out |
| gpt-5-nano | Cheapest; classification, extraction, high volume | Fastest | $0.05 in / $0.40 out |
| text-embedding-3-small | Embeddings for semantic search / RAG | Fast | $0.02 per 1M tokens |
| text-embedding-3-large | Higher-accuracy embeddings | Fast | $0.13 per 1M tokens |
| gpt-image-1 | Image generation | Medium | ~$0.01–$0.17 per image (by quality) |
| whisper-1 / gpt-4o-transcribe | Speech-to-text transcription | Fast | $0.006/minute |
There is no separate "reasoning model" tier anymore — the GPT-5 series has reasoning built in, tunable per request (reasoning: { effort: "low" | "medium" | "high" }). Older models (gpt-4o, gpt-4o-mini, the o-series) still work but are legacy; do not start new projects on them.
Free tier: None — you pay from the first token. Start with a $5–$10 credit purchase.
Basic API Call (Responses API)
The Responses API is OpenAI's primary interface now (Chat Completions remains supported, but new features land on Responses first):
import OpenAI from "openai"; const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); const response = await openai.responses.create({ model: "gpt-5-mini", instructions: "You are a helpful assistant.", input: "Explain closures in JavaScript in one paragraph.", max_output_tokens: 500, }); const answer = response.output_text;
The Chat Completions equivalent still works — note the token cap is max_completion_tokens now (max_tokens is deprecated):
const response = await openai.chat.completions.create({ model: "gpt-5-mini", messages: [ { role: "system", content: "You are a helpful assistant." }, { role: "user", content: "Explain closures in JavaScript in one paragraph." }, ], max_completion_tokens: 500, }); const answer = response.choices[0].message.content;
Streaming Responses
For chat UIs, stream tokens as they are generated — users see output immediately rather than waiting for the full response:
const stream = await openai.responses.create({ model: "gpt-5-mini", input: prompt, stream: true, }); for await (const event of stream) { if (event.type === "response.output_text.delta") { process.stdout.write(event.delta); // or send via SSE to the browser } }
Structured Output
Force the model to return valid JSON matching a schema. The parse helper is stable now — chat.completions.parse, not the old beta.chat.completions.parse (the Responses API has an equivalent responses.parse with zodTextFormat):
import { z } from "zod"; import { zodResponseFormat } from "openai/helpers/zod"; const schema = z.object({ sentiment: z.enum(["positive", "neutral", "negative"]), confidence: z.number().min(0).max(1), summary: z.string(), }); const response = await openai.chat.completions.parse({ model: "gpt-5-mini", messages: [{ role: "user", content: `Analyze: "${reviewText}"` }], response_format: zodResponseFormat(schema, "analysis"), }); const result = response.choices[0].message.parsed; // result is fully typed: { sentiment, confidence, summary }
Other LLM Providers
| Provider | Models | Free tier | Best for |
|---|---|---|---|
| Anthropic | Claude Opus 4.5, Sonnet 4.5, Haiku 4.5 | No | Coding and agentic workflows; long context; nuanced instruction-following |
| Gemini 3 Pro, Gemini 2.5 Flash | Yes (limited) | Multimodal; very long context (1M tokens); competitive pricing | |
| Mistral | Mistral Large / Medium 3, Magistral | Yes (small) | European data residency; open-weight options |
| Meta (Llama) | Llama 4 (Scout, Maverick) | Self-host only | Open-weight; can run locally or on your own GPU |
| Groq | Llama, GPT-OSS, Qwen (inference) | Yes | Extremely fast inference on open-weight models |
| Together AI | Many open-weight models | No | Run open-weight models cheaply |
Model generations turn over every 6–12 months — treat hardcoded model names as config values, and check each provider's docs for the current lineup before launch.
Vercel AI SDK provides a unified interface for all major LLM providers — switch models with one line change:
import { generateText } from "ai"; import { openai } from "@ai-sdk/openai"; import { anthropic } from "@ai-sdk/anthropic"; // Same API, different provider const { text } = await generateText({ model: openai("gpt-5-mini"), // or anthropic("claude-haiku-4-5") prompt: "Write a tagline for a fintech startup.", });
RAG: Retrieval-Augmented Generation
The problem: LLMs have a knowledge cutoff date and no access to your private data. If you ask a model about your user's account history or your internal documentation, it cannot answer accurately — it either guesses or says it does not know.
The solution: RAG retrieves relevant documents from your own data store and includes them in the prompt as context. The LLM then answers based on your data, not its training.
How RAG Works
User query
↓
Generate embedding for the query (text-embedding-3-small)
↓
Search vector database for semantically similar documents
↓
Retrieve top-K results (e.g., top 5 most relevant chunks)
↓
Build a prompt: "[System instructions] + [Retrieved context] + [User query]"
↓
Send to LLM → answer grounded in your dataBuilding a RAG Pipeline
Step 1: Ingest and index your documents
import { OpenAI } from "openai"; const openai = new OpenAI(); async function ingestDocument(content: string, metadata: object) { // Chunk the document (split into ~500 token pieces with overlap) const chunks = splitIntoChunks(content, { size: 500, overlap: 50 }); for (const chunk of chunks) { // Generate embedding const { data } = await openai.embeddings.create({ model: "text-embedding-3-small", input: chunk, }); const embedding = data[0].embedding; // Store in your vector DB (using pgvector here) await db.execute(sql` INSERT INTO documents (content, metadata, embedding) VALUES (${chunk}, ${JSON.stringify(metadata)}, ${embedding}::vector) `); } }
Step 2: Retrieve relevant chunks and generate an answer
async function answerWithRAG(userQuery: string) { // Embed the query const { data } = await openai.embeddings.create({ model: "text-embedding-3-small", input: userQuery, }); const queryEmbedding = data[0].embedding; // Find similar documents const similar = await db.execute(sql` SELECT content, 1 - (embedding <=> ${queryEmbedding}::vector) AS similarity FROM documents ORDER BY embedding <=> ${queryEmbedding}::vector LIMIT 5 `); const context = similar.rows.map((r) => r.content).join("\n\n"); // Generate grounded answer const response = await openai.responses.create({ model: "gpt-5-mini", instructions: `Answer using only the context below. If the answer is not in the context, say so.\n\nContext:\n${context}`, input: userQuery, }); return response.output_text; }
Embeddings and Vector Search
An embedding is a list of ~1,500 floating-point numbers that represents the semantic meaning of text. Texts with similar meaning have similar vectors. This lets you search by meaning rather than keyword.
Use cases: - Semantic search ("find articles about payment issues" matches "my card was declined" without the exact words) - Document similarity / deduplication - Recommendation systems ("users who liked this also liked...") - Clustering content by topic
See the Databases page for vector database options (pgvector, Pinecone, etc.).
Prompt Engineering Basics
Getting the right output from an LLM requires clear prompts. Key techniques:
System prompt — sets the persona, task, and constraints. Write it carefully.
Few-shot examples — include examples of the input/output you want in the prompt. Models match the pattern.
Chain-of-thought — ask the model to think step by step before answering. Dramatically improves accuracy on complex tasks.
Temperature — controls randomness. 0 = deterministic (same output every time); 1 = creative/varied. For factual extraction or structured output, use 0. For creative writing, use 0.7–1.0.
// Good system prompt example const systemPrompt = ` You are a customer support agent for Acme Corp. Your job is to: 1. Classify the user's issue into: billing, technical, feature_request, or other 2. Draft a professional, empathetic reply in under 100 words 3. Suggest one follow-up action Reply in JSON: { "category": "...", "reply": "...", "action": "..." } `.trim();
Rate Limiting and Cost Control
AI API costs can spike unexpectedly. Protect yourself:
- Set
max_output_tokens(Responses API) ormax_completion_tokens(Chat Completions) on every request — prevents runaway generation - Cap prompt length — truncate or summarize long context before sending
- Rate limit users — track usage in your database; deny requests over quota
- Cache responses — identical prompts return identical results; cache with Redis (Upstash)
- Use cheaper models for simple tasks —
gpt-5-minicosts 5x less thangpt-5.1(andgpt-5-nano25x less) and handles most tasks - Turn reasoning effort down — high reasoning effort burns output tokens; use
lowfor simple tasks
// Simple usage tracking await db.insert(aiUsage).values({ userId, model, promptTokens: usage.prompt_tokens, completionTokens: usage.completion_tokens, cost: calculateCost(model, usage), }); // Check before allowing request const monthlyUsage = await db.select({ total: sum(aiUsage.cost) }) .from(aiUsage) .where(and(eq(aiUsage.userId, userId), gte(aiUsage.createdAt, startOfMonth))); if (monthlyUsage[0].total > USER_MONTHLY_LIMIT) { throw new Error("Monthly AI usage limit reached"); }
When to Train Your Own Model
Almost never — especially at an early-stage startup. Fine-tuning or training from scratch requires:
- Large labeled datasets (10,000+ examples for fine-tuning; millions for training from scratch)
- GPU infrastructure (expensive)
- ML engineering expertise
- Months of work
Use the OpenAI / Anthropic API with good prompting instead. Fine-tune only when you have a very specific, repetitive task where prompt engineering consistently fails and you have the data to prove it.
The one exception: using open-source models (Llama, Mistral) self-hosted on your own GPU makes sense when privacy is critical (processing sensitive data that cannot leave your servers) or at very high volume where per-token API costs exceed self-hosting costs.