Calling AI APIs from Python and Node.js
To build with AI, your program sends a request to a model over the internet and reads the reply. This is called calling an AI API.
The Chat Completions Shape
Many providers offer an OpenAI-compatible chat completions API. The request and reply have the same shape everywhere, so the same code works with different providers. You only change three settings:
| Setting | Example | Where it comes from |
|---|---|---|
AI_BASE_URL | https://api.example-provider.com/v1 | Provider's docs |
AI_API_KEY | a long secret string | Provider's dashboard |
AI_MODEL | a model name | Provider's docs |
You send a list of messages. Each message has a role:
system: your instructions for how the model should behaveuser: what the person typedassistant: earlier replies from the model (for chat history)
If you have not set up your .env file yet, see How to Use This Book.
Calling from Python
Install the official openai package, which works with any OpenAI-compatible provider, and python-dotenv to load the .env file:
Example
pip install openai python-dotenvExample
# ask.py
import os
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv() # reads AI_API_KEY, AI_BASE_URL and AI_MODEL from .env
client = OpenAI(
base_url=os.environ["AI_BASE_URL"],
api_key=os.environ["AI_API_KEY"],
)
response = client.chat.completions.create(
model=os.environ["AI_MODEL"],
messages=[
{"role": "system", "content": "You are a patient computer teacher. Answer in 3 short sentences."},
{"role": "user", "content": "What is RAM? Explain it for a Class 9 student."},
],
temperature=0.3,
max_tokens=200,
)
print(response.choices[0].message.content)
print("Tokens used:", response.usage.total_tokens)Run it with python ask.py. It prints the model's answer and the number of tokens the request used. The answer text will be different every time, and different for each model.
Some providers and newer models use max_completion_tokens instead of max_tokens. If you get an error about this setting, check your provider's documentation.
Calling from Node.js
Node.js 20 and newer have fetch built in and can load a .env file with a flag, so you need no packages:
Example
// ask.mjs - run with: node --env-file=.env ask.mjs
const response = await fetch(`${process.env.AI_BASE_URL}/chat/completions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.AI_API_KEY}`,
},
body: JSON.stringify({
model: process.env.AI_MODEL,
messages: [
{ role: "system", content: "You are a patient computer teacher. Answer in 3 short sentences." },
{ role: "user", content: "What is RAM? Explain it for a Class 9 student." },
],
temperature: 0.3,
max_tokens: 200,
}),
signal: AbortSignal.timeout(30_000), // give up after 30 seconds
});
if (!response.ok) {
throw new Error(`AI API error ${response.status}: ${await response.text()}`);
}
const data = await response.json();
console.log(data.choices[0].message.content);
console.log("Tokens used:", data.usage.total_tokens);The file ends in .mjs so we can use await at the top level. To turn this into a web endpoint for a website chatbot, follow Call an AI API from a Server in the Chatbots Tutorial. To show the answer word by word as it is written, see Streaming Replies.
The Settings You Will Use Most
| Setting | What it does | Typical value |
|---|---|---|
model | Which model answers | From your provider |
messages | The conversation | System + user messages |
temperature | Randomness: low = focused, high = creative | 0.2 for facts, 0.8 for ideas |
max_tokens | Maximum length of the answer | 100 to 1000 |
stream | Send the answer in small parts | true for chat apps |
Handling Errors and Retries
Real APIs sometimes fail. A 429 error means "too many requests" or "out of credit". A 5xx error means a problem on the provider's side. For these, wait and try again, waiting a bit longer each time. Here is the idea with a fake API call, so it runs offline:
Example
import time
def with_retries(call, attempts=3, wait=1.0):
for n in range(1, attempts + 1):
try:
return call()
except Exception as e:
print(f"Attempt {n} failed: {e}")
if n == attempts:
raise
time.sleep(wait)
wait *= 2 # wait longer each time (1s, 2s, 4s...)
# A fake API call that fails twice with "429 Too Many Requests", then works
tries = {"count": 0}
def fake_call():
tries["count"] += 1
if tries["count"] < 3:
raise RuntimeError("429 Too Many Requests")
return "RAM is the computer's short-term memory."
print(with_retries(fake_call, wait=0.1))Attempt 1 failed: 429 Too Many Requests Attempt 2 failed: 429 Too Many Requests RAM is the computer's short-term memory.
Do not retry a 401 (wrong key) or 400 (bad request) error. Fix the cause instead. The official SDKs already retry some errors for you; the OpenAI client has a max_retries option.
Safety Rules for API Keys
- Call the API from your server or script, never from browser JavaScript
- Keep the key in environment variables, and
.envin.gitignore - Set a monthly spending limit in your provider's dashboard
- Use a separate key for each project, so you can cancel one without breaking the others
Exercise
Exercise
In a chat completions request, which role holds your instructions for how the model should behave?
