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):

ModelBest forSpeedCost (per 1M tokens)
gpt-5.1Flagship: complex reasoning, coding, multimodal; adjustable reasoning effortMedium$1.25 in / $10 out
gpt-5-miniFast, cheap; most product tasksFast$0.25 in / $2 out
gpt-5-nanoCheapest; classification, extraction, high volumeFastest$0.05 in / $0.40 out
text-embedding-3-smallEmbeddings for semantic search / RAGFast$0.02 per 1M tokens
text-embedding-3-largeHigher-accuracy embeddingsFast$0.13 per 1M tokens
gpt-image-1Image generationMedium~$0.01–$0.17 per image (by quality)
whisper-1 / gpt-4o-transcribeSpeech-to-text transcriptionFast$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

ProviderModelsFree tierBest for
AnthropicClaude Opus 4.5, Sonnet 4.5, Haiku 4.5NoCoding and agentic workflows; long context; nuanced instruction-following
GoogleGemini 3 Pro, Gemini 2.5 FlashYes (limited)Multimodal; very long context (1M tokens); competitive pricing
MistralMistral Large / Medium 3, MagistralYes (small)European data residency; open-weight options
Meta (Llama)Llama 4 (Scout, Maverick)Self-host onlyOpen-weight; can run locally or on your own GPU
GroqLlama, GPT-OSS, Qwen (inference)YesExtremely fast inference on open-weight models
Together AIMany open-weight modelsNoRun 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 data

Building 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;
}

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) or max_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 tasksgpt-5-mini costs 5x less than gpt-5.1 (and gpt-5-nano 25x less) and handles most tasks
  • Turn reasoning effort down — high reasoning effort burns output tokens; use low for 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.