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
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.

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.
