How to Integrate ChatGPT with PHP (Complete Guide 2026)

Integrating ChatGPT into a PHP application means calling the OpenAI Chat Completions API from your backend. This guide covers every approach: the official PHP SDK, raw cURL, streaming responses, conversation history, and a working web chat form. All examples use gpt-4o-mini — fast, capable, and cost-effective.

For the full PHP SDK reference, see OpenAI PHP SDK Guide. For a Laravel-specific integration, see Laravel OpenAI Integration.


Option A: Official PHP SDK (Recommended)

Install via Composer:

composer require openai-php/client

Basic chat completion:

<?php

require 'vendor/autoload.php';

$client = OpenAI::client(getenv('OPENAI_API_KEY'));

$response = $client->chat()->create([
    'model'    => 'gpt-4o-mini',
    'messages' => [
        ['role' => 'system', 'content' => 'You are a helpful assistant.'],
        ['role' => 'user',   'content' => 'Explain closures in PHP in one sentence.'],
    ],
    'max_tokens' => 200,
]);

echo $response->choices[0]->message->content;

Option B: Raw cURL (No Dependencies)

If you can’t install Composer, use cURL directly:

<?php

function chatgpt(string $message, string $apiKey): string
{
    $payload = json_encode([
        'model'    => 'gpt-4o-mini',
        'messages' => [
            ['role' => 'system', 'content' => 'You are a helpful assistant.'],
            ['role' => 'user',   'content' => $message],
        ],
        'max_tokens' => 512,
    ]);

    $ch = curl_init('https://api.openai.com/v1/chat/completions');
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => $payload,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => [
            'Content-Type: application/json',
            'Authorization: Bearer ' . $apiKey,
        ],
        CURLOPT_TIMEOUT => 30,
    ]);

    $result = curl_exec($ch);
    $errno  = curl_errno($ch);
    curl_close($ch);

    if ($errno) {
        throw new RuntimeException('cURL error: ' . curl_strerror($errno));
    }

    $data = json_decode($result, true);

    if (isset($data['error'])) {
        throw new RuntimeException('OpenAI error: ' . $data['error']['message']);
    }

    return $data['choices'][0]['message']['content'];
}

echo chatgpt('What is the difference between == and === in PHP?', getenv('OPENAI_API_KEY'));

Conversation History

Maintain context by keeping the full message history in a session:

<?php

require 'vendor/autoload.php';

session_start();

if (!isset($_SESSION['messages'])) {
    $_SESSION['messages'] = [
        ['role' => 'system', 'content' => 'You are a helpful PHP developer assistant.'],
    ];
}

function chat(string $input): string
{
    $client = OpenAI::client(getenv('OPENAI_API_KEY'));

    $_SESSION['messages'][] = ['role' => 'user', 'content' => $input];

    // Keep last 20 exchanges to avoid token overflow
    $messages = array_merge(
        [array_shift($_SESSION['messages'])],  // always keep system prompt
        array_slice($_SESSION['messages'], -20)
    );

    $response = $client->chat()->create([
        'model'    => 'gpt-4o-mini',
        'messages' => $messages,
    ]);

    $reply = $response->choices[0]->message->content;
    $_SESSION['messages'][] = ['role' => 'assistant', 'content' => $reply];

    return $reply;
}

// Usage
echo chat("My name is Alex.");            // Nice to meet you, Alex!
echo "\n";
echo chat("What's my name?");             // Your name is Alex.

Streaming Responses

Stream tokens as they are generated for a faster-feeling UI:

<?php

require 'vendor/autoload.php';

header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');

$input = json_decode(file_get_contents('php://input'), true)['message'] ?? '';

$client = OpenAI::client(getenv('OPENAI_API_KEY'));

$stream = $client->chat()->createStreamed([
    'model'    => 'gpt-4o-mini',
    'messages' => [
        ['role' => 'user', 'content' => $input],
    ],
]);

foreach ($stream as $response) {
    $delta = $response->choices[0]->delta->content;
    if ($delta !== null) {
        echo "data: " . json_encode(['chunk' => $delta]) . "\n\n";
        ob_flush();
        flush();
    }
}

echo "data: [DONE]\n\n";

Web Chat Form

A minimal HTML form that sends messages and displays replies:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>ChatGPT PHP</title>
    <style>
        body { font-family: sans-serif; max-width: 600px; margin: 40px auto; padding: 0 16px; }
        #chat { border: 1px solid #ddd; height: 360px; overflow-y: auto;
                padding: 12px; border-radius: 8px; margin-bottom: 12px; }
        .user { color: #1a56db; margin-bottom: 8px; }
        .bot  { color: #111; margin-bottom: 8px; }
        #form { display: flex; gap: 8px; }
        input { flex: 1; padding: 8px 12px; border: 1px solid #ddd; border-radius: 6px; }
        button { padding: 8px 16px; background: #1a56db; color: #fff; border: none;
                 border-radius: 6px; cursor: pointer; }
    </style>
</head>
<body>
<div id="chat"></div>
<form id="form">
    <input id="msg" placeholder="Ask ChatGPT...">
    <button>Send</button>
</form>
<script>
const chat = document.getElementById('chat');
const msg  = document.getElementById('msg');

function append(cls, text) {
    const d = document.createElement('div');
    d.className = cls;
    d.textContent = text;
    chat.appendChild(d);
    chat.scrollTop = chat.scrollHeight;
    return d;
}

document.getElementById('form').addEventListener('submit', async (e) => {
    e.preventDefault();
    const text = msg.value.trim();
    if (!text) return;
    msg.value = '';
    append('user', 'You: ' + text);
    const bot = append('bot', 'AI: ');

    const res = await fetch('stream.php', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ message: text }),
    });

    const reader = res.body.getReader();
    const dec = new TextDecoder();
    let buf = '';

    while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        buf += dec.decode(value, { stream: true });
        for (const line of buf.split('\n')) {
            if (!line.startsWith('data: ')) continue;
            const d = line.slice(6);
            if (d === '[DONE]') break;
            try { bot.textContent += JSON.parse(d).chunk; } catch {}
        }
        buf = buf.split('\n').pop();
    }
});
</script>
</body>
</html>

Using a Custom System Prompt

Control ChatGPT’s persona by changing the system message:

$messages = [
    [
        'role'    => 'system',
        'content' => 'You are an expert PHP developer. '
                   . 'Give short, precise answers with code examples. '
                   . 'Always follow PSR-12 coding standards.',
    ],
    ['role' => 'user', 'content' => $userInput],
];

Error Handling

use OpenAI\Exceptions\ErrorException;
use OpenAI\Exceptions\TransporterException;

try {
    $response = $client->chat()->create([...]);
    $reply = $response->choices[0]->message->content;
} catch (ErrorException $e) {
    $code = $e->getCode();
    $reply = match (true) {
        $code === 401 => 'Invalid API key.',
        $code === 429 => 'Rate limit reached. Please wait a moment.',
        $code >= 500  => 'OpenAI is temporarily unavailable.',
        default       => 'API error: ' . $e->getMessage(),
    };
} catch (TransporterException $e) {
    $reply = 'Network error. Please check your connection.';
}

Summary

  • SDK: composer require openai-php/client then OpenAI::client($key)->chat()->create([...])
  • Raw cURL: works without Composer — POST JSON to https://api.openai.com/v1/chat/completions
  • History: store $_SESSION['messages'], trim to last 20 to avoid token overflow
  • Streaming: use createStreamed() with SSE headers for real-time output
  • Catch ErrorException (API errors) and TransporterException (network errors)

Subscribe to my newsletter — practical guides on Claude API, AI agents, RAG, and automation.

Subscribe