What is an AI chatbot?
Understand the core concept before writing a single line of code.
Plain English
An AI chatbot is a program that takes text input, sends it to an AI model over the internet, and shows the model's text response back to the user. That's it.
The AI model lives on a server (Google's, in our case). Your code just asks it questions and displays answers. You don't need to train anything — the hard work is already done.
3 Real-World Examples
Customer Support Bot
Answers FAQs 24/7, escalates to humans only when needed. Reduces support ticket volume by ~60%.
Coding Assistant
Explains code, suggests fixes, generates boilerplate. Like having a senior dev always available.
Language Tutor
Practices conversation, corrects grammar, adapts to your level. Available in 40+ languages.
Why Gemini API?
| Feature | Gemini API | OpenAI | Claude |
|---|---|---|---|
| Free tier | ✓ Generous | ✗ Paid only | ✗ Paid only |
| Speed (Flash) | ✓ Very fast | ~ Medium | ~ Medium |
| Context window | ✓ 1M tokens | ✓ 128k | ✓ 200k |
| Multimodal | ✓ Native | ✓ Yes | ✓ Yes |
| REST API | ✓ Simple | ✓ Simple | ✓ Simple |
Get your API key
Your API key is the password that lets your code talk to Gemini.
How to get your key
- Go to aistudio.google.com/app/apikey — sign in with your Google account.
- Click "Create API key" → "Create API key in new project".
- Copy the key that appears (starts with
AIza). Store it somewhere safe — it won't show again. - Come back here and paste it in the field below (or the one in the top bar).
⚠ Keep your key private
Never put your API key directly in code you share publicly or push to GitHub. For this guide we store it in localStorage (your browser only — never sent anywhere else). For production apps, use a backend server or environment variables.
Live API Key Tester
Your first API call
Send a message to Gemini from your browser with plain JavaScript.
How fetch() + REST APIs work
fetch() is a built-in browser function that sends HTTP requests. The Gemini API is a REST API — it accepts HTTP POST requests with JSON bodies and returns JSON responses.
You send: { contents: [{ parts: [{ text: "your message" }] }] }
You get back: { candidates: [{ content: { parts: [{ text: "the reply" }] } }] }
Common errors explained
400 Bad Request — your JSON body is malformed. Check the structure matches exactly.
401 / 403 — API key is invalid or missing. Paste your key in the top bar first.
429 Too Many Requests — you've hit the rate limit. Wait a minute and try again.
undefined response — the path to the text is wrong. Log data to check the shape.
Add conversation history
Make the chatbot remember what was said earlier in the conversation.
The messages array pattern
Gemini (like all LLMs) is stateless — each request is independent. To give it memory, you send the entire conversation history with every request.
Each message has a role ("user" or "model") and parts containing the text. You append each new message to the array before sending.
// Maintain history across turns
const history = [];
async function chat(userMessage) {
// Add user's message
history.push({
role: 'user',
parts: [{ text: userMessage }]
});
const response = await fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ contents: history }) // Send ALL history
});
const data = await response.json();
const reply = data.candidates[0].content.parts[0].text;
// Add bot's reply to history
history.push({
role: 'model',
parts: [{ text: reply }]
});
return reply;
}
Try it — Live chat demo
System prompts & personas
Give your chatbot a personality, rules, and a purpose.
What is a system prompt?
A system prompt is a hidden instruction you give the model before the conversation starts. It sets the persona, tone, task, and constraints. Users don't see it, but it shapes every response.
With Gemini, you add a system_instruction field alongside contents:
const response = await fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
system_instruction: {
parts: [{ text: "You are a friendly pirate. Speak in pirate slang and say 'Arrr!' often." }]
},
contents: history
})
});
Try example system prompts:
Build the UI
A complete, copy-ready HTML chat interface with CSS included.
Structure overview
A chat UI needs 3 parts: a message display area, an input row, and a send button. The JS fetches from Gemini and appends message bubbles to the display area.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My AI Chatbot</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: sans-serif; background: #0f0f0f; color: #e0e0e0; height: 100vh; display: flex; flex-direction: column; }
#header { background: #1a1a1a; padding: 16px 20px; font-weight: 700; border-bottom: 1px solid #2a2a2a; }
#messages { flex: 1; overflow-y: auto; padding: 20px; display: flex; flex-direction: column; gap: 12px; }
.msg { max-width: 75%; padding: 10px 16px; border-radius: 12px; line-height: 1.5; }
.user { background: rgba(184,200,232,0.15); align-self: flex-end; }
.bot { background: #1a1a1a; border: 1px solid #2a2a2a; align-self: flex-start; }
#input-row { display: flex; gap: 10px; padding: 16px; background: #1a1a1a; border-top: 1px solid #2a2a2a; }
#user-input { flex: 1; background: #0f0f0f; border: 1px solid #2a2a2a; border-radius: 8px; color: #e0e0e0; padding: 10px 14px; font-size: 1rem; outline: none; }
#send-btn { background: #b8c8e8; color: #0f0f0f; border: none; border-radius: 8px; padding: 10px 20px; font-weight: 700; cursor: pointer; }
</style>
</head>
<body>
<div id="header">My AI Chatbot</div>
<div id="messages"></div>
<div id="input-row">
<input id="user-input" type="text" placeholder="Type a message..." />
<button id="send-btn" onclick="send()">Send</button>
</div>
<script>
const API_KEY = 'YOUR_API_KEY'; // ← replace this
const API_URL = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${API_KEY}`;
const history = [];
document.getElementById('user-input').addEventListener('keydown', e => { if (e.key === 'Enter') send(); });
function addMsg(text, role) {
const div = document.createElement('div');
div.className = `msg ${role}`;
div.textContent = text;
document.getElementById('messages').appendChild(div);
div.scrollIntoView({ behavior: 'smooth' });
}
async function send() {
const input = document.getElementById('user-input');
const text = input.value.trim();
if (!text) return;
input.value = '';
addMsg(text, 'user');
history.push({ role: 'user', parts: [{ text }] });
const res = await fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ contents: history })
});
const data = await res.json();
const reply = data.candidates?.[0]?.content?.parts?.[0]?.text ?? 'Error getting response';
history.push({ role: 'model', parts: [{ text: reply }] });
addMsg(reply, 'bot');
}
</script>
</body>
</html>
Add features
Upgrade your basic chat UI with these practical enhancements.
Typing Indicator
Show an animated "..." while waiting for the API response — prevents users from thinking the app froze.
Show code snippet
// Show before fetch
function showTyping() {
const div = document.createElement('div');
div.className = 'msg bot typing';
div.id = 'typing-indicator';
div.textContent = '...';
messages.appendChild(div);
}
// Remove after fetch
document.getElementById('typing-indicator')?.remove();
Copy Button
Add a small copy icon to each bot message so users can copy the response with one click.
Show code snippet
function addBotMsg(text) {
const wrap = document.createElement('div');
wrap.className = 'msg-wrap';
const msg = document.createElement('div');
msg.className = 'msg bot';
msg.textContent = text;
const btn = document.createElement('button');
btn.textContent = '📋';
btn.onclick = () => navigator.clipboard.writeText(text);
wrap.appendChild(msg);
wrap.appendChild(btn);
messages.appendChild(wrap);
}
Clear Chat
Let users start a fresh conversation without reloading the page.
Show code snippet
function clearChat() {
history.length = 0; // Reset history array
document.getElementById('messages').innerHTML = '';
// Optional: add a welcome message back
addMsg('Chat cleared! Start a new conversation.', 'bot');
}
Token Counter
Estimate how much of the context window you've used (helpful for long conversations).
Show code snippet
// Rough estimate: ~4 chars per token
function estimateTokens() {
const allText = history
.map(m => m.parts[0].text)
.join(' ');
return Math.round(allText.length / 4);
}
// Update after each message
document.getElementById('token-count').textContent =
`~${estimateTokens()} tokens used`;
Export Chat
Let users download the conversation as a .txt file.
Show code snippet
function exportChat() {
const text = history.map(m =>
`${m.role === 'user' ? 'You' : 'Bot'}: ${m.parts[0].text}`
).join('\n\n');
const blob = new Blob([text], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'chat-export.txt';
a.click();
URL.revokeObjectURL(url);
}
Deploy to GitHub Pages
Put your chatbot on the internet — free, fast, and permanent.
Step-by-step deploy
- Go to github.com/new and create a new public repository (e.g.
my-ai-chatbot). - On your computer, open a terminal in your project folder and run:
git init && git add . && git commit -m "initial commit" - Push to GitHub:
git remote add origin https://github.com/YOUR_USERNAME/my-ai-chatbot.gitthengit push -u origin main - In your GitHub repo, go to Settings → Pages. Set Source to "Deploy from a branch", Branch to
main, folder to/. Click Save. - Wait ~2 minutes. Your site will be live at
https://YOUR_USERNAME.github.io/my-ai-chatbot/
Pre-launch checklist
- Remove hardcoded API key — use a prompt for users to enter their own key
- Test on mobile (resize browser to 375px width)
- Test with a slow connection (Chrome DevTools → Network → Slow 3G)
- Add a README.md explaining what the chatbot does and how to use it
- Add an error message when the API call fails
Keep building — 100 AI Agents Project
This guide is Agent #28 of the 100 AI Agents challenge — a series of 100 useful AI tools built with HTML and Gemini.
Explore the full collection for inspiration on what to build next: hhanng.github.io