Building AI Features in Laravel with the Claude API
LaravelAIClaude APISaaS

Building AI Features in Laravel with the Claude API

How I integrated Claude API into a SaaS platform to automate repetitive data entry workflows — and what I got wrong before I got it right.

Mokammel Tanvir

Mokammel Tanvir

Software Engineer

The Problem Worth Solving

At Smart Provider LLC, our operators were spending hours every day copy-pasting information between systems. Same data, different screens, over and over. Nobody enjoys that kind of work, and it's exactly the kind of thing an LLM is genuinely good at replacing.

So I built the automation layer. Here's what that actually looked like in practice — including what I got wrong first.

Calling Claude from Laravel

I used Laravel's HTTP client directly. No third-party SDK, no extra dependency for straightforward use cases.

$response = Http::withHeaders([
    'x-api-key'         => config('services.claude.key'),
    'anthropic-version' => '2023-06-01',
    'content-type'      => 'application/json',
])->post('https://api.anthropic.com/v1/messages', [
    'model'      => 'claude-sonnet-4-6',
    'max_tokens' => 1024,
    'system'     => 'You are a data extraction assistant. Return only valid JSON. If a field cannot be found, return null for that field.',
    'messages'   => [
        ['role' => 'user', 'content' => $prompt],
    ],
]);

$result = $response->json('content.0.text');

Simple enough. The real complexity is in everything around the API call.

What I Got Wrong First

Version one called the Claude API synchronously inside a controller. It worked fine — until a slow response held up the entire request cycle and users started hitting timeouts.

The fix is obvious in hindsight: AI calls belong in queues.

// Wrong — blocks the web request
public function process(Request $request): JsonResponse
{
    $result = $this->claude->extract($request->input('data'));
    return response()->json($result);
}

// Right — dispatch and return immediately
public function process(Request $request): JsonResponse
{
    ProcessAIExtraction::dispatch($request->input('data'));
    return response()->json(['status' => 'processing']);
}

Three Rules That Stuck

Queue every AI call. A 3-second Claude response in a synchronous web request is a user experience problem. Dispatch a job, return a job ID, let the frontend poll or use websockets for the result.

Cache identical prompts. Same input should hit the cache, not the API. A quick MD5 hash on the prompt content is all you need for a cache key.

$cacheKey = 'claude_extraction_' . md5($prompt);

return Cache::remember($cacheKey, now()->addHours(24), function () use ($prompt) {
    return $this->callClaude($prompt);
});

Parse defensively. LLMs don't always return exactly what you asked for. Validate the response shape before you try to use it. When it fails, log the raw output and flag the record for manual review — don't silently discard the failure.

$data = json_decode($result, true);

if (!isset($data['customer_name']) || !array_key_exists('address', $data)) {
    Log::warning('Unexpected Claude response', ['raw' => $result]);
    throw new AIExtractionException('Invalid response structure');
}

The Outcome

After the automation was stable in production: roughly 60% reduction in manual operator work. The operators still review the AI output — they're not removed from the loop. The mindless copy-paste is just gone.

That's the right framing for AI in a business context. Augment the human, don't try to replace them entirely.

All Posts
Mokammel Tanvir

Mokammel Tanvir

Full-Stack Engineer · Laravel · Vue · WordPress · AI

Building web applications with Laravel, Vue/Nuxt, and WordPress — SaaS platforms, REST APIs, and AI-integrated workflows. Open to remote and hybrid opportunities.