Understand business needs objectives
Propose tech stack, timelines architecture
Sprint-based development with regular demos
Final testing and go-live with complete documentation
Ongoing maintenance, upgrades scale-up
Understand business needs objectives
Propose tech stack, timelines architecture
Sprint-based development with regular demos
Final testing and go-live with complete documentation
Ongoing maintenance, upgrades scale-up

We help define the ideal stack
Our architects suggest best options
With estimation, milestones & approach
Flexible delivery model: fixed or dedicated

Get expert tech consultation at no cost.
Get Free Tech AdviceBrief us on your needs (tech stack, duration, etc.)
Receive matched profiles within 24–48 hours
Interview the candidates and choose the right fit
Kick off the project seamlessly with our support

Adding an AI chat box or a summarizer to a Laravel app takes an afternoon. Keeping it affordable once real people use it takes more planning. Large language model APIs bill by the token, so a single long prompt, a retry loop, or one over-enthusiastic user can burn through a monthly budget in a few hours.
Traditional API protection only counts requests. With LLMs, the size of each request matters just as much as the number of requests. This guide walks through a practical, layered setup for rate limiting in Laravel and cost caps for LLM features, using tools the framework already ships with. At Appeak Technology, we apply the same layered approach when we build AI features for client products.
It helps to separate the two ideas before writing any code.
A rate limit controls speed. It answers the question: how many requests can this user or IP send in a minute or a day? It protects you from bursts, bots, and accidental loops.
A cost cap controls spend. It answers the question: how much money are we willing to let this user, or the whole app, consume today? It protects your budget even when every request is technically within the rate limit.
You need both. A user sending only five requests a minute can still cost you a lot if each request carries a huge document. Think of the rate limiter as a turnstile and the cost cap as a spending account.
Laravel's built-in rate limiter works well for this. Define a named limiter in your AppServiceProvider and give different tiers different limits:
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
public function boot(): void
{
RateLimiter::for('ai', function (Request $request) {
$user = $request->user();
if (! $user) {
return Limit::perMinute(3)->by($request->ip());
}
return [
Limit::perMinute($user->isPaid() ? 20 : 5)->by('ai-min:'.$user->id),
Limit::perDay($user->isPaid() ? 500 : 30)->by('ai-day:'.$user->id),
];
});
}
Then attach it to the route that talks to the model:
Route::post('/assistant', AssistantController::class)
->middleware(['auth', 'throttle:ai']);
Returning an array lets you stack a short window and a long window together. When a limit is hit, Laravel responds with a 429 status and a Retry-After header, so your front end can show a friendly countdown instead of a vague error.
One production detail matters here: use a shared cache driver such as Redis. If each server keeps its own file-based counters, a user can multiply their allowance by the number of servers behind your load balancer.
Request counts are blunt. To cap cost, record what each call actually used. Most LLM providers return input and output token counts with every response, which gives you everything you need.
A small service class can convert those tokens into spend and keep daily totals. To avoid floating-point rounding problems, store money as whole micro-dollars (one dollar equals 1,000,000):
spent($this->userKey($userId)) < $this->userDailyLimit
&& $this->spent($this->globalKey()) < $this->globalDailyLimit;
}
public function record(int $userId, int $inputTokens, int $outputTokens): void
{
$cost = intdiv($inputTokens * self::INPUT_PER_1K, 1000)
+ intdiv($outputTokens * self::OUTPUT_PER_1K, 1000);
$this->add($this->userKey($userId), $cost);
$this->add($this->globalKey(), $cost);
}
private function add(string $key, int $amount): void
{
Cache::add($key, 0, now()->endOfDay());
Cache::increment($key, $amount);
}
private function spent(string $key): int
{
return (int) Cache::get($key, 0);
}
private function userKey(int $id): string
{
return 'ai-spend:user:'.$id.':'.now()->toDateString();
}
private function globalKey(): string
{
return 'ai-spend:global:'.now()->toDateString();
}
}
The Cache::add call creates the counter only if it does not exist yet and sets it to expire at midnight, so budgets reset daily without a scheduled job. Cache::increment is atomic on Redis, which keeps totals accurate when many requests arrive at once.
Now wire the budget into your controller. Check first, call second, record third:
public function __invoke(AssistantRequest $request, AiBudget $budget)
{
$userId = $request->user()->id;
if (! $budget->allows($userId)) {
return response()->json([
'message' => 'Daily AI usage limit reached. Please try again tomorrow.',
], 429);
}
// $this->llm is your own thin wrapper around the provider SDK.
$result = $this->llm->chat(
$request->validated()['message'],
maxTokens: 500
);
$budget->record($userId, $result->inputTokens, $result->outputTokens);
return response()->json(['reply' => $result->text]);
}
'message' => ['required', 'string', 'max:2000']
so nobody can paste an entire book into the prompt.
use Illuminate\Queue\Middleware\RateLimited;
use Illuminate\Support\Facades\RateLimiter;
// In AppServiceProvider::boot()
RateLimiter::for('ai-jobs', fn (object $job) => Limit::perMinute(30));
// In the job class
public function middleware(): array
{
return [new RateLimited('ai-jobs')];
}
Limits only help if users understand them and you can see them working.
Retry-After header makes this easy.config('ai.enabled') checkIf you want to understand why prototypes often fail once real traffic arrives, our article on why Laravel AI demos break in production covers the wider picture.
Safe LLM features in Laravel come down to layers. Rate limiters control how fast people can call the model, token-based budgets control how much they can spend, request size limits keep each call predictable, and queue middleware plus monitoring protect the background work. None of it needs a new package; Laravel’s limiter, cache, and middleware cover the essentials.
If you are planning an AI feature and want it designed with cost control from day one, the team at Appeak Technology can help you build it so it scales without surprises.
Return a 429 response with a clear message and a retry time. Optionally offer an upgrade path, and never let the request silently fail or reach the model anyway.