Tutorials 12 min read 2026-07-20

How to Integrate OpenAI API with Laravel in 2026 (Step-by-Step with Code)

W

Websicon Team

CRM Development Experts

  • What this guide covers:
  • ✓ Installing the OpenAI PHP package in Laravel
  • ✓ Chat completions with conversation history
  • ✓ Streaming responses to the browser in real time
  • ✓ Running AI calls as queued background jobs
  • ✓ Embeddings and semantic search (RAG pattern)
  • ✓ Caching, rate limiting, and cost control

Prerequisites

You'll need a Laravel 10 or 11 project, PHP 8.1+, Composer, an OpenAI API key (from platform.openai.com), and optionally Redis for caching and queues. Basic Laravel knowledge is assumed.

Step 1: Install the OpenAI Laravel Package

The openai-php/laravel package wraps the official OpenAI PHP client with Laravel service provider integration:

composer require openai-php/laravel

Publish the config file:

php artisan vendor:publish --provider="OpenAI\Laravel\ServiceProvider"

Add your API key to .env:

OPENAI_API_KEY=sk-your-key-here
OPENAI_REQUEST_TIMEOUT=60

The package auto-discovers via Laravel's package discovery — no manual service provider registration needed.

Step 2: Basic Chat Completion

Inject the OpenAI client via the facade or dependency injection. Here's a simple controller method:

use OpenAI\Laravel\Facades\OpenAI;

class AiController extends Controller
{
    public function chat(Request $request)
    {
        $response = OpenAI::chat()->create([
            'model' => 'gpt-4o',
            'messages' => [
                ['role' => 'system', 'content' => 'You are a helpful business assistant.'],
                ['role' => 'user', 'content' => $request->input('message')],
            ],
            'max_tokens' => 500,
            'temperature' => 0.7,
        ]);

        return response()->json([
            'reply' => $response->choices[0]->message->content,
            'tokens_used' => $response->usage->totalTokens,
        ]);
    }
}

Register the route in routes/api.php:

Route::post('/chat', [AiController::class, 'chat'])->middleware('auth:sanctum');

Step 3: Maintain Conversation History

For a stateful chat experience, store message history in the user's session or a database table. Here's a session-based approach:

public function chat(Request $request)
{
    $history = session('chat_history', [
        ['role' => 'system', 'content' => 'You are a helpful assistant for our SaaS platform.']
    ]);

    $history[] = ['role' => 'user', 'content' => $request->input('message')];

    $response = OpenAI::chat()->create([
        'model' => 'gpt-4o',
        'messages' => $history,
        'max_tokens' => 500,
    ]);

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

    // Keep last 20 messages to control token cost
    if (count($history) > 21) {
        $history = [$history[0], ...array_slice($history, -20)];
    }

    session(['chat_history' => $history]);

    return response()->json(['reply' => $reply]);
}

Step 4: Stream Responses to the Browser

Streaming makes long AI responses feel instant — the user sees text as it generates rather than waiting for the complete response. Use Laravel's StreamedResponse:

use Symfony\Component\HttpFoundation\StreamedResponse;

public function stream(Request $request): StreamedResponse
{
    return response()->stream(function () use ($request) {
        $stream = OpenAI::chat()->createStreamed([
            'model' => 'gpt-4o',
            'messages' => [
                ['role' => 'user', 'content' => $request->input('message')]
            ],
        ]);

        foreach ($stream as $response) {
            $text = $response->choices[0]->delta->content ?? '';
            if ($text !== '') {
                echo "data: " . json_encode(['text' => $text]) . "\n\n";
                ob_flush();
                flush();
            }
        }
        echo "data: [DONE]\n\n";
    }, 200, [
        'Content-Type' => 'text/event-stream',
        'Cache-Control' => 'no-cache',
        'X-Accel-Buffering' => 'no', // Disable Nginx buffering
    ]);
}

On the frontend, consume with the EventSource API or a fetch stream reader. The X-Accel-Buffering: no header is important if your app sits behind Nginx — without it, Nginx buffers the response and streaming appears broken.

Step 5: Queue Long AI Jobs

For AI tasks that don't need real-time responses — document analysis, batch classification, email drafting — dispatch to a Laravel queue job to avoid HTTP timeouts and improve UX:

// app/Jobs/GenerateAiSummary.php
class GenerateAiSummary implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $tries = 3;
    public int $timeout = 120;

    public function __construct(
        private readonly int $documentId,
        private readonly int $userId
    ) {}

    public function handle(): void
    {
        $document = Document::findOrFail($this->documentId)

        $response = OpenAI::chat()->create([
            'model' => 'gpt-4o',
            'messages' => [
                ['role' => 'system', 'content' => 'Summarize the following document in 3 bullet points.'],
                ['role' => 'user', 'content' => $document->content],
            ],
            'max_tokens' => 300,
        ]);

        $document->update([
            'ai_summary' => $response->choices[0]->message->content,
            'summarized_at' => now(),
        ]);

        // Notify user via broadcast or notification
        $this->userId && User::find($this->userId)?->notify(new SummaryReady($document));
    }
}

// Dispatch from controller
GenerateAiSummary::dispatch($document->id, auth()->id());

Step 6: Embeddings and Semantic Search (RAG)

Retrieval-Augmented Generation lets your AI answer questions from your own data — documentation, knowledge base, CRM records. First, embed your documents:

// Store embeddings when a document is created/updated
public function embed(Document $document): void
{
    $response = OpenAI::embeddings()->create([
        'model' => 'text-embedding-3-small',
        'input' => $document->content,
    ]);

    $document->update([
        'embedding' => json_encode($response->embeddings[0]->embedding)
    ]);
}

At query time, embed the user's question and find the most similar documents using cosine similarity (or use pgvector in PostgreSQL for efficient vector search):

public function search(string $query): array
{
    $queryEmbedding = OpenAI::embeddings()->create([
        'model' => 'text-embedding-3-small',
        'input' => $query,
    ])->embeddings[0]->embedding;

    // Fetch top matches — with pgvector this is a single SQL query
    $docs = Document::all()->sortByDesc(function ($doc) use ($queryEmbedding) {
        return $this->cosineSimilarity($queryEmbedding, json_decode($doc->embedding, true))
    })->take(3);

    // Feed context to GPT
    $context = $docs->pluck('content')->join("\n\n---\n\n");

    $response = OpenAI::chat()->create([
        'model' => 'gpt-4o',
        'messages' => [
            ['role' => 'system', 'content' => "Answer using only this context:\n\n{$context}"],
            ['role' => 'user', 'content' => $query],
        ],
    ]);

    return [
        'answer' => $response->choices[0]->message->content,
        'sources' => $docs->pluck('title'),
    ];
}

Step 7: Caching and Cost Control

OpenAI API costs can grow quickly without discipline. These patterns keep costs predictable:

  • Cache identical prompts: Use Cache::remember() with a hash of the prompt as the key. Identical support questions get cached answers without hitting the API again.
  • Use gpt-4o-mini for simple tasks: At roughly 15× cheaper than gpt-4o, mini handles classification, sentiment, extraction, and short summaries perfectly. Reserve gpt-4o for complex reasoning.
  • Log token usage per request: Store $response->usage->totalTokens in a database table with user ID and timestamp. Query weekly to spot unexpected spikes.
  • Rate limit per user: Laravel's built-in rate limiter can cap AI requests per user per hour — prevents a single user burning your monthly budget.
// In RouteServiceProvider or routes/api.php
RateLimiter::for('ai', function (Request $request) {
    return Limit::perHour(20)->by($request->user()?->id);
});

Route::middleware(['auth:sanctum', 'throttle:ai'])
    ->post('/chat', [AiController::class, 'chat']);

Handling Errors Gracefully

OpenAI API calls fail — rate limits, timeouts, and model unavailability happen in production. Wrap calls in try/catch and return user-friendly messages:

try {
    $response = OpenAI::chat()->create([...]);
} catch (\OpenAI\Exceptions\ErrorException $e) {
    Log::error('OpenAI API error', ['message' => $e->getMessage()]);
    return response()->json(['error' => 'AI is temporarily unavailable. Please try again.'], 503);
} catch (\OpenAI\Exceptions\TransporterException $e) {
    Log::error('OpenAI timeout', ['message' => $e->getMessage()]);
    return response()->json(['error' => 'Request timed out. Please try again.'], 504);
}

Production Checklist

  • API key stored in .env, never committed to version control
  • Rate limiting per user enabled on AI routes
  • Token usage logged per request for cost monitoring
  • Long AI jobs dispatched to queue (not synchronous HTTP calls)
  • Streaming enabled for real-time response endpoints
  • Error handling covers API rate limits and timeouts
  • Cache layer (Redis) for repeated or near-identical prompts

For more on embedding AI capabilities into existing CRM workflows, see our guide on AI-powered Perfex CRM features — many of the same patterns apply.

Need OpenAI integrated into your Laravel application?

Websicon builds production AI features for Laravel apps — chat interfaces, document analysis, semantic search, and AI-powered automation. Serving US & UK clients. Projects from $500 USD.

Get a free AI integration quote →

Building something more complex — multi-agent AI, RAG over large document sets, or AI features inside a CRM? Websicon's AI integration team can architect and build the full solution.

#Laravel#OpenAI#AI#PHP

Ready to get started?

Let Websicon build your perfect CRM solution

Free consultation · Projects from $500 USD · No long-term contracts · EST/PST/GMT timezone support

Get Free Consultation