Table of Contents
Building a PHP OpenAI API chatbot is simpler than most developers expect. No complex ML setup, no Python environment – just cURL requests to an API endpoint that returns AI-generated responses. If you can make a cURL request in PHP, you can build a working AI chatbot.
This guide covers the complete implementation – API authentication, sending messages, handling conversation history so the AI remembers context, error handling for rate limits and failures, and building a simple web chat interface. Every code block runs against the live OpenAI API.
What You Need
- PHP 7.4 or higher with cURL enabled
- An OpenAI account and API key – sign up at platform.openai.com
- Basic PHP knowledge
OpenAI API usage costs money based on tokens processed. The gpt-4.1-mini model used in this guide is significantly cheaper than GPT-4 and more than capable for chatbot applications. Check current pricing at openai.com/pricing before building anything at scale.
Storing Your API Key Securely
Never hardcode your API key directly in a PHP file that could end up in version control or a public directory. Store it in a config file outside your web root:
<?php
// config.php - store OUTSIDE public_html
// e.g. /home/username/config.php
return [
'openai_api_key' => 'sk-your-api-key-here',
'openai_model' => 'gpt-4.1-mini',
'max_tokens' => 1000,
'temperature' => 0.7, // 0 = focused/deterministic, 1 = creative/random
];
?>
<?php
// In your chatbot script
$config = require '/home/username/config.php';
$apiKey = $config['openai_api_key'];
?>
Sending Your First PHP OpenAI API Request
<?php
function send_message($apiKey, $message, $model = 'gpt-4.1-mini', $maxTokens = 1000) {
$payload = json_encode([
'model' => $model,
'max_tokens' => $maxTokens,
'messages' => [
[
'role' => 'user',
'content' => $message,
],
],
]);
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://api.openai.com/v1/chat/completions',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_TIMEOUT => 60, // AI responses can take time
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
]);
$response = curl_exec($ch);
$errno = curl_errno($ch);
$error = curl_error($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($errno) {
echo "cURL error: $error" . PHP_EOL;
return false;
}
if ($httpCode !== 200) {
echo "API error: HTTP $httpCode" . PHP_EOL;
echo "Response: $response" . PHP_EOL;
return false;
}
$data = json_decode($response, true);
return $data['choices'][0]['message']['content'] ?? false;
}
// Load config and test
$config = require '/home/username/config.php';
$response = send_message($config['openai_api_key'], "What is PHP?");
if ($response) {
echo $response . PHP_EOL;
}
?>
Output:
PHP (Hypertext Preprocessor) is a widely-used open-source
server-side scripting language designed primarily for web
development. It can be embedded into HTML and runs on the
server to generate dynamic web page content before the page
is sent to the client's browser...
Adding a System Prompt
A system prompt sets the personality, role, and behavior of the AI before any user messages. Without one the AI behaves as a generic assistant. With one you can make it a customer support agent, a code reviewer, or any specialized role:
<?php
function send_message_with_system($apiKey, $userMessage, $systemPrompt, $model = 'gpt-4.1-mini') {
$payload = json_encode([
'model' => $model,
'messages' => [
[
'role' => 'system',
'content' => $systemPrompt,
],
[
'role' => 'user',
'content' => $userMessage,
],
],
]);
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://api.openai.com/v1/chat/completions',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_TIMEOUT => 60,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) return false;
$data = json_decode($response, true);
return $data['choices'][0]['message']['content'] ?? false;
}
$systemPrompt = "You are a PHP expert assistant. Answer all questions
specifically in the context of PHP development. Keep responses concise
and include code examples where helpful.";
$response = send_message_with_system(
$config['openai_api_key'],
"How do I connect to a database?",
$systemPrompt
);
echo $response . PHP_EOL;
?>
Output:
In PHP, the recommended way to connect to a MySQL database
is using PDO:
```php
$pdo = new PDO(
"mysql:host=localhost;dbname=mydb;charset=utf8mb4",
"username",
"password",
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
```
This gives you prepared statement support and proper error
handling through exceptions...
Handling Conversation History
The OpenAI API is stateless – it has no memory of previous messages unless you send them with each request. To build a chatbot that remembers context, you must include the full conversation history in every API call:
<?php
class ChatBot {
private $apiKey;
private $model;
private $systemPrompt;
private $history = [];
private $maxTokens;
public function __construct($apiKey, $systemPrompt = '', $model = 'gpt-4.1-mini', $maxTokens = 1000) {
$this->apiKey = $apiKey;
$this->model = $model;
$this->systemPrompt = $systemPrompt;
$this->maxTokens = $maxTokens;
// Add system prompt as first message if provided
if (!empty($systemPrompt)) {
$this->history[] = [
'role' => 'system',
'content' => $systemPrompt,
];
}
}
public function chat($userMessage) {
// Add user message to history
$this->history[] = [
'role' => 'user',
'content' => $userMessage,
];
$payload = json_encode([
'model' => $this->model,
'messages' => $this->history,
'max_tokens' => $this->maxTokens,
]);
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://api.openai.com/v1/chat/completions',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_TIMEOUT => 60,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json',
],
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
$error = json_decode($response, true);
$msg = $error['error']['message'] ?? "Unknown API error (HTTP $httpCode)";
// Remove the user message we just added since request failed
array_pop($this->history);
return "Error: $msg";
}
$data = json_decode($response, true);
$aiReply = $data['choices'][0]['message']['content'] ?? '';
// Add AI response to history for next turn
$this->history[] = [
'role' => 'assistant',
'content' => $aiReply,
];
return $aiReply;
}
public function getHistory() {
return $this->history;
}
public function clearHistory() {
$this->history = [];
if (!empty($this->systemPrompt)) {
$this->history[] = [
'role' => 'system',
'content' => $this->systemPrompt,
];
}
}
public function getTokenEstimate() {
// Rough estimate: ~4 chars per token
$totalChars = array_sum(array_map(
fn($msg) => strlen($msg['content']),
$this->history
));
return (int) ($totalChars / 4);
}
}
// Usage
$config = require '/home/username/config.php';
$bot = new ChatBot(
$config['openai_api_key'],
"You are a helpful PHP programming assistant.",
$config['openai_model']
);
// Multi-turn conversation - AI remembers previous messages
$reply1 = $bot->chat("My name is Kapil and I'm building a web scraper.");
echo "Bot: $reply1" . PHP_EOL . PHP_EOL;
$reply2 = $bot->chat("What PHP extension should I use for HTTP requests?");
echo "Bot: $reply2" . PHP_EOL . PHP_EOL;
// AI remembers the context from earlier
$reply3 = $bot->chat("What was I building again?");
echo "Bot: $reply3" . PHP_EOL;
?>
Output:
Bot: Nice to meet you, Kapil! A web scraper in PHP is a
great project. What kind of data are you planning to
collect, and have you decided on your approach - cURL
with DOMDocument, or a library like Goutte?
Bot: For HTTP requests in a PHP web scraper, I'd recommend
the built-in cURL extension. It gives you full control over
headers, timeouts, cookies, and redirects...
Bot: You mentioned you're building a web scraper, Kapil!
The third response demonstrates context retention – the AI correctly recalls both the project type and the user’s name from earlier in the conversation without being told again.
Handling API Errors and Rate Limits
The OpenAI API returns specific HTTP status codes for different error conditions. Handling each one correctly prevents your chatbot from crashing and gives users meaningful error messages:
<?php
function call_openai_api($apiKey, $messages, $model = 'gpt-4.1-mini', $maxRetries = 3) {
$attempt = 0;
while ($attempt < $maxRetries) {
$attempt++;
$payload = json_encode([
'model' => $model,
'messages' => $messages,
'max_tokens' => 1000,
]);
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://api.openai.com/v1/chat/completions',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_TIMEOUT => 60,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
]);
$response = curl_exec($ch);
$errno = curl_errno($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// Network error
if ($errno) {
echo "Network error on attempt $attempt. Retrying..." . PHP_EOL;
sleep($attempt * 2);
continue;
}
$data = json_decode($response, true);
switch ($httpCode) {
case 200:
return $data['choices'][0]['message']['content'] ?? '';
case 429:
// Rate limited - check retry-after header or use backoff
$waitTime = 10 * $attempt;
echo "Rate limited. Waiting {$waitTime}s before retry..." . PHP_EOL;
sleep($waitTime);
continue 2;
case 401:
// Invalid API key - no point retrying
echo "Invalid API key. Check your credentials." . PHP_EOL;
return false;
case 400:
$errorMsg = $data['error']['message'] ?? 'Bad request';
echo "Bad request: $errorMsg" . PHP_EOL;
return false;
case 500:
case 503:
// OpenAI server error - retry
echo "OpenAI server error (HTTP $httpCode). Retrying in " . ($attempt * 3) . "s..." . PHP_EOL;
sleep($attempt * 3);
continue 2;
default:
echo "Unexpected HTTP $httpCode" . PHP_EOL;
return false;
}
}
echo "All $maxRetries attempts failed." . PHP_EOL;
return false;
}
?>
Tracking Token Usage and Costs
Every API response includes token usage data. Track it to monitor costs and avoid unexpected bills:
<?php
function send_message_with_usage($apiKey, $messages, $model = 'gpt-4.1-mini') {
$payload = json_encode([
'model' => $model,
'messages' => $messages,
]);
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://api.openai.com/v1/chat/completions',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_TIMEOUT => 60,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) return false;
$data = json_decode($response, true);
$reply = $data['choices'][0]['message']['content'] ?? '';
$promptTokens = $data['usage']['prompt_tokens'] ?? 0;
$outputTokens = $data['usage']['completion_tokens'] ?? 0;
$totalTokens = $data['usage']['total_tokens'] ?? 0;
echo "Reply: $reply" . PHP_EOL;
echo PHP_EOL . "Token usage:" . PHP_EOL;
echo " Prompt tokens: $promptTokens" . PHP_EOL;
echo " Output tokens: $outputTokens" . PHP_EOL;
echo " Total tokens: $totalTokens" . PHP_EOL;
return [
'reply' => $reply,
'prompt_tokens' => $promptTokens,
'output_tokens' => $outputTokens,
'total_tokens' => $totalTokens,
];
}
$config = require '/home/username/config.php';
$result = send_message_with_usage($config['openai_api_key'], [
['role' => 'user', 'content' => 'Explain PHP sessions in one sentence.'],
], $config['openai_model']);
?>
Output:
Reply: PHP sessions store user data on the server between
requests, identified by a session ID stored in a cookie,
allowing state persistence across stateless HTTP connections.
Token usage:
Prompt tokens: 16
Output tokens: 38
Total tokens: 54
Building a PHP OpenAI API Chatbot Web Interface
Save this as chat.php in your web root for a working browser-based chat interface:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PHP AI Chatbot</title>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 40px auto; padding: 0 20px; }
#chat-box { border: 1px solid #ddd; height: 400px; overflow-y: auto; padding: 15px; margin-bottom: 15px; border-radius: 8px; background: #f9f9f9; }
.message { margin: 10px 0; padding: 10px 15px; border-radius: 8px; max-width: 80%; }
.user { background: #3498db; color: white; margin-left: auto; }
.assistant { background: white; border: 1px solid #ddd; }
#chat-form { display: flex; gap: 10px; }
#user-input { flex: 1; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; }
button { padding: 10px 20px; background: #3498db; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; }
button:disabled { background: #bbb; }
.typing { color: #999; font-style: italic; }
</style>
</head>
<body>
<h2>PHP AI Chatbot</h2>
<div id="chat-box">
<div class="message assistant">Hello! I'm your PHP assistant. How can I help you today?</div>
</div>
<form id="chat-form">
<input type="text" id="user-input" placeholder="Type your message..." autocomplete="off">
<button type="submit" id="send-btn">Send</button>
</form>
<script>
const chatBox = document.getElementById('chat-box');
const form = document.getElementById('chat-form');
const input = document.getElementById('user-input');
const sendBtn = document.getElementById('send-btn');
function addMessage(content, role) {
const div = document.createElement('div');
div.className = 'message ' + role;
div.textContent = content;
chatBox.appendChild(div);
chatBox.scrollTop = chatBox.scrollHeight;
return div;
}
form.addEventListener('submit', async function(e) {
e.preventDefault();
const message = input.value.trim();
if (!message) return;
input.value = '';
sendBtn.disabled = true;
addMessage(message, 'user');
const typingDiv = addMessage('Thinking...', 'assistant typing');
try {
const response = await fetch('chat_api.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message }),
});
const data = await response.json();
typingDiv.textContent = data.reply || 'Sorry, something went wrong.';
typingDiv.classList.remove('typing');
} catch (error) {
typingDiv.textContent = 'Connection error. Please try again.';
typingDiv.classList.remove('typing');
}
sendBtn.disabled = false;
input.focus();
});
</script>
</body>
</html>
Create chat_api.php to handle the AJAX requests from the frontend:
<?php
session_start();
header('Content-Type: application/json');
$config = require '/home/username/config.php';
$input = json_decode(file_get_contents('php://input'), true);
$message = trim($input['message'] ?? '');
if (empty($message)) {
echo json_encode(['reply' => 'Empty message.']);
exit;
}
// Initialize conversation history in session
if (!isset($_SESSION['chat_history'])) {
$_SESSION['chat_history'] = [
[
'role' => 'system',
'content' => 'You are a helpful PHP programming assistant. Keep responses concise and practical.',
],
];
}
// Add user message
$_SESSION['chat_history'][] = [
'role' => 'user',
'content' => $message,
];
// Limit history to last 20 messages to control token usage
if (count($_SESSION['chat_history']) > 21) { // 1 system + 20 messages
$systemPrompt = array_shift($_SESSION['chat_history']);
$_SESSION['chat_history'] = array_slice($_SESSION['chat_history'], -20);
array_unshift($_SESSION['chat_history'], $systemPrompt);
}
// Call OpenAI API
$payload = json_encode([
'model' => $config['openai_model'],
'messages' => $_SESSION['chat_history'],
'max_tokens' => $config['max_tokens'],
]);
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://api.openai.com/v1/chat/completions',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_TIMEOUT => 60,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $config['openai_api_key'],
'Content-Type: application/json',
],
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
echo json_encode(['reply' => 'API error. Please try again.']);
exit;
}
$data = json_decode($response, true);
$reply = $data['choices'][0]['message']['content'] ?? 'No response.';
// Add AI response to history
$_SESSION['chat_history'][] = [
'role' => 'assistant',
'content' => $reply,
];
echo json_encode(['reply' => $reply]);
?>
The session stores conversation history between requests so the AI maintains context across the entire browser session. The history is capped at 20 messages to prevent token counts from growing indefinitely.
Frequently Asked Questions
How much does the OpenAI API cost for a PHP chatbot?
It depends entirely on usage volume and which model you choose. As of 2026, GPT-4.1 Mini costs a fraction of GPT-4 and handles most chatbot use cases well. A typical short conversation of 10 messages costs well under $0.01. High-traffic production chatbots with thousands of daily conversations cost more – check current pricing at openai.com/pricing and set usage limits in your OpenAI dashboard to prevent unexpected charges.
Why is the AI forgetting previous messages?
The OpenAI API is stateless – it has no built-in memory. You must send the complete conversation history with every request. If the AI seems to forget previous messages, your code isn’t including $_SESSION['chat_history'] in the messages array. Check that previous assistant responses are being appended to the history array before each new API call.
What is the difference between GPT-4 and GPT-4.1 Mini?
GPT-4.1 Mini is faster and significantly cheaper than full GPT-4 while handling most tasks – answering questions, writing code, explaining concepts – equally well for chatbot purposes. Full GPT-4 is worth the extra cost for complex reasoning tasks, nuanced analysis, or situations where response quality is critical. Start with GPT-4.1 Mini and upgrade only if you hit capability limits.
How do I limit conversation history to control costs?
Cap the history array to the last N messages before sending to the API. The web interface example above limits to 20 messages. Always keep the system prompt as the first message when trimming – remove the oldest user and assistant message pairs from the middle rather than the beginning.
Can I use this chatbot with Laravel?
Yes – the core cURL code works identically in Laravel. In a Laravel application, store the API key in your .env file and access it with env('OPENAI_API_KEY'). Store conversation history in the session with session(['chat_history' => $history]) and retrieve it with session('chat_history', []). The HTTP controller handles the API call the same way as the standalone PHP example.
Summary
Building an AI chatbot in PHP using the OpenAI API comes down to three things – proper authentication, sending conversation history with every request, and handling errors correctly:
- Store API keys securely – config file outside the web root, never hardcoded in PHP files that could reach version control
- Always send conversation history – the API is stateless, context retention requires you to pass previous messages on every call
- Add a system prompt – defines the AI’s role and behavior before any user interaction
- Handle rate limits with backoff – 429 responses require waiting, not immediate retry
- Track token usage – every response includes usage data, log it to monitor costs before they become a surprise
- Cap conversation history – unlimited history means unlimited token costs. Limit to the last 15-20 messages for most chatbots.
The cURL skills used to call the OpenAI API are identical to those used in web scraping projects. If you want to build a chatbot that also fetches and summarizes web content, the PHP cURL web scraping complete guide covers every request option and HTML parsing pattern that combines naturally with AI API integration.
Complete Source Code (GitHub)
You can access the complete AI chatbot project here:
👉 View AI Chatbot Source Code on GitHub
This project includes:
- PHP integration with OpenAI API
- cURL-based API requests
- JSON response handling
- Simple chatbot implementation
git clone https://github.com/phpspiderblog/ai-chatbot-php
