Create a chatbot in PHP using the Gemini API (from Google AI)

24804 views
chatbot_gemani_ipdata_code

AI chatbots are no longer optional add-ons — they are the new standard for websites, SaaS platforms, internal tools, and customer support systems. And while Python dominates the AI development space, PHP powers over 77% of the web, making it critical to know how to plug cutting-edge AI into PHP projects without migrating your entire stack.

In this guide, you will learn how to build a production-ready AI chatbot in PHP using Google`s Gemini API — the same AI behind Google`s Bard and Gemini products. We`ll go far beyond a basic “hello world” API call. By the end, you’ll have:

  • A working multi-turn chatbot with conversation memory
  • A clean UI with AJAX-powered real-time responses
  • Proper error handling and security practices
  • Tips for deploying this on WordPress or any PHP host

Let`s build something real.

 

How a PHP Gemini Chatbot Works

 

Why Gemini API for PHP Developers?

Before we write a single line of code, let`s understand why Gemini is the right choice for PHP-based chatbot projects:

Feature Gemini API OpenAI GPT
Free tier ✅ Generous ❌ Limited
Multimodal (text + image) ✅ Yes ✅ Yes (GPT-4V)
PHP SDK ⚠️ REST only ⚠️ REST only
Response quality ✅ Excellent ✅ Excellent
Google ecosystem integration ✅ Native ❌ No
Rate limits (free) 60 req/min 3 req/min

For PHP developers on a budget or building for Google Cloud infrastructure, Gemini is an outstanding choice.

 

Prerequisites

Before starting, make sure your environment has:

  • PHP 7.4 or higher (PHP 8.x recommended)
  • cURL extension enabled – check with phpinfo() or php -m | grep curl
  • A Gemini API Key – get one free from Google AI Studio
  • Basic knowledge of PHP, HTML, and JSON

 

Project Structure

Here`s the file layout we`ll build:

gemini-chatbot/
├── index.php          # Main UI (HTML + JS)
├── chat.php           # Backend API handler
├── config.php         # API key and settings
└── style.css          # Optional: chatbot styles

 

Step 1: Store Your API Key Safely

Never hardcode API keys in your PHP files. Use a config file and add it to .gitignore.

config.php

   
<?php
// config.php — Never commit this to version control!
define('GEMINI_API_KEY', 'YOUR_GEMINI_API_KEY_HERE');
define('GEMINI_MODEL', 'gemini-1.5-flash'); // or gemini-1.5-pro
define('GEMINI_API_URL', 'https://generativelanguage.googleapis.com/v1beta/models/' . GEMINI_MODEL . ':generateContent');

Security Tip: Add config.php to your .gitignore. On production servers, consider loading the key from environment variables using $_ENV[‘GEMINI_API_KEY’] instead.

 

Step 2: Build the Core PHP API Handler

This is the engine of your chatbot — it takes a user message (plus optional conversation history), sends it to Gemini, and returns the AI`s response.

chat.php

<?php
require_once 'config.php';

// Only accept POST requests
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    echo json_encode(['error' => 'Method not allowed']);
    exit;
}

// Read and decode JSON body
$input = json_decode(file_get_contents('php://input'), true);

if (empty($input['message'])) {
    http_response_code(400);
    echo json_encode(['error' => 'Message is required']);
    exit;
}

$userMessage = htmlspecialchars(strip_tags(trim($input['message'])));

// Conversation history (passed from frontend for multi-turn memory)
$history = $input['history'] ?? [];

// Build the contents array with full conversation history
$contents = [];

foreach ($history as $turn) {
    $contents[] = [
        'role'  => $turn['role'],  // 'user' or 'model'
        'parts' => [['text' => $turn['text']]]
    ];
}

// Append the latest user message
$contents[] = [
    'role'  => 'user',
    'parts' => [['text' => $userMessage]]
];

// Gemini API request payload
$payload = [
    'contents' => $contents,
    'generationConfig' => [
        'temperature'     => 0.7,   // 0 = deterministic, 1 = creative
        'maxOutputTokens' => 1024,
        'topP'            => 0.9,
    ],
    'safetySettings' => [
        [
            'category'  => 'HARM_CATEGORY_HARASSMENT',
            'threshold' => 'BLOCK_MEDIUM_AND_ABOVE'
        ],
        [
            'category'  => 'HARM_CATEGORY_HATE_SPEECH',
            'threshold' => 'BLOCK_MEDIUM_AND_ABOVE'
        ]
    ]
];

// Make the cURL request
$ch = curl_init(GEMINI_API_URL . '?key=' . GEMINI_API_KEY);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => json_encode($payload),
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
    CURLOPT_TIMEOUT        => 30,
    CURLOPT_SSL_VERIFYPEER => true,
]);

$response    = curl_exec($ch);
$httpCode    = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError   = curl_error($ch);
curl_close($ch);

// Handle cURL errors
if ($curlError) {
    http_response_code(500);
    echo json_encode(['error' => 'Connection failed: ' . $curlError]);
    exit;
}

// Handle non-200 responses from Gemini
if ($httpCode !== 200) {
    http_response_code($httpCode);
    $errorData = json_decode($response, true);
    echo json_encode(['error' => $errorData['error']['message'] ?? 'Gemini API error']);
    exit;
}

// Parse the response
$data  = json_decode($response, true);
$reply = $data['candidates'][0]['content']['parts'][0]['text'] ?? null;

if (!$reply) {
    // Possibly blocked by safety filters
    $blockReason = $data['candidates'][0]['finishReason'] ?? 'UNKNOWN';
    echo json_encode(['error' => 'Response blocked or empty. Reason: ' . $blockReason]);
    exit;
}

// Return the bot reply as JSON
header('Content-Type: application/json');
echo json_encode(['reply' => $reply]);

 

Step 3: Build the Frontend with Conversation Memory

A great chatbot UI needs to feel alive — instant responses, a typing indicator, and memory across the conversation.

index.php

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>PHP Gemini AI Chatbot</title>
    <style>
        * { box-sizing: border-box; margin: 0; padding: 0; }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, sans-serif;
            background: #0f172a;
            display: flex;
            align-items: center;
            justify-content: center;
            min-height: 100vh;
        }

        .chat-container {
            width: 420px;
            height: 600px;
            background: #1e293b;
            border-radius: 16px;
            display: flex;
            flex-direction: column;
            overflow: hidden;
            box-shadow: 0 25px 60px rgba(0,0,0,0.5);
        }

        .chat-header {
            background: linear-gradient(135deg, #4f46e5, #7c3aed);
            padding: 16px 20px;
            color: white;
        }

        .chat-header h2 { font-size: 1rem; font-weight: 600; }
        .chat-header p  { font-size: 0.75rem; opacity: 0.8; margin-top: 2px; }

        .chat-messages {
            flex: 1;
            overflow-y: auto;
            padding: 16px;
            display: flex;
            flex-direction: column;
            gap: 12px;
        }

        .message {
            max-width: 80%;
            padding: 10px 14px;
            border-radius: 12px;
            font-size: 0.875rem;
            line-height: 1.5;
            white-space: pre-wrap;
            word-wrap: break-word;
        }

        .message.user {
            background: #4f46e5;
            color: white;
            align-self: flex-end;
            border-bottom-right-radius: 4px;
        }

        .message.bot {
            background: #334155;
            color: #e2e8f0;
            align-self: flex-start;
            border-bottom-left-radius: 4px;
        }

        .typing-indicator {
            display: none;
            align-self: flex-start;
        }

        .typing-indicator span {
            display: inline-block;
            width: 8px; height: 8px;
            background: #64748b;
            border-radius: 50%;
            animation: bounce 1.2s infinite ease-in-out;
            margin: 0 2px;
        }

        .typing-indicator span:nth-child(2) { animation-delay: 0.2s; }
        .typing-indicator span:nth-child(3) { animation-delay: 0.4s; }

        @keyframes bounce {
            0%, 80%, 100% { transform: scale(0.7); opacity: 0.5; }
            40%            { transform: scale(1.2); opacity: 1;   }
        }

        .chat-input-area {
            padding: 12px 16px;
            background: #0f172a;
            display: flex;
            gap: 8px;
            align-items: center;
        }

        .chat-input-area input {
            flex: 1;
            background: #1e293b;
            border: 1px solid #334155;
            color: #e2e8f0;
            padding: 10px 14px;
            border-radius: 8px;
            font-size: 0.875rem;
            outline: none;
            transition: border-color 0.2s;
        }

        .chat-input-area input:focus { border-color: #4f46e5; }

        .chat-input-area button {
            background: #4f46e5;
            color: white;
            border: none;
            padding: 10px 16px;
            border-radius: 8px;
            cursor: pointer;
            font-size: 0.875rem;
            font-weight: 600;
            transition: background 0.2s;
        }

        .chat-input-area button:hover    { background: #4338ca; }
        .chat-input-area button:disabled { background: #334155; cursor: not-allowed; }

        .error-message {
            color: #f87171;
            font-size: 0.8rem;
            text-align: center;
            padding: 4px;
        }
    </style>
</head>
<body>
<div class="chat-container">
    <div class="chat-header">
        <h2>✨ Gemini AI Assistant</h2>
        <p>Powered by Google Gemini 1.5 Flash</p>
    </div>

    <div class="chat-messages" id="chatMessages">
        <div class="message bot">
            Hello! I'm your AI assistant powered by Google Gemini. How can I help you today?
        </div>
        <div class="typing-indicator" id="typingIndicator">
            <span></span><span></span><span></span>
        </div>
    </div>

    <div class="chat-input-area">
        <input
            type="text"
            id="userInput"
            placeholder="Type your message..."
            autocomplete="off"
        />
        <button id="sendBtn" onclick="sendMessage()">Send</button>
    </div>
</div>

<script>
// Store conversation history for multi-turn memory
let conversationHistory = [];

function appendMessage(role, text) {
    const messagesDiv = document.getElementById('chatMessages');
    const typingIndicator = document.getElementById('typingIndicator');

    const msg = document.createElement('div');
    msg.classList.add('message', role === 'user' ? 'user' : 'bot');
    msg.textContent = text;

    // Insert before typing indicator
    messagesDiv.insertBefore(msg, typingIndicator);
    messagesDiv.scrollTop = messagesDiv.scrollHeight;
}

function setTyping(visible) {
    const indicator = document.getElementById('typingIndicator');
    indicator.style.display = visible ? 'flex' : 'none';
    if (visible) {
        const messagesDiv = document.getElementById('chatMessages');
        messagesDiv.scrollTop = messagesDiv.scrollHeight;
    }
}

async function sendMessage() {
    const input   = document.getElementById('userInput');
    const sendBtn = document.getElementById('sendBtn');
    const message = input.value.trim();

    if (!message) return;

    // Show user message
    appendMessage('user', message);
    input.value = '';
    sendBtn.disabled = true;
    setTyping(true);

    // Add to history
    conversationHistory.push({ role: 'user', text: message });

    try {
        const response = await fetch('chat.php', {
            method:  'POST',
            headers: { 'Content-Type': 'application/json' },
            body:    JSON.stringify({
                message: message,
                history: conversationHistory.slice(0, -1) // Send history excluding current message
            })
        });

        const data = await response.json();

        setTyping(false);

        if (data.error) {
            appendMessage('bot', '⚠️ Error: ' + data.error);
        } else {
            appendMessage('bot', data.reply);
            // Add bot reply to history
            conversationHistory.push({ role: 'model', text: data.reply });
        }

        // Keep conversation history manageable (last 20 turns)
        if (conversationHistory.length > 20) {
            conversationHistory = conversationHistory.slice(-20);
        }

    } catch (err) {
        setTyping(false);
        appendMessage('bot', '⚠️ Network error. Please try again.');
        console.error(err);
    }

    sendBtn.disabled = false;
    input.focus();
}

// Allow sending with Enter key
document.getElementById('userInput').addEventListener('keydown', function(e) {
    if (e.key === 'Enter' && !e.shiftKey) {
        e.preventDefault();
        sendMessage();
    }
});
</script>
</body>
</html>

 

Step 4: Understanding the Gemini API Response Structure

When Gemini responds, you get back a JSON object. Here`s what it looks like and how to read it:

{
  "candidates": [
    {
      "content": {
        "parts": [
          { "text": "Hello! How can I assist you today?" }
        ],
        "role": "model"
      },
      "finishReason": "STOP",
      "safetyRatings": [...]
    }
  ],
  "usageMetadata": {
    "promptTokenCount": 12,
    "candidatesTokenCount": 9,
    "totalTokenCount": 21
  }
}

Key fields to know:

  • candidates[0].content.parts[0].text — The actual AI response text
  • candidates[0].finishReason — Why the response ended (STOP, MAX_TOKENS, SAFETY, RECITATION)
  • usageMetadata.totalTokenCount — Token usage (important for rate limits)

 

Step 5: Advanced Configuration — System Prompts & Personality

You can give your chatbot a custom personality using a system instruction, available in Gemini 1.5 models:

 
$payload = [
    'system_instruction' => [
        'parts' => [[
            'text' => 'You are a friendly and knowledgeable customer support agent for AcmeCorp software. 
                       Always be concise, helpful, and professional. 
                       If asked about pricing, direct users to acmecorp.com/pricing.'
        ]]
    ],
    'contents' => $contents,
    // ... rest of payload
];
 

This is what transforms a generic chatbot into a branded AI assistant specific to your product or business.

 

Step 6: Integrating into WordPress

To embed this chatbot inside a WordPress site, you have two clean options:

Option A – Shortcode Widget

Place the chatbot files inside a plugin or your theme, then register a shortcode:

// In your theme's functions.php or a custom plugin
function gemini_chatbot_shortcode() {
    ob_start();
    include get_template_directory() . '/chatbot/index.php';
    return ob_get_clean();
}
add_shortcode('gemini_chat', 'gemini_chatbot_shortcode');

// Use [gemini_chat] in any page or post

Option B — REST API Endpoint

Register a custom WordPress REST API route to handle the chat backend cleanly:

 
add_action('rest_api_init', function () {
    register_rest_route('gemini/v1', '/chat', [
        'methods'             => 'POST',
        'callback'            => 'handle_gemini_chat',
        'permission_callback' => '__return_true',
    ]);
});

function handle_gemini_chat(WP_REST_Request $request) {
    $message = sanitize_text_field($request->get_param('message'));
    $history = $request->get_param('history') ?? [];
    
    // ... (your cURL logic from chat.php goes here)
    
    return new WP_REST_Response(['reply' => $reply], 200);
}

Then your JS fetches from /wp-json/gemini/v1/chat instead of chat.php.

 

Complete Chatbot Architecture

 

Common Errors and How to Fix Them

Error Cause Fix
API key not valid Wrong or missing API key Double-check key in Google AI Studio
cURL error 60 SSL certificate issue Update CA certificates or set CURLOPT_CAINFO
Response blocked (SAFETY) Content flagged by filters Adjust safety thresholds or rephrase prompt
429 Too Many Requests Rate limit hit Add retry logic with exponential backoff
Empty candidates array Model returned nothing Check promptFeedback in response for block reason

 

Second Project Structure

Step 1: Setup Your PHP Environment

Create a PHP file, e.g., chatbot.php.

Step 2: Write the Gemini API Chat Code in PHP

Here’s a simple example:

<?php
$apiKey = 'YOUR_GEMINI_API_KEY'; // Replace with your actual API key

$prompt = "Hello, how can I help you today?";

$data = [
    "contents" => [
        [
            "role" => "user",
            "parts" => [
                ["text" => $prompt]
            ]
        ]
    ]
];

$ch = curl_init('https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=' . $apiKey);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
]);

$response = curl_exec($ch);
curl_close($ch);

// Handle response
$responseData = json_decode($response, true);
$botReply = $responseData['candidates'][0]['content']['parts'][0]['text'] ?? "No response";

echo "<strong>Bot:</strong> " . htmlspecialchars($botReply);

Step 3: Test

Put this on a local server or a live PHP-compatible host and access the file in the browser.

Optional: Add a Simple HTML Form for User Input

<form method="post">
  <input type="text" name="prompt" placeholder="Say something..." required>
  <button type="submit">Send</button>
</form>

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['prompt'])) {
    $userPrompt = $_POST['prompt'];
    // use $userPrompt in the API call above
}
?>

Production Checklist

Before deploying to production, make sure you’ve covered:

  • Rate limiting – Add per-user or per-IP request throttling to prevent abuse
  • Input sanitization – Always sanitize user input before sending to any API
  • API key protection – Never expose the key in frontend JS; always call from the PHP backend
  • HTTPS only — Never run AI chatbots over plain HTTP
  • Token budgeting – Set maxOutputTokens to control costs
  • Session handling – For persistent history across page reloads, store conversation in PHP sessions or a database
  • Logging – Log errors (not user messages) for debugging

 

What to Build Next

Once your chatbot is live, here are logical next steps to level up:

1. Add Streaming Responses — Stream tokens word-by-word using Server-Sent Events (SSE) for a ChatGPT-style experience. Gemini supports streaming via the :streamGenerateContent endpoint.

2. Connect to a Database — Store chat history in MySQL and let users resume past conversations.

3. Upload Documents/PDFs — Gemini is multimodal. You can send PDFs and images alongside text, enabling document Q&A features.

4. Add Voice Input — Use the browser’s Web Speech API to let users speak to your chatbot.

5. Use Gemini 1.5 Pro for Complex Tasks — Flash is fast and cheap; Pro is more capable for reasoning and long-context tasks (up to 1 million tokens).

Feature Gemini API ChatGPT API
Free Tier Yes Limited
PHP Integration Easy Easy
Multimodal Yes Yes
Cost Lower Higher

 

Building a chatbot in PHP with the Gemini API is genuinely straightforward once you understand the REST API structure and how multi-turn conversation history works. What makes this powerful isn`t the basic API call — it`s the layers on top: proper error handling, conversation memory, system prompts, and a polished UI.

Google Gemini`s free tier is generous enough to build and test a full production chatbot, making it one of the most accessible AI APIs available today.

The code in this guide is production-ready. Fork it, adapt the system prompt to your domain, and ship your first AI-powered PHP product.

 

Frequently Asked Questions

+

1. What is Google Gemini API?

Google Gemini API is a generative AI service from Google that allows developers to integrate AI-powered text generation, chatbots, content creation, and intelligent assistants into applications.
+

2. Can I create a chatbot in PHP using Gemini API?

Yes. PHP can easily connect with the Gemini API using cURL or HTTP requests, enabling developers to build AI chatbots, virtual assistants, and customer support applications.
+

3. Is Gemini API free to use?

Google provides free usage limits for Gemini API through Google AI Studio. However, higher usage may require a paid plan depending on API consumption.
+

4. How do I get a Gemini API key?

You can generate an API key from Google AI Studio after creating a Google account and enabling Gemini API access.
+

5. Which PHP version is required for Gemini API integration?

PHP 7.4 or higher is recommended. PHP 8.x is preferred for better performance and security.
+

6. Can I deploy a Gemini chatbot on shared hosting?

Yes. Most shared hosting providers support PHP and cURL, making it possible to deploy a Gemini-powered chatbot without a VPS.
+

7. How can I secure my Gemini API key?

Store the API key in environment variables or configuration files outside the public web directory. Never expose API keys in JavaScript or public repositories.
+

8. Can Gemini API remember previous messages?

By default, Gemini API does not automatically store conversation history. Developers must manage chat sessions and conversation memory in PHP.
+

9. What are common errors when integrating Gemini API with PHP?

Common issues include invalid API keys, incorrect JSON payloads, API rate limits, network timeouts, and malformed request headers.
+

10. Can I build a ChatGPT-like application using Gemini API and PHP?

Yes. Gemini API provides conversational AI capabilities that can be used to build ChatGPT-style applications, customer support bots, and AI assistants.
+

11. Does Gemini API support multiple languages?

Yes. Gemini API supports multiple languages, including English, Hindi, Spanish, French, German, and many others.
+

12. Is Gemini API suitable for production applications?

Yes. Gemini API can be used in production environments for chatbots, content generation, customer support systems, and enterprise AI applications when proper security and rate-limiting measures are implemented.
Next Article

PDF in WordPress using FPDF : Custom PDF Generator

Write a Comment

Leave a Comment

Your email address will not be published. Required fields are marked *

Subscribe to our Newsletter

Subscribe to our email newsletter to get the latest posts delivered right to your email.
Pure inspiration, zero spam ✨