jeffersongoncalves/laravel-metrics-plausible
Laravel Metrics Plausible brings Plausible Analytics stats into your Laravel app, with a simple, fluent API to fetch pageviews, visitors, and other metrics for dashboards, reporting, and monitoring. Lightweight, easy to configure.
Install the package with Composer and make sure your app meets the platform requirements: PHP 8.2+ and Laravel 11, 12, or 13.
The package stores credentials through spatie/laravel-settings, seeded from environment variables. Add these to .env before using it:
PLAUSIBLE_API_KEY=your-api-key
PLAUSIBLE_SITE_ID=example.com
PLAUSIBLE_BASE_URL=https://plausible.io
For Plausible Cloud, PLAUSIBLE_BASE_URL should point at https://plausible.io. For self-hosted Community Edition, point it at your own Plausible host. Run the settings migrations/publishing steps required by the package and by spatie/laravel-settings, then clear config and settings caches if values do not appear to update.
The first practical use case is a dashboard stat: pull aggregate pageviews for the last 30 days and compare with the previous period.
use Plausible\Facades\Plausible;
use Plausible\Enums\DateRange;
use Plausible\Enums\Metric;
$current = Plausible::aggregate(
metrics: [Metric::Visitors, Metric::Pageviews],
dateRange: DateRange::Last30Days,
);
$previous = Plausible::aggregate(
metrics: [Metric::Visitors, Metric::Pageviews],
dateRange: DateRange::Previous30Days,
);
Start by exploring the Plausible facade for ready-made reports, then move to StatsQuery when you need filters, dimensions, ordering, pagination, or imports. Use the sites and goals methods for administrative tasks rather than analytics reporting.
Use the facade for standard reports. The ready-made methods cover the common reports most apps need: pages, entryPages, exitPages, sources, channels, utm, countries, regions, cities, devices, browsers, operatingSystems, goalConversions, customEvents, and realtimeVisitors. These are best for dashboard widgets, scheduled summaries, and admin panels where the report shape is already known.
Use StatsQuery for anything custom. Build queries with explicit metrics, dimensions, filters, ordering, pagination, imports, and time labels. This is the right layer for reusable reporting services rather than scattering facade calls across controllers.
use Plausible\StatsQuery;
$query = StatsQuery::make()
->metrics(['visitors', 'pageviews', 'bounce_rate'])
->dimensions(['event:page'])
->dateRange('30d')
->orderBy('visitors', 'desc')
->limit(10);
Prefer the Metric, Dimension, DateRange, and GoalType enums over raw strings where the package exposes them. Enums give you IDE completion, reduce typos in metric and dimension names, and make refactors safer.
Wrap reporting in a service or repository. Controllers and Livewire components should not know about Plausible response shapes. Create a small AnalyticsService that returns arrays or DTOs your views expect. This also gives you one place to add caching and exception handling.
Cache aggressively for dashboards. Analytics data does not need to be fresh on every page load. Cache aggregate and timeseries results for 5–15 minutes, and use shorter TTLs only for realtimeVisitors. Include the site ID, date range, metrics, dimensions, and filters in the cache key so different reports do not collide.
Poll realtime visitors from a dedicated endpoint. Do not call realtimeVisitors inside a normal page render if the page is frequently refreshed. Expose a small JSON endpoint and poll it from the frontend, with a reasonable interval such as 30–60 seconds.
Handle typed exceptions at the boundary. Catch AuthenticationException for 401/403 cases, including missing API key or site ID, and surface a configuration error to administrators rather than a generic 500. Catch RateLimitException for 429 responses and either back off or serve stale cached data. Let the base PlausibleException bubble up only where a generic analytics failure is acceptable.
use Plausible\Exceptions\AuthenticationException;
use Plausible\Exceptions\RateLimitException;
use Plausible\Exceptions\PlausibleException;
try {
$stats = Plausible::timeseries(...);
} catch (AuthenticationException $e) {
report($e);
$stats = [];
} catch (RateLimitException $e) {
$stats = Cache::get($cacheKey, []);
} catch (PlausibleException $e) {
report($e);
$stats = [];
}
Use sites and goals management for provisioning. createSite, deleteSite, createEventGoal, createPageGoal, and deleteGoal are useful in onboarding flows, multi-tenant setups, or internal admin tooling. Keep destructive operations such as deleteSite and deleteGoal behind explicit confirmation and authorization.
Schedule periodic reports. Use Laravel's scheduler for daily or weekly summaries. Combine aggregate or breakdown with a notification or email, and cache the result so a scheduler retry does not hammer the API.
Validate site IDs from your own domain model. Treat PLAUSIBLE_SITE_ID as a default, not a universal constant, if you manage multiple sites. Pass site IDs explicitly from your own tenant or site model where the package allows it.
The package spans two API versions. Stats endpoints use the Plausible Stats API v2, while sites and goals use v1 endpoints. Do not assume response shapes or filter syntax are identical across both areas. Check the package source or tests when moving between reporting and management calls.
Self-hosted setups need PLAUSIBLE_BASE_URL. The default may target Plausible Cloud. If you use Community Edition, set the base URL explicitly and confirm whether your instance exposes the same API endpoints and version support.
Settings are cached by spatie/laravel-settings. Changing PLAUSIBLE_API_KEY, PLAUSIBLE_SITE_ID, or PLAUSIBLE_BASE_URL in .env may not take effect until settings and config caches are cleared. This is a common source of “it still says unauthorized” confusion in local and deployed environments.
A missing API key or site ID surfaces as AuthenticationException. The release notes explicitly group missing key and missing site ID with 401/403 responses. If you see an authentication exception, check configuration before assuming the key is invalid.
Rate limits are real. Handle RateLimitException deliberately. Avoid tight loops that call the API for each row or each site. Batch where possible and cache results. For multi-site dashboards, prefer showing cached data with a refresh action over blocking the request on many API calls.
Do not confuse frontend Plausible tracking with this package. This is a server-side Stats API client. Your browser tracking script and goals still need to be installed and configured separately. This package reads data and manages sites/goals; it does not instrument pageviews.
Date ranges and time zones matter. Analytics totals can differ from what you see in the Plausible UI if date ranges, time zones, or “last 30 days” semantics differ. Be explicit about date ranges and use the DateRange enum where available. When comparing periods, request both ranges rather than computing deltas from a single dataset.
Pagination and limits can hide data. Breakdowns and custom queries may return only a page or a limited number of rows. If totals do not add up, check whether you need pagination or a higher limit. Use ordering deliberately so pagination is stable.
Enum values should match the Plausible API, not your UI labels. Use Metric and Dimension enums for API calls, and map them to human-readable labels in your presentation layer. Do not send display labels such as “Unique visitors” to the API.
Goal creation is type-specific. Use createEventGoal for custom event goals and createPageGoal for page-based goals, and use the GoalType enum where applicable. Deleting and recreating goals can affect historical goal conversion data, so prefer creating goals once and managing them idempotently.
Treat destructive site and goal methods as privileged operations. deleteSite and deleteGoal should never be reachable from unauthenticated routes. Log who performed the action and consider a soft-delete or archive pattern in your own application before calling the API.
Testing analytics code requires isolation. Since the release notes do not mention a fake or sandbox client, avoid hitting the real API in tests. Abstract Plausible calls behind your own service interface and bind a fake implementation in the container during tests. This also makes it easy to simulate RateLimitException and AuthenticationException paths.
Pin and upgrade deliberately. This is a first release, so API surface may still evolve. Pin the version in composer.json, review changelogs before upgrading, and keep a small integration test or smoke command that calls a low-cost endpoint such as aggregate for a known site.
How can I help you explore Laravel packages today?