NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Plausible for Laravel

Querying stats

The fluent builder is the recommended way to read stats. Start it with for($site) — or query() to use the configured default site — chain options, and finish with a terminal call that returns a typed result, so you stop hand-parsing JSON. Enums give type-safe metrics, periods, intervals and properties; plain strings still work everywhere.

use RoundlyConsulting\Plausible\Facades\Plausible;

Plausible::stats()->for('my-site.com');  // a fluent PendingStatsQuery for one site
Plausible::stats()->query();             // the same, for the configured PLAUSIBLE_SITE

Realtime visitors

$live = Plausible::stats()->for('my-site.com')->realtime(); // int — visitors on the site right now

Aggregate

aggregate() returns an AggregateResult. metric() gives a metric’s value (0 when absent), change() the comparison delta when compare() was on, and has() whether the metric is in the response:

use RoundlyConsulting\Plausible\Enums\Metric;

// Aggregate metrics for a custom range, compared with the previous period
$result = Plausible::stats()
    ->for('my-site.com')
    ->metrics(Metric::Visitors, Metric::Pageviews, Metric::BounceRate)
    ->between(now()->subDays(7), now())           // auto-sets period=custom
    ->compare()
    ->aggregate();                                // -> AggregateResult

$visitors = $result->metric(Metric::Visitors);    // typed int
$change   = $result->change(Metric::Visitors);    // comparison delta (or null)
$result->has(Metric::BounceRate);                 // bool
$result->results;                                 // ['visitors' => ['value' => 2001, 'change' => 30], …]
$raw      = $result->response();                  // escape hatch to the Response

Timeseries

timeseries() returns a TimeseriesResult of TimeseriesPoint objects, each with its date as Carbon and its metrics. Plausible’s v1 timeseries endpoint takes only the date (alias day) and month intervals; minute, hour or week throw UnsupportedRequest before anything is sent — for hourly or weekly buckets use run() with Dimension::TimeHour or Dimension::TimeWeek (Stats v2 queries):

use RoundlyConsulting\Plausible\Enums\Interval;
use RoundlyConsulting\Plausible\Enums\Metric;
use RoundlyConsulting\Plausible\Enums\Period;

// Day-by-day trend you can chart
$points = Plausible::stats()
    ->for('my-site.com')
    ->metrics(Metric::Visitors, Metric::Pageviews)
    ->period(Period::ThirtyDays)
    ->interval(Interval::Date)                    // date or month — hourly or weekly buckets go through run()
    ->timeseries()                                // -> TimeseriesResult
    ->points();                                   // list<TimeseriesPoint>

foreach ($points as $point) {
    $point->date;                                 // Carbon
    $point->metric(Metric::Visitors);             // int|float
}

Breakdown

breakdown() needs a property — pass it to the terminal or set it earlier with property(); without one it throws MissingConfiguration naming the missing property. Rows keep Plausible’s response keys, so read the dimension with get() (page for event:page) and metrics with metric():

use RoundlyConsulting\Plausible\Enums\Metric;
use RoundlyConsulting\Plausible\Enums\Property;

// Top pages, ranked and paginated
$rows = Plausible::stats()
    ->for('my-site.com')
    ->metrics(Metric::Visitors)
    ->last30Days()
    ->perPage(20)                                 // default 100
    ->breakdown(Property::EventPage)              // -> BreakdownResult
    ->rows();                                     // list<BreakdownRow>

foreach ($rows as $row) {
    $row->get('page');                            // "/pricing" — the dimension, by its response key
    $row->metric(Metric::Visitors);               // int|float
}

// Or set the property first
Plausible::stats()->for('my-site.com')->property(Property::VisitSource)->breakdown();

Periods and date ranges

Skip the enum for the common ranges with named shortcuts, or pass any Period or an explicit range:

use RoundlyConsulting\Plausible\Enums\Period;

Plausible::stats()->for('my-site.com')->today()->aggregate();
Plausible::stats()->for('my-site.com')->yesterday()->aggregate();      // custom range
Plausible::stats()->for('my-site.com')->last7Days()->timeseries();
Plausible::stats()->for('my-site.com')->last30Days()->aggregate();
Plausible::stats()->for('my-site.com')->last6Months()->timeseries();
Plausible::stats()->for('my-site.com')->last12Months()->timeseries();
Plausible::stats()->for('my-site.com')->thisMonth()->aggregate();
Plausible::stats()->for('my-site.com')->lastMonth()->aggregate();      // custom range
Plausible::stats()->for('my-site.com')->thisYear()->aggregate();     // v1: custom range, 1 January to today
Plausible::stats()->for('my-site.com')->allTime()->run();            // run() only — v1 terminals throw UnsupportedRequest

// Any Period, or an explicit range
Plausible::stats()->for('my-site.com')->period(Period::SixMonths)->aggregate();
Plausible::stats()->for('my-site.com')->between(now()->subDays(14), now())->aggregate();
Plausible::stats()->for('my-site.com')->since(now()->startOfYear())->aggregate();   // year to date — through today
ShortcutSends
today()period=day
yesterday()custom range — yesterday, start to end of day
last7Days()period=7d
last30Days()period=30d
last6Months()period=6mo
last12Months()period=12mo
thisMonth()period=month
lastMonth()custom range — the previous calendar month
thisYear()period=year
allTime()period=all

between(Carbon $start, Carbon $end) and since(Carbon $start) both set period=custom with a closed range for you: the v1 terminals send date=start,end and run() sends date_range: [start, end]. since() runs through today, in the app’s timezone, fixed when you call it. A later period() or shortcut such as last7Days() replaces an earlier between(), since(), yesterday() or lastMonth() range. The v1 terminals have no year or all-time period: thisYear() goes to them as a custom range from 1 January to today, while allTime() throws UnsupportedRequest pointing to run(), which sends both unchanged. Unset options fall back to the builder defaults: the visitors metric, a 30d period, a date interval, page 1 and 100 rows per page.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.