chatbot in PHP with OpenAI API:Building a chatbot used to mean months of training data and machine-learning pipelines. Today, you can wire up a working conversational bot in a single PHP file — if you know where to look.
In this tutorial, you will build a real chatbot in PHP that connects to the OpenAI API. By the end you will have a browser-based chat interface, a PHP backend that talks to GPT, and session-based conversation memory — all from scratch, with no JavaScript frameworks and no Composer dependencies you do not actually need.
This guide is aimed at developers who know basic PHP and want a practical, production-ready starting point — not a toy demo.

Let’s get into it.
Table of Contents
- What You Will Build
- Prerequisites
- Step 1 — Get Your OpenAI API Key
- Step 2 — Set Up the Project Structure
- Step 3 — Create the Config File
- Step 4 — Build the PHP API Handler
- Step 5 — Add Session-Based Conversation Memory
- Step 6 — Build the Chat UI (HTML + CSS + Vanilla JS)
- Step 7 — Connect the Frontend to the Backend
- Step 8 — Test Your Chatbot
- Step 9 — Customise the System Prompt
- Step 10 — Security Tips Before Going Live
What You Will Build
Here is a quick overview of the finished project:
- A single-page chat interface that looks clean in any modern browser
- A PHP backend (chat.php) that receives user messages and forwards them to the OpenAI /v1/chat/completions endpoint
- Session memory so the bot remembers what was said earlier in the conversation
- A config.php file to store your API key safely (outside the web root if you prefer)
- Basic rate-limiting and input sanitisation to make it safer for real use
The stack is deliberately minimal: PHP 8+, cURL, and plain HTML/CSS/JS. No Laravel, no Composer, no Node. If you can upload files to a shared hosting server, you can deploy this today.
Prerequisites for chatbot in PHP with OpenAI API
Before you start, make sure you have the following:
- PHP 8.0 or higher — check with php -v in your terminal
- cURL extension enabled — most hosting providers have this on by default
- An OpenAI account with API access (free tier works for testing)
- A local development server — XAMPP, Laragon, or the built-in PHP server (php -S localhost:8000) all work fine
- A text editor such as VS Code
You do not need Composer, a database, or any PHP framework.
Step 1 — Get Your OpenAI API Key
If you already have a key, skip ahead. If not:
- Go to platform.openai.com and create a free account.
- After logging in, click your profile icon → API keys.
- Click Create new secret key. Give it a name like php-chatbot.
- Copy the key immediately — you cannot view it again after closing the dialog.
Cost note: GPT-3.5-turbo costs roughly $0.002 per 1K tokens. A typical short chat message is about 50–150 tokens. For testing, your bill will be negligible — but always set a usage limit in your OpenAI dashboard to avoid surprises.
Step 2 — Set Up the Project Structure-chatbot in PHP with OpenAI API
Create a folder called php-chatbot anywhere on your local machine or server. Inside it, create the following files:
php-chatbot/
├── index.html ← The chat interface
├── chat.php ← The PHP backend / API handler
└── config.php ← API key and settings (keep private)
That is the entire project. Three files.
Step 3 — Create the Config File
Open config.php and add the following:
<?php
// config.php
// Your OpenAI API key — never expose this in client-side code
define(‘OPENAI_API_KEY’, ‘sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx’);
// The model to use
define(‘OPENAI_MODEL’, ‘gpt-3.5-turbo’);
// Maximum tokens the model can return per response
define(‘MAX_TOKENS’, 500);
// System prompt — this shapes the chatbot’s personality and role
define(‘SYSTEM_PROMPT’, ‘You are a helpful assistant. Answer clearly and concisely.’);
Replace sk-xxx… with your actual key. Add config.php to your .gitignore if you use Git, so the key never ends up in a repository.
Step 4 — Build the PHP API Handler
This is the core of the project. Open chat.php and paste in the following code. Read through the comments — they explain each section.
<?php
// chat.php
session_start();
require_once ‘config.php’;
// Handle conversation reset
if (isset($_GET[‘action’]) && $_GET[‘action’] === ‘clear’) {
$_SESSION[‘messages’] = [
[‘role’ => ‘system’, ‘content’ => SYSTEM_PROMPT]
];
exit(json_encode([‘status’ => ‘cleared’]));
}
// 1. Only accept POST requests
if ($_SERVER[‘REQUEST_METHOD’] !== ‘POST’) {
http_response_code(405);
exit(json_encode([‘error’ => ‘Method not allowed’]));
}
// 2. Read and sanitise the incoming message
$input = json_decode(file_get_contents(‘php://input’), true);
$userMsg = isset($input[‘message’]) ? trim(strip_tags($input[‘message’])) : ”;
if ($userMsg === ”) {
http_response_code(400);
exit(json_encode([‘error’ => ‘Message cannot be empty’]));
}
if (mb_strlen($userMsg) > 1000) {
http_response_code(400);
exit(json_encode([‘error’ => ‘Message too long (max 1000 characters)’]));
}
// 3. Rate limiting (20 requests per user per hour)
$rateKey = ‘rate_’ . date(‘YmdH’);
$_SESSION[$rateKey] = ($_SESSION[$rateKey] ?? 0) + 1;
if ($_SESSION[$rateKey] > 20) {
http_response_code(429);
exit(json_encode([‘error’ => ‘Too many requests. Please wait a while.’]));
}
// 4. Build the conversation history from the session
if (!isset($_SESSION[‘messages’])) {
$_SESSION[‘messages’] = [
[‘role’ => ‘system’, ‘content’ => SYSTEM_PROMPT]
];
}
$_SESSION[‘messages’][] = [‘role’ => ‘user’, ‘content’ => $userMsg];
// Trim to last 20 messages (keep system prompt at index 0)
if (count($_SESSION[‘messages’]) > 21) {
$systemPrompt = array_shift($_SESSION[‘messages’]);
$_SESSION[‘messages’] = array_slice($_SESSION[‘messages’], -20);
array_unshift($_SESSION[‘messages’], $systemPrompt);
}
// 5. Build the request payload
$payload = [
‘model’ => OPENAI_MODEL,
‘messages’ => $_SESSION[‘messages’],
‘max_tokens’ => MAX_TOKENS,
‘temperature’ => 0.7,
];
// 6. Send the request to OpenAI via cURL
$ch = curl_init(‘https://api.openai.com/v1/chat/completions’);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
‘Content-Type: application/json’,
‘Authorization: Bearer ‘ . OPENAI_API_KEY,
],
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_TIMEOUT => 30,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
if ($curlError) {
http_response_code(500);
exit(json_encode([‘error’ => ‘Network error: ‘ . $curlError]));
}
$data = json_decode($response, true);
if ($httpCode !== 200 || !isset($data[‘choices’][0][‘message’][‘content’])) {
$errorMsg = $data[‘error’][‘message’] ?? ‘Unknown API error’;
http_response_code(500);
exit(json_encode([‘error’ => $errorMsg]));
}
$botReply = trim($data[‘choices’][0][‘message’][‘content’]);
$_SESSION[‘messages’][] = [‘role’ => ‘assistant’, ‘content’ => $botReply];
header(‘Content-Type: application/json’);
echo json_encode([‘reply’ => $botReply]);
Step 5 — Add Session-Based Conversation Memory
You already added session memory inside chat.php, but it is worth understanding why this approach works.
The OpenAI Chat API is stateless — it has no memory between API calls. Every request is independent. The way you give a chatbot “memory” is by sending the entire conversation history (as an array of role/content pairs) on every call.
PHP sessions store that array server-side between HTTP requests. So the flow on every message looks like this:
- User sends a message
- PHP loads the history from $_SESSION[‘messages’]
- Appends the new user message
- Sends the full array to OpenAI
- Appends the bot’s reply to the array
- Saves the updated array back to the session
The result is a bot that remembers everything said in the current browser session. When the session expires (or the user clears cookies), the conversation resets.
If you want persistent memory across sessions — e.g. a logged-in user who returns days later — you would store the messages array in a database instead of a PHP session. That is a natural next step once this working version is live.
Step 6 — Build the Chat UI (HTML + CSS + Vanilla JS)
Open index.html and paste in the following. It is a self-contained page — the CSS and JavaScript are embedded, so you do not need any extra files.
<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”UTF-8″>
<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
<title>PHP Chatbot</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, sans-serif; background: #f0f2f5;
display: flex; justify-content: center; align-items: center;
min-height: 100vh; }
.chat-wrapper { width: 100%; max-width: 680px; background: #fff;
border-radius: 16px; box-shadow: 0 8px 32px rgba(0,0,0,.10);
display: flex; flex-direction: column; height: 90vh; overflow: hidden; }
.chat-header { background: #10a37f; color: #fff; padding: 18px 24px;
font-size: 1.1rem; font-weight: 600; display: flex;
align-items: center; gap: 10px; }
.chat-header span.dot { width:10px; height:10px; background:#6effc8;
border-radius:50%; display:inline-block; }
.chat-messages { flex:1; overflow-y:auto; padding:24px 20px;
display:flex; flex-direction:column; gap:14px; }
.message { max-width:78%; padding:12px 16px; border-radius:14px;
font-size:.95rem; line-height:1.55; white-space:pre-wrap; }
.message.user { background:#10a37f; color:#fff; align-self:flex-end;
border-bottom-right-radius:4px; }
.message.bot { background:#f0f4f8; color:#1a1a2e; align-self:flex-start;
border-bottom-left-radius:4px; }
.message.error { background:#fff0f0; color:#c0392b; align-self:flex-start; }
.typing-indicator { display:flex; gap:5px; padding:14px 16px;
background:#f0f4f8; border-radius:14px;
border-bottom-left-radius:4px; align-self:flex-start; }
.typing-indicator span { width:8px; height:8px; background:#aab;
border-radius:50%; animation:bounce 1.2s infinite; }
.typing-indicator span:nth-child(2) { animation-delay:.2s; }
.typing-indicator span:nth-child(3) { animation-delay:.4s; }
@keyframes bounce { 0%,60%,100%{transform:translateY(0)}
30%{transform:translateY(-6px)} }
.chat-input-area { display:flex; padding:16px 20px; gap:10px;
border-top:1px solid #eee; }
.chat-input-area textarea { flex:1; resize:none; border:1.5px solid #d1d5db;
border-radius:10px; padding:10px 14px;
font-size:.95rem; font-family:inherit; max-height:120px; }
.chat-input-area textarea:focus { border-color:#10a37f; outline:none; }
.send-btn { background:#10a37f; color:#fff; border:none; border-radius:10px;
padding:0 20px; font-size:1rem; cursor:pointer; }
.send-btn:hover { background:#0d8c6c; }
.send-btn:disabled { background:#a0d6c8; cursor:not-allowed; }
.clear-btn { background:none; border:1.5px solid #e0e0e0; border-radius:10px;
padding:0 14px; font-size:.85rem; color:#888; cursor:pointer; }
.clear-btn:hover { border-color:#c0392b; color:#c0392b; }
</style>
</head>
<body>
<div class=”chat-wrapper”>
<div class=”chat-header”>
<span class=”dot”></span> PHP Chatbot · OpenAI
</div>
<div class=”chat-messages” id=”chatMessages”>
<div class=”message bot”>Hello! How can I help you today?</div>
</div>
<div class=”chat-input-area”>
<textarea id=”userInput” rows=”1″ placeholder=”Type a message…”></textarea>
<button class=”clear-btn” id=”clearBtn”>Clear</button>
<button class=”send-btn” id=”sendBtn”>Send ➤</button>
</div>
</div>
<script>
const msgEl = document.getElementById(‘chatMessages’);
const inputEl = document.getElementById(‘userInput’);
const sendBtn = document.getElementById(‘sendBtn’);
const clearBtn= document.getElementById(‘clearBtn’);
inputEl.addEventListener(‘input’, () => {
inputEl.style.height = ‘auto’;
inputEl.style.height = inputEl.scrollHeight + ‘px’;
});
inputEl.addEventListener(‘keydown’, e => {
if (e.key===’Enter’ && !e.shiftKey) { e.preventDefault(); sendMessage(); }
});
sendBtn.addEventListener(‘click’, sendMessage);
clearBtn.addEventListener(‘click’, clearConversation);
function appendMessage(text, role) {
const div = document.createElement(‘div’);
div.className = ‘message ‘ + role;
div.textContent = text;
msgEl.appendChild(div);
msgEl.scrollTop = msgEl.scrollHeight;
}
function showTyping() {
const d = document.createElement(‘div’);
d.className=’typing-indicator’; d.id=’typing’;
d.innerHTML='<span></span><span></span><span></span>’;
msgEl.appendChild(d); msgEl.scrollTop = msgEl.scrollHeight;
}
function removeTyping() {
const el = document.getElementById(‘typing’);
if (el) el.remove();
}
async function sendMessage() {
const text = inputEl.value.trim();
if (!text) return;
appendMessage(text, ‘user’);
inputEl.value=”; inputEl.style.height=’auto’;
sendBtn.disabled=true; showTyping();
try {
const res = await fetch(‘chat.php’, {
method:’POST’,
headers:{‘Content-Type’:’application/json’},
body: JSON.stringify({ message: text })
});
const data = await res.json();
removeTyping();
appendMessage(data.error ? ‘Error: ‘+data.error : data.reply,
data.error ? ‘error’ : ‘bot’);
} catch(e) {
removeTyping();
appendMessage(‘Could not reach the server.’, ‘error’);
} finally { sendBtn.disabled=false; inputEl.focus(); }
}
async function clearConversation() {
if (!confirm(‘Clear the conversation?’)) return;
await fetch(‘chat.php?action=clear’, { method:’POST’ });
msgEl.innerHTML='<div class=”message bot”>Conversation cleared. How can I help?</div>’;
}
</script>
</body></html>
Step 7 — Connect the Frontend to the Backend
Good news: the clear-conversation handler is already inside chat.php — you added it at the very top of the file in Step 4. When the Clear button sends a POST request to chat.php?action=clear, PHP detects the query string, resets the session messages array back to just the system prompt, and returns a JSON confirmation.
At this point all three files are complete and wired together. You are ready to test.
Step 8 — Test Your Chatbot
Start your local PHP server from inside the project folder:
php -S localhost:8000
Open http://localhost:8000 in your browser. You should see the chat interface. Type anything and press Enter.
If it works, you will see a reply from the bot within a second or two. Try a multi-turn test to confirm memory is working:
- Type: “My name is Alex.”
- Type: “What is my name?”
The bot should reply with Alex. If it does, session memory is working correctly.
Step 9 — Customise the System Prompt
The system prompt is the most powerful tool you have for shaping the bot’s behaviour. Open config.php and change the SYSTEM_PROMPT constant.
Customer support bot:
define(‘SYSTEM_PROMPT’, ‘You are a friendly support agent for Acme SaaS.
Help users with account issues, billing questions, and feature guidance.
If you do not know the answer, say so and direct users to support@acme.com.
Keep replies concise and professional.’);
Coding assistant:
define(‘SYSTEM_PROMPT’, ‘You are an expert PHP developer.
When asked to write code, always use PHP 8+ syntax, add inline comments,
and point out any security issues. Format code in Markdown code blocks.’);
Scoped FAQ bot:
define(‘SYSTEM_PROMPT’, ‘You only answer questions about our return policy,
shipping times, and product specifications.
For anything else, say: I can only help with product and order queries.’);
Step 10 — Security Tips Before Going Live
1. Move config.php above the web root
On most shared hosts, your web root is public_html/. Move config.php to the folder above it and update the path in chat.php:
require_once ‘../config.php’;
2. Rate limiting
The rate limiting code is already in chat.php from Step 4. It allows 20 requests per user per hour using the PHP session. Adjust the limit to suit your traffic.
3. Validate the Content-Type header
if ($_SERVER[‘CONTENT_TYPE’] !== ‘application/json’) {
http_response_code(415);
exit(json_encode([‘error’ => ‘Unsupported Media Type’]));
}
4. Set a spending limit in OpenAI
Go to OpenAI Platform → Settings → Billing → Usage limits and set a hard monthly cap. Even $5 is plenty for testing and eliminates the risk of an unexpected large bill.
5. Never log full conversation content to plain text files
If you add logging for debugging, use a database and hash or redact anything sensitive. Raw conversation logs stored in .txt files in your web root are a common and easily avoidable security risk.
Common Errors and How to Fix Them
“You exceeded your current quota”
Your OpenAI account has no credit. Go to platform.openai.com → Billing and add a payment method. Free-tier limits are easy to hit during testing.
“cURL error 60: SSL certificate problem”
This usually happens on Windows with XAMPP. Download the cacert.pem file from curl.se and point to it in your php.ini:
curl.cainfo = “C:/xampp/php/extras/ssl/cacert.pem”
The chatbot replies but forgets previous messages
PHP sessions require cookies to work. Confirm that session_start() is the very first line of chat.php — before any output.
“SyntaxError: Unexpected token” in the browser console
PHP is likely outputting an error before the JSON. Add ob_start() at the very top of chat.php and ob_clean() just before the echo json_encode() call. This buffers any accidental output.
The API call times out
Increase the cURL timeout: CURLOPT_TIMEOUT => 60. OpenAI occasionally has slow response times during peak hours. GPT-3.5-turbo is generally faster than GPT-4 for latency-sensitive applications.
Next Steps and Ideas
- Streaming responses — use OpenAI’s stream: true option and PHP’s fopen() with chunked transfer encoding to display replies word-by-word, like ChatGPT itself.
- Persistent chat history — store the messages array in a MySQL or SQLite database linked to a user ID, so conversations persist across sessions and devices.
- File uploads — accept uploaded PDFs or text files, extract the content in PHP, and inject it as context into the messages array. This turns the bot into a simple document Q&A tool.
- Function calling — OpenAI’s function calling feature lets the model trigger PHP functions based on what the user asks. This is how real production bots are built.
- Switch to GPT-4o — just change OPENAI_MODEL in config.php to gpt-4o. The rest of the code stays identical.
- Embed in WordPress — wrap the chatbot in a WordPress shortcode or Elementor HTML widget. Drop the body content of index.html into a Custom HTML block and host chat.php in your theme or a custom plugin.
Frequently Asked Questions
Can I use this chatbot on shared hosting?
Yes. As long as your host runs PHP 8+ and has the cURL extension enabled (almost all do), this setup works without any special server configuration. You just upload the three files.
What is the difference between GPT-3.5-turbo and GPT-4 for a chatbot?
GPT-3.5-turbo is faster and cheaper, making it suitable for most customer-facing chatbots where response speed matters. GPT-4 (and GPT-4o) are significantly more capable at reasoning, following complex instructions, and handling edge cases — but cost more per token and respond slightly slower.
Is the OpenAI API free?
New accounts get a small amount of free credit. After that, you pay per token used. GPT-3.5-turbo is inexpensive enough that a low-traffic chatbot will typically cost under a dollar per month. Always set a spending limit in your OpenAI dashboard.
How do I add this chatbot to a WordPress site?
The simplest method is to upload chat.php and config.php to your server, then embed the HTML and JavaScript from index.html into a page using a Custom HTML block or a plugin like WPCode. Alternatively, you can turn it into a proper WordPress plugin with a shortcode.
How do I make the chatbot forget previous messages?
The Clear button in the UI sends a request to chat.php?action=clear, which resets $_SESSION[‘messages’] back to just the system prompt.
Can I use a different AI model, like Claude or Gemini?
Yes. The PHP structure (send a POST request, parse a JSON response) is the same for most AI APIs. You would change the endpoint URL, authentication header format, and response parsing to match the provider’s documentation. The session memory logic stays identical.
Wrapping Up
You have just built a working PHP chatbot connected to the OpenAI API — with a clean UI, session memory, input validation, and a clear path to production.
The three-file structure keeps things easy to understand and easy to extend. From here you can add a database, switch to a more powerful model, or turn the whole thing into a WordPress plugin. The foundation is solid.
If you run into issues, drop a comment below. And if this tutorial saved you time, sharing it helps keep content like this free.
Happy building.
EDITOR NOTES — Internal Linking Suggestions
• “PHP sessions” → link to any existing PHP sessions tutorial on Technolila
• “OpenAI function calling” → link to your function calling tutorial (if exists)
• “WordPress shortcode” → link to your WP shortcode guide (if exists)
• “GPT-4o” → link to your GPT-4o overview article (if exists)
• “XAMPP” / “Laragon” → link to your local dev environment setup guides
