Your First AI API Call: A Complete Guide for JavaScript Developers

This is the post where you stop reading and start building.
By the end of this guide, you'll have made your first AI API call, seen the response, and understood exactly what's happening under the hood. No theory, no fluff—just working code.
What You'll Build
By the end of this tutorial, you'll have a working script that can:
- Query an AI model and get responses
- Stream responses in real-time (like ChatGPT)
- Handle errors gracefully
Total time: ~15 minutes. Total cost: less than $0.01.
Prerequisites
Before we start, make sure you have:
- Node.js 18+ installed (required for native fetch support)
- npm (comes with Node.js)
- A text editor (VS Code, Cursor, etc.)
That's it. Let's go.
Step 1: Get Your API Key
We'll use OpenAI since it's the most common starting point.
- Go to platform.openai.com
- Create an account or log in
- Add a payment method — Even if you have free credits, the API requires a card on file. Without it, your calls will silently fail.
- Click your profile icon → API Keys
- Click Create new secret key
- Give it a name (e.g., "my-first-app")
- Copy the key immediately — you won't see it again
Your key looks something like: sk-proj-abc123...
Store it somewhere safe. We'll use it in a moment.
Security note: If you ever accidentally expose your API key (e.g., push it to GitHub), regenerate it immediately from the API Keys page. Exposed keys can be used by anyone and you'll be billed for their usage.
Worried about costs? The examples in this tutorial cost roughly $0.001 total. You'd need to run them thousands of times to spend even $1. We debunked the cost myth in detail → 5 AI Myths.
Step 2: Set Up Your Project
Open your terminal and run:
mkdir my-first-ai-app
cd my-first-ai-app
npm init -y
npm install openai dotenv
npm install -D typescript tsx @types/nodeCreate three files:
touch index.ts .env .gitignoreAdd your API key to .env:
OPENAI_API_KEY=sk-proj-your-key-here
Add this to .gitignore (never commit your API key):
.env
node_modules
That's your project set up. A few commands, three files.
Step 3: Your First API Call
Open index.ts and paste this code:
import OpenAI from 'openai'
import 'dotenv/config' // This line loads your .env file into process.env
const openai = new OpenAI() // Automatically reads OPENAI_API_KEY from environment
async function main() {
const response = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
{ role: 'system', content: 'You are a helpful assistant.' },
{ role: 'user', content: 'What is the capital of France?' }
]
})
console.log(response.choices[0].message.content)
}
main()Run it:
npx tsx index.tsExpected output:
The capital of France is Paris.
🎉 You just made your first AI API call.
Let me break down what happened:
import 'dotenv/config'— Loads your.envfile soOPENAI_API_KEYis available. Don't remove this line!new OpenAI()— Creates the client, automatically reading your API key from the environmentmodel: 'gpt-4o-mini'— A fast, cheap model perfect for learning (~$0.15 per million input tokens)messages— The conversation: a system prompt (sets behavior) and a user message (your question)response.choices[0].message.content— The model's actual answer
Note on models: OpenAI frequently updates their model lineup. Check OpenAI's pricing page for current options. If
gpt-4o-miniisn't available,gpt-3.5-turbois an even cheaper alternative.
Step 4: Understanding the Response
The response object contains more than just the answer. Let's see the full structure:
console.log(JSON.stringify(response, null, 2))You'll see something like:
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"model": "gpt-4o-mini-2024-07-18",
"usage": {
"prompt_tokens": 25,
"completion_tokens": 8,
"total_tokens": 33
},
"choices": [
{
"message": {
"role": "assistant",
"content": "The capital of France is Paris."
},
"finish_reason": "stop"
}
]
}Key fields to know:
usage.prompt_tokens— How many tokens your input usedusage.completion_tokens— How many tokens the response usedusage.total_tokens— What you're billed for (33 tokens ≈ $0.000005)finish_reason: "stop"— The model finished naturally (vs. being cut off by max_tokens)
Want to understand tokens better? We covered them in detail → LLM Glossary.
Step 5: Streaming Responses
For chat-like experiences, you don't want to wait for the entire response. You want it to appear word-by-word, like ChatGPT.
When to use streaming:
- User-facing chat interfaces where perceived speed matters
- Long responses where you want to show progress
- Any UI where waiting feels slow
When to use standard calls:
- Background processing where you need the full response before continuing
- Short responses where streaming overhead isn't worth it
- When you need accurate token counts (streaming doesn't return usage stats)
Here's how to stream:
import OpenAI from 'openai'
import 'dotenv/config'
const openai = new OpenAI()
async function main() {
const stream = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
{ role: 'user', content: 'Write a haiku about coding.' }
],
stream: true
})
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content || ''
process.stdout.write(content)
}
console.log() // New line at the end
}
main()What's different:
stream: true— Tells the API to send chunks as they're generateddelta.content— Each chunk contains a small piece of textprocess.stdout.write()— Prints without newlines, so it flows naturally
Watch the haiku appear character by character. This is how you build responsive AI UIs.
Step 6: Handling Errors
Things will go wrong. Here's what to expect and how to fix it:
Common Errors
Invalid API Key:
Error: 401 Incorrect API key provided
→ Check your .env file. Make sure there are no extra spaces. Regenerate your key if needed.
No Payment Method:
Error: 429 You exceeded your current quota
→ Even with free credits, you need a payment method on file. Add one at platform.openai.com/account/billing.
Rate Limited:
Error: 429 Rate limit reached
→ You're making too many requests. Wait a minute and try again. For production apps, implement exponential backoff.
Model Not Found:
Error: 404 The model does not exist
→ Typo in the model name. It's gpt-4o-mini, not gpt4-mini or gpt-4-o-mini.
Error Handling Pattern
Always wrap your API calls:
import OpenAI from 'openai'
import 'dotenv/config'
const openai = new OpenAI()
async function main() {
try {
const response = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
{ role: 'user', content: 'Hello!' }
]
})
console.log(response.choices[0].message.content)
} catch (error) {
if (error instanceof OpenAI.APIError) {
console.error(`API Error: ${error.status} - ${error.message}`)
// Handle specific errors
if (error.status === 401) {
console.error('Check your API key in .env')
} else if (error.status === 429) {
console.error('Rate limited. Wait and retry.')
}
} else {
throw error
}
}
}
main()What's Next?
You've made your first API call. You understand the response. You can stream. You can handle errors.
Now what?
- Experiment with prompts — Change the system message, ask different questions, see how behavior changes
- Build something real — A CLI chatbot, a document summarizer, a code explainer
- Try other models — GPT-4o for complex reasoning, or explore Claude (Anthropic) and Gemini (Google)
- Learn prompt engineering — The skill that turns basic calls into powerful features
Want to go deeper? My AI Integration Engineer course takes you from these basics to building production-ready AI features—the skills that are in high demand right now.
Complete Working Example
Here's everything in one file—copy, paste, and run:
import OpenAI from 'openai'
import 'dotenv/config'
const openai = new OpenAI()
// Basic call
async function basicCall() {
const response = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
{ role: 'system', content: 'You are a helpful assistant.' },
{ role: 'user', content: 'Explain recursion in one sentence.' }
]
})
console.log('Basic:', response.choices[0].message.content)
console.log('Tokens used:', response.usage?.total_tokens)
}
// Streaming call
async function streamingCall() {
console.log('\nStreaming:')
const stream = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
{ role: 'user', content: 'Write a one-line joke about APIs.' }
],
stream: true
})
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content || '')
}
console.log('\n')
}
// Run both
async function main() {
try {
await basicCall()
await streamingCall()
} catch (error) {
if (error instanceof OpenAI.APIError) {
console.error(`API Error: ${error.status} - ${error.message}`)
} else {
throw error
}
}
}
main()You're no longer someone who reads about AI. You're someone who builds with it.
In the next post, we'll compare the major AI providers—OpenAI, Anthropic, and Google—so you know which model to use for what.

Frank Atukunda
Software Engineer documenting my transition to AI Engineering. Building 10x .dev to share what I learn along the way.
Comments (0)
Join the discussion
Sign in with GitHub to leave a comment and connect with other engineers.