Skip to content

How to Add ChatGPT to Your WordPress Site (No-Code + PHP)

Adding an AI chatbot or content assistant to WordPress doesn’t require a developer and doesn’t require writing a single line of code — if you pick the right plugin. But if you want full control over the UX, conversation history, and how the AI responds, a custom PHP integration beats any off-the-shelf plugin. This guide covers both paths: the fastest no-code route using the AI Engine plugin, and a custom PHP shortcode that calls the OpenAI API directly inside your theme or child theme. You’ll also see how to swap OpenAI for the Claude API if you want Anthropic’s models instead.


Option 1: No-Code — AI Engine Plugin

AI Engine by Jordy Meow is the most complete free WordPress plugin for OpenAI integration. It handles the API calls, conversation history, rate limiting, and UI — you just connect your API key and drop a shortcode.

Install and configure:

  1. In WordPress admin → Plugins → Add New → search “AI Engine” → Install & Activate
  2. Go to Meow Apps → AI Engine → Settings
  3. Paste your OpenAI API key (get one at platform.openai.com → API keys)
  4. Choose your default model: gpt-4o for best quality, gpt-4o-mini for lower cost
  5. Save settings

Add a chatbot to any page or post:

[mwai_chatbot]

That’s the entire shortcode. Drop it anywhere in the editor. AI Engine renders a floating chat widget or inline panel depending on your layout settings. Key configuration options:

[mwai_chatbot
  ai_name="Assistant"
  start_sentence="Hi! How can I help you today?"
  model="gpt-4o-mini"
  max_tokens="500"
  temperature="0.7"
  context="You are a helpful assistant for a WordPress developer blog."
]

The context parameter sets the system prompt — this is where you define the assistant’s personality, restrict it to your topic area, or give it knowledge about your business.

Content generation is AI Engine’s second major feature. In the post editor, you get a sidebar panel with AI-assisted writing: generate a draft from a title, expand a paragraph, summarize, or rewrite in a different tone. It works inside Gutenberg and the Classic Editor.

Limits of the plugin approach: you can’t deeply customize the UI without CSS overrides, conversation data lives in the plugin’s own tables (not your schema), and adding logic (e.g., “if user mentions pricing, show a discount code”) requires hooks into plugin filters. For those needs, the PHP integration below gives you full control.


Option 2: Custom PHP — Call the OpenAI API Directly

If you need custom UI, server-side logic, or want to store conversations in your own database, build a small PHP integration in your child theme’s functions.php (or a custom plugin). You need an OpenAI API key and the free OpenAI PHP SDK — or just raw wp_remote_post() with no external dependency.

Register Your API Key Safely

Never hardcode secrets. Add the key to wp-config.php above the “stop editing” comment:

define( 'OPENAI_API_KEY', 'sk-...' );

This keeps the key out of your theme files and out of version control.


A Simple Chat Shortcode

The function below registers a [ai_chat] shortcode that renders a chat form. On submit, it calls the OpenAI Chat Completions endpoint via WordPress’s built-in wp_remote_post():

<?php
// functions.php (child theme) or a custom plugin

function ai_chat_shortcode( $atts ) {
    $atts = shortcode_atts( [
        'model'   => 'gpt-4o-mini',
        'system'  => 'You are a helpful assistant.',
        'title'   => 'Ask me anything',
    ], $atts );

    $answer = '';
    if ( isset( $_POST['ai_question'] ) && wp_verify_nonce( $_POST['ai_nonce'], 'ai_chat' ) ) {
        $question = sanitize_text_field( wp_unslash( $_POST['ai_question'] ) );
        $answer   = ai_chat_ask( $question, $atts['model'], $atts['system'] );
    }

    ob_start();
    ?>
    <div class="ai-chat-widget">
        <h3><?php echo esc_html( $atts['title'] ); ?></h3>
        <form method="post">
            <?php wp_nonce_field( 'ai_chat', 'ai_nonce' ); ?>
            <textarea name="ai_question" rows="3" placeholder="Type your question..."
                style="width:100%;padding:8px;box-sizing:border-box"
            ><?php echo isset( $_POST['ai_question'] ) ? esc_textarea( $_POST['ai_question'] ) : ''; ?></textarea>
            <button type="submit" style="margin-top:8px;padding:8px 16px">Ask</button>
        </form>
        <?php if ( $answer ) : ?>
            <div class="ai-answer" style="margin-top:16px;padding:12px;background:#f5f5f5;border-radius:4px">
                <?php echo wp_kses_post( nl2br( $answer ) ); ?>
            </div>
        <?php endif; ?>
    </div>
    <?php
    return ob_get_clean();
}
add_shortcode( 'ai_chat', 'ai_chat_shortcode' );


function ai_chat_ask( string $question, string $model, string $system ): string {
    $response = wp_remote_post(
        'https://api.openai.com/v1/chat/completions',
        [
            'timeout' => 30,
            'headers' => [
                'Authorization' => 'Bearer ' . OPENAI_API_KEY,
                'Content-Type'  => 'application/json',
            ],
            'body' => wp_json_encode( [
                'model'    => $model,
                'messages' => [
                    [ 'role' => 'system', 'content' => $system ],
                    [ 'role' => 'user',   'content' => $question ],
                ],
                'max_tokens' => 500,
            ] ),
        ]
    );

    if ( is_wp_error( $response ) ) {
        return 'Error: ' . $response->get_error_message();
    }

    $body = json_decode( wp_remote_retrieve_body( $response ), true );
    return $body['choices'][0]['message']['content'] ?? 'No response.';
}

Use it in any post or page:

[ai_chat title="Ask about WordPress" system="You are a WordPress expert. Answer concisely." model="gpt-4o-mini"]

Adding Conversation History

The simple version above forgets each message. To keep context across a conversation, store messages in the PHP session and pass the full history on each call:

<?php
function ai_chat_with_history_shortcode( $atts ) {
    $atts = shortcode_atts( [
        'model'  => 'gpt-4o-mini',
        'system' => 'You are a helpful assistant.',
    ], $atts );

    if ( ! session_id() ) {
        session_start();
    }

    $session_key = 'ai_chat_' . md5( $atts['system'] );

    if ( ! isset( $_SESSION[ $session_key ] ) ) {
        $_SESSION[ $session_key ] = [];
    }

    if ( isset( $_POST['ai_question'] ) && wp_verify_nonce( $_POST['ai_nonce'], 'ai_chat_h' ) ) {
        $question = sanitize_text_field( wp_unslash( $_POST['ai_question'] ) );

        $_SESSION[ $session_key ][] = [ 'role' => 'user', 'content' => $question ];

        $messages = array_merge(
            [ [ 'role' => 'system', 'content' => $atts['system'] ] ],
            $_SESSION[ $session_key ]
        );

        $response = wp_remote_post( 'https://api.openai.com/v1/chat/completions', [
            'timeout' => 30,
            'headers' => [
                'Authorization' => 'Bearer ' . OPENAI_API_KEY,
                'Content-Type'  => 'application/json',
            ],
            'body' => wp_json_encode( [
                'model'      => $atts['model'],
                'messages'   => $messages,
                'max_tokens' => 500,
            ] ),
        ] );

        $body   = json_decode( wp_remote_retrieve_body( $response ), true );
        $answer = $body['choices'][0]['message']['content'] ?? 'No response.';

        $_SESSION[ $session_key ][] = [ 'role' => 'assistant', 'content' => $answer ];
    }

    if ( isset( $_POST['ai_reset'] ) ) {
        $_SESSION[ $session_key ] = [];
    }

    ob_start();
    ?>
    <div class="ai-chat-widget">
        <div class="ai-history" style="max-height:300px;overflow-y:auto;padding:12px;background:#f9f9f9;border-radius:4px;margin-bottom:12px">
        <?php foreach ( $_SESSION[ $session_key ] as $msg ) : ?>
            <div style="margin-bottom:8px">
                <strong><?php echo $msg['role'] === 'user' ? 'You' : 'AI'; ?>:</strong>
                <?php echo wp_kses_post( nl2br( $msg['content'] ) ); ?>
            </div>
        <?php endforeach; ?>
        </div>
        <form method="post">
            <?php wp_nonce_field( 'ai_chat_h', 'ai_nonce' ); ?>
            <textarea name="ai_question" rows="2" placeholder="Your message..."
                style="width:100%;padding:8px;box-sizing:border-box"></textarea>
            <button type="submit" style="margin-top:6px;padding:8px 16px">Send</button>
            <button type="submit" name="ai_reset" value="1"
                style="margin-top:6px;margin-left:8px;padding:8px 12px;background:#eee">Reset</button>
        </form>
    </div>
    <?php
    return ob_get_clean();
}
add_shortcode( 'ai_chat_history', 'ai_chat_with_history_shortcode' );

Sessions persist across page loads for the same visitor. Each chatbot instance is keyed by system prompt hash, so you can run multiple independent bots on the same site without sessions colliding.


Adding AI to the WordPress REST API

If you’re building a headless WordPress site or a JavaScript front end, expose the AI endpoint via the WP REST API instead of a shortcode:

<?php
// Register a REST endpoint: POST /wp-json/ai/v1/chat
add_action( 'rest_api_init', function () {
    register_rest_route( 'ai/v1', '/chat', [
        'methods'             => 'POST',
        'callback'            => 'ai_rest_chat',
        'permission_callback' => '__return_true',  // add auth if needed
        'args'                => [
            'message' => [
                'required'          => true,
                'sanitize_callback' => 'sanitize_text_field',
            ],
            'model' => [
                'default'           => 'gpt-4o-mini',
                'sanitize_callback' => 'sanitize_text_field',
            ],
        ],
    ] );
} );

function ai_rest_chat( WP_REST_Request $request ): WP_REST_Response {
    $message = $request->get_param( 'message' );
    $model   = $request->get_param( 'model' );

    $response = wp_remote_post( 'https://api.openai.com/v1/chat/completions', [
        'timeout' => 30,
        'headers' => [
            'Authorization' => 'Bearer ' . OPENAI_API_KEY,
            'Content-Type'  => 'application/json',
        ],
        'body' => wp_json_encode( [
            'model'    => $model,
            'messages' => [
                [ 'role' => 'system', 'content' => 'You are a helpful assistant.' ],
                [ 'role' => 'user',   'content' => $message ],
            ],
            'max_tokens' => 500,
        ] ),
    ] );

    if ( is_wp_error( $response ) ) {
        return new WP_REST_Response( [ 'error' => $response->get_error_message() ], 500 );
    }

    $body = json_decode( wp_remote_retrieve_body( $response ), true );
    $text = $body['choices'][0]['message']['content'] ?? '';

    return new WP_REST_Response( [ 'reply' => $text ] );
}

Call it from JavaScript:

const res = await fetch('/wp-json/ai/v1/chat', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ message: 'What is prompt caching?' }),
});
const data = await res.json();
console.log(data.reply);

Using Claude API Instead of ChatGPT

The PHP integration above uses OpenAI’s endpoint. Swapping to the Claude API is a URL and header change — the logic is identical:

<?php
function claude_chat_ask( string $question, string $system = '' ): string {
    $messages = [ [ 'role' => 'user', 'content' => $question ] ];

    $response = wp_remote_post(
        'https://api.anthropic.com/v1/messages',
        [
            'timeout' => 30,
            'headers' => [
                'x-api-key'         => ANTHROPIC_API_KEY,  // define() in wp-config.php
                'anthropic-version' => '2023-06-01',
                'Content-Type'      => 'application/json',
            ],
            'body' => wp_json_encode( [
                'model'      => 'claude-sonnet-5',
                'max_tokens' => 500,
                'system'     => $system,
                'messages'   => $messages,
            ] ),
        ]
    );

    if ( is_wp_error( $response ) ) {
        return 'Error: ' . $response->get_error_message();
    }

    $body = json_decode( wp_remote_retrieve_body( $response ), true );
    return $body['content'][0]['text'] ?? 'No response.';
}

The response structure differs from OpenAI’s: Claude returns content[0].text instead of choices[0].message.content. Everything else — WordPress hooks, session handling, REST API registration — stays the same. The Claude model comparison covers which model to pick for your use case.


Content Generation with a WP-CLI Command

Beyond chatbots, a common WordPress + AI pattern is batch content generation: generate post excerpts, rewrite meta descriptions, or create FAQ sections for existing posts. A WP-CLI command is the cleanest way to run this as a one-time or scheduled job:

<?php
// Register: wp ai generate-excerpts [--limit=<n>]
WP_CLI::add_command( 'ai generate-excerpts', function( $args, $assoc_args ) {
    $limit = (int) ( $assoc_args['limit'] ?? 10 );

    $posts = get_posts( [
        'post_type'      => 'post',
        'post_status'    => 'publish',
        'posts_per_page' => $limit,
        'meta_query'     => [ [
            'key'     => '_ai_excerpt_generated',
            'compare' => 'NOT EXISTS',
        ] ],
    ] );

    foreach ( $posts as $post ) {
        $content = wp_strip_all_tags( $post->post_content );
        $excerpt = ai_chat_ask(
            "Write a 2-sentence SEO excerpt for this article: " . mb_substr( $content, 0, 1500 ),
            'gpt-4o-mini',
            'You are an SEO copywriter. Be concise and keyword-rich.'
        );

        wp_update_post( [ 'ID' => $post->ID, 'post_excerpt' => $excerpt ] );
        update_post_meta( $post->ID, '_ai_excerpt_generated', '1' );

        WP_CLI::success( "Post {$post->ID}: excerpt updated." );
        sleep( 1 );  // rate limit
    }
} );

Run it with:

wp ai generate-excerpts --limit=20

Security Checklist

  • Never expose your API key in JavaScript or public HTML — all API calls go server-side (PHP), not from the browser
  • Always use wp_verify_nonce() on any form that triggers an API call — prevents CSRF abuse
  • Rate-limit your endpoint — a public REST endpoint without auth will be abused; add a simple transient-based counter or use a plugin like WP REST API Rate Limit
  • Sanitize all input with sanitize_text_field() before passing to the API
  • Cap max_tokens — uncapped responses cost more and can time out wp_remote_post()
  • Store API keys in wp-config.php, not in the database or theme files

Summary

  • No-code: AI Engine plugin + [mwai_chatbot] shortcode — live in 5 minutes, zero PHP needed
  • Custom PHP: wp_remote_post() to OpenAI endpoint registered as a shortcode or REST route — full control, no dependencies
  • Conversation history: store messages in PHP session keyed by system prompt hash
  • Headless / JS front end: register a POST /wp-json/ai/v1/chat REST endpoint and call it from JavaScript
  • Claude instead of ChatGPT: change the URL, header (x-api-key), and response path (content[0].text) — logic is identical
  • Batch jobs: WP-CLI + OpenAI is the cleanest pattern for bulk content generation
  • All API calls go server-side — never expose keys in HTML or JavaScript

Related guides: n8n + Claude API for no-code AI automation beyond WordPress, and OpenAI PHP Tutorial for a deeper look at the OpenAI SDK in PHP.


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

Subscribe