The Matomo API provides systematic entry to research records. Using the API, users can create custom-designed reviews and dashboards specific to their wishes. This flexibility is vital for companies with precise reporting needs that are not met using popular analytics interfaces.
The Analytics API allows for the automated retrieval of reports such as
Data Categories:
Website List:
Date and Period:
Data Formats:
Many users and developers are using the Matomo APIs to provide customized reports to their clients.
This blog will guide you through the process of fetching data from the Matomo API using PHP. The retrieved data will be formatted into a PDF file, which will then be attached to an email.
Before starting our project, let us discuss some basic requirements, including installing libraries that will assist us in achieving the requirements.
First of all, we used the TCPDF external library, a powerful tool for formatting data into PDFs. Additionally, we integrated the PHPMailer library, which facilitates the sending of emails with ease.
error_reporting(E_ALL); ini_set(‘display_errors’, 1); require_once(‘TCPDF/tcpdf.php’); require_once(‘PHPMailer/src/PHPMailer.php’); require_once(‘PHPMailer/src/Exception.php’); require_once(‘PHPMailer/src/SMTP.php’); use TCPDF; use PHPMailer\PHPMailer\PHPMailer; use PHPMailer\PHPMailer\SMTP; |
Also add the error handling lines during the development phase to catch and address any issues in the code.
I assume you already have the Matomo instance URL and AUTH_TOKEN. If you haven’t already, go to the administration section of your matomo and look for “security” under personal option. You can create your authentication token there
In this tutorial, basically our focus will be on retrieving data related to countries. However, if you have a different perspective or specific requirements, feel free to explore other APIs. To do so, simply check the API option under the Export or Platform section of Administration.
Let’s create a function and make use of the matomo API to retrieve our data.
$matomo = ‘https://matomo.com’; $auth = ‘TOKEN’; $idsite = 1; function countryData($matomo, $auth, $idsite){ $limit = 1000; $offset = 0; do { $apiUrl = $matomo . ‘/index.php?module=API&method=UserCountry.getCountry&idSite=’.$idsite.’&period=week&date=yesterday&format=JSON&token_auth=’ . $auth . ‘&force_api_session=1&showColumns=label,sum_daily_nb_uniq_visitors,nb_visits,nb_actions,goals,bounce_count&filter_limit=’ . $limit . ‘&filter_offset=’ . $offset; $response = file_get_contents($apiUrl); if ($response === FALSE) { die(‘Error fetching data from API’); } $data = json_decode($response, true); $offset += $limit; } while (!empty($data)); return $data; } countryData($matomo, $auth, $idsite); |
We’re utilizing the UserCountry API and specifying the essential columns for our report to streamline the API requests and reduce load times. Pagination is implemented through the use of filter_limit and filter_offset parameters. For a comprehensive understanding of API parameters, you can
By the use of the showColumns parameter in API we are capable of having the subsequent KPIs for our report.
$countriesData = []; // api request here foreach ($data as $countryData) { if (isset($countryData[‘goals’][‘idgoal=9’]) && $countryData[‘goals’][‘idgoal=9’][‘nb_conversions’] > 0) { $countryLabel = $countryData[‘label’]; $currentConversions = $countryData[‘goals’][‘idgoal=9’][‘nb_conversions’]; $countriesData[] = [ ‘label’ => $countryLabel, ‘sum_daily_nb_uniq_visitors’ => $countryData[‘sum_daily_nb_uniq_visitors’], ‘nb_visits’ => $countryData[‘nb_visits’], ‘nb_actions’ => $countryData[‘nb_actions’], ‘bounce_rate’ => isset($countryData[‘nb_visits’]) && $countryData[‘nb_visits’] > 0 ? ($countryData[‘bounce_count’] / $countryData[‘nb_visits’]) * 100 : 0, ‘conversions’ => $currentConversions, ]; } } |
I have initialized the array to store all country related data in it. The goal ID for booking (conversions) has been set to 9, aiming to retrieve data specifically related to the countries where conversions occurred. For obtaining the bounce rate, you can utilize the following columns obtained through the API
To gather data for Evolution, I utilized the same API for the previous week through the following function. The focus was solely on obtaining the evolution data for conversions from the previous period.
function previousData($matomo, $auth, $idsite) { $limit = 1000; $offset = 0; $previous_data = []; $previousWeek = (new DateTime())->modify(‘-1 week’)->modify(‘+1 days’)->format(‘Y-m-d’); do { $apiUrl = $matomo . ‘/index.php?module=API&method=UserCountry.getCountry&idSite=’.$idsite.’&period=week&date=’ . $previousWeek . ‘&format=JSON&token_auth=’ . $auth . ‘&force_api_session=1&showColumns=label,goals&filter_limit=’ . $limit . ‘&filter_offset=’ . $offset; $response = file_get_contents($apiUrl); if ($response === FALSE) { die(‘Error fetching data from API’); } $data = json_decode($response, true); foreach ($data as $countryData) { $countryName = $countryData[‘label’]; if (isset($countryData[‘goals’][‘idgoal=9’]) && $countryData[‘goals’][‘idgoal=9’][‘nb_conversions’] > 0) { $conversions = $countryData[‘goals’][‘idgoal=9’][‘nb_conversions’]; $previous_data[$countryName] = $conversions; } } $offset += $limit; } while (!empty($data)); return $previous_data; } |
Call the previousData function to calculate the evolution in the countryData function.
function countryData($matomo, $auth) { $previous_data = previousData($matomo, $auth); // Remaining code … } |
Now, by implementing the following changes, we will incorporate the evolution data for conversions into our report. We will also define the color scheme for evolution to enhance its clarity and visual representation.
foreach ($data as $countryData) { if (isset($countryData[‘goals’][‘idgoal=9’]) && $countryData[‘goals’][‘idgoal=9’][‘nb_conversions’] > 0) { $countryLabel = $countryData[‘label’]; $currentConversions = $countryData[‘goals’][‘idgoal=9’][‘nb_conversions’]; $previousConversions = isset($previous_data[$countryLabel]) ? $previous_data[$countryLabel] : 0; $evolution = ($currentConversions – $previousConversions) / ($previousConversions > 0 ? $previousConversions : 1) * 100; $valueColor = $evolution >= 0 ? ‘green’ : ‘red’; $countriesData[] = [ ‘label’ => $countryLabel, ‘sum_daily_nb_uniq_visitors’ => $countryData[‘sum_daily_nb_uniq_visitors’], ‘nb_visits’ => $countryData[‘nb_visits’], ‘nb_actions’ => $countryData[‘nb_actions’], ‘bounce_rate’ => isset($countryData[‘nb_visits’]) && $countryData[‘nb_visits’] > 0 ? ($countryData[‘bounce_count’] / $countryData[‘nb_visits’]) * 100 : 0, ‘conversions’ => $currentConversions, ‘evolution’ => $evolution, ‘valueColor’ => $valueColor, ]; } } |
Okay, this code will output all of the data in array format. Let’s give it a tabular format. To achieve this, I need to use the $htmlContent variable to accumulate the HTML content, including the dynamically generated table rows. The .= operator is used for string concatenation, allowing you to append content to an existing string.
$today = date(‘Y-m-d’); $startDate = date(‘Y-m-d’, strtotime(‘monday this week’, strtotime($today))); $endDate = date(‘Y-m-d’, strtotime(‘sunday this week’, strtotime($today))); $weekRange = ‘From ‘ . $weekStart . ‘ To ‘ . $yesterday; $htmlContent = ‘<h3 style=”text-align: center; margin: 10px !important;”>’ . $weekRange . ‘</h3>’; $htmlContent .= ‘<h2>Country-wise Conversion Insights</h2>’; $htmlContent .= ‘<table border=”1″>’; $htmlContent .= ‘<tr style=”color:white; background-color:black; border: 2px solid white; !important”>’; $htmlContent .= ‘<th>Country</th>’; $htmlContent .= ‘<th>Unique Visits</th>’; $htmlContent .= ‘<th>Total Visits</th>’; $htmlContent .= ‘<th>Total Actions</th>’; $htmlContent .= ‘<th>Bounce Rate</th>’; $htmlContent .= ‘<th>Conversions (Booking)</th>’; $htmlContent .= ‘<th>Evolution</th>’; $htmlContent .= ‘</tr>’; foreach ($countriesData as $countryData) { $htmlContent .= ‘<tr>’; $htmlContent .= ‘<td>’ . $countryData[‘label’] . ‘</td>’; $htmlContent .= ‘<td>’ . $countryData[‘sum_daily_nb_uniq_visitors’] . ‘</td>’; $htmlContent .= ‘<td>’ . $countryData[‘nb_visits’] . ‘</td>’; $htmlContent .= ‘<td>’ . $countryData[‘nb_actions’] . ‘</td>’; $htmlContent .= ‘<td>’ . number_format($countryData[‘bounce_rate’], 0) . ‘%</td>’; $htmlContent .= ‘<td>’ . $countryData[‘conversions’] . ‘</td>’; $htmlContent .= ‘<td style=”color: ‘ . $countryData[‘valueColor’] . ‘;”>’ . ($countryData[‘evolution’] >= 0 ? ‘+’ : ”) . number_format($countryData[‘evolution’], 1) . ‘%</td>’; $htmlContent .= ‘</tr>’; } $htmlContent .= ‘</table>’; |
As we know that selecting period=week&date=yesterday in the API fetches the data for the current week. To enhance the clarity of the current week’s data in our report, I implemented this code to display the corresponding date range at the top of the table. This code gets the start date and end date of the recent week.
$today = date(‘Y-m-d’); $startDate = date(‘Y-m-d’, strtotime(‘monday this week’, strtotime($today))); $endDate = date(‘Y-m-d’, strtotime(‘sunday this week’, strtotime($today))); $weekRange = ‘From ‘ . $weekStart . ‘ To ‘ . $yesterday; |
Now it’s time to see the output of our code.
The usort function is utilized here to sort the $countriesData array in descending order based on the ‘conversions’ key. This ensures that the countries with the highest number of conversions appear first in the array. You can modify it to suit your requirement.
usort($countriesData, function ($a, $b) { return $b[‘conversions’] – $a[‘conversions’]; }); |
Here is the complete code for the countryData function.
function countryData($matomo, $auth, $idsite) { $previous_data = previousData($matomo, $auth); $limit = 10000; $offset = 0; $today = date(‘Y-m-d’); $startDate = date(‘Y-m-d’, strtotime(‘monday this week’, strtotime($today))); $endDate = date(‘Y-m-d’, strtotime(‘sunday this week’, strtotime($today))); $weekRange = ‘From ‘ . $weekStart . ‘ To ‘ . $yesterday; $htmlContent = ‘<h3 style=”text-align: center; margin: 10px !important;”>’ . $weekRange . ‘</h3>’; $htmlContent .= ‘<h2>Country-wise Conversion Insights</h2>’; $htmlContent .= ‘<table border=”1″>’; $htmlContent .= ‘<tr style=”color:white; background-color:black; border: 2px solid white; !important”>’; $htmlContent .= ‘<th>Country</th>’; $htmlContent .= ‘<th>Unique Visits</th>’; $htmlContent .= ‘<th>Total Visits</th>’; $htmlContent .= ‘<th>Total Actions</th>’; $htmlContent .= ‘<th>Bounce Rate</th>’; $htmlContent .= ‘<th>Conversions (Booking)</th>’; $htmlContent .= ‘<th>Evolution</th>’; $htmlContent .= ‘</tr>’; $countriesData = []; do { $apiUrl = $matomo . ‘/index.php?module=API&method=UserCountry.getCountry&idSite=’.$idsite.’&period=week&date=yesterday&format=JSON&token_auth=’ . $auth . ‘&force_api_session=1&showColumns=label,sum_daily_nb_uniq_visitors,nb_visits,nb_actions,nb_visits,goals,bounce_count&filter_limit=’ . $limit . ‘&filter_offset=’ . $offset; $response = file_get_contents($apiUrl); if ($response === FALSE) { die(‘Error fetching data from API’); } $data = json_decode($response, true); foreach ($data as $countryData) { if (isset($countryData[‘goals’][‘idgoal=9’]) && $countryData[‘goals’][‘idgoal=9’][‘nb_conversions’] > 0) { $countryLabel = $countryData[‘label’]; $currentConversions = $countryData[‘goals’][‘idgoal=9’][‘nb_conversions’]; $previousConversions = isset($previous_data[$countryLabel]) ? $previous_data[$countryLabel] : 0; $evolution = ($currentConversions – $previousConversions) / ($previousConversions > 0 ? $previousConversions : 1) * 100; $valueColor = $evolution >= 0 ? ‘green’ : ‘red’; $countriesData[] = [ ‘label’ => $countryLabel, ‘sum_daily_nb_uniq_visitors’ => $countryData[‘sum_daily_nb_uniq_visitors’], ‘nb_visits’ => $countryData[‘nb_visits’], ‘nb_actions’ => $countryData[‘nb_actions’], ‘bounce_rate’ => isset($countryData[‘nb_visits’]) && $countryData[‘nb_visits’] > 0 ? ($countryData[‘bounce_count’] / $countryData[‘nb_visits’]) * 100 : 0, ‘conversions’ => $currentConversions, ‘evolution’ => $evolution, ‘valueColor’ => $valueColor, ]; } } $offset += $limit; } while (!empty($data)); usort($countriesData, function ($a, $b) { return $b[‘conversions’] – $a[‘conversions’]; }); foreach ($countriesData as $countryData) { $htmlContent .= ‘<tr>’; $htmlContent .= ‘<td>’ . $countryData[‘label’] . ‘</td>’; $htmlContent .= ‘<td>’ . $countryData[‘sum_daily_nb_uniq_visitors’] . ‘</td>’; $htmlContent .= ‘<td>’ . $countryData[‘nb_visits’] . ‘</td>’; $htmlContent .= ‘<td>’ . $countryData[‘nb_actions’] . ‘</td>’; $htmlContent .= ‘<td>’ . number_format($countryData[‘bounce_rate’], 0) . ‘%</td>’; $htmlContent .= ‘<td>’ . $countryData[‘conversions’] . ‘</td>’; $htmlContent .= ‘<td style=”color: ‘ . $countryData[‘valueColor’] . ‘;”>’ . ($countryData[‘evolution’] >= 0 ? ‘+’ : ”) . number_format($countryData[‘evolution’], 1) . ‘%</td>’; $htmlContent .= ‘</tr>’; } $htmlContent .= ‘</table>’; return $htmlContent; } |
Here is another example screenshot showcasing the data retrieved from the API for converted channels.
We have the flexibility to obtain various types of data, such as channel-wise conversions, ecommerce orders with revenue, total visits and unique visitors, pageviews, goal conversions, data on converted countries and cities, ecommerce products, converted users’ data, information on user devices and browsers, events, and much more.
Furthermore, to attach this PDF document, we make use of the following code. You can add styling in line with your choice. This consists of modifying the PDF document name, adjusting font size, shade, styling, table cell spacing, etc.
$htmlContent .= ‘<style>’; $htmlContent .= ‘body { font-family: Arial, sans-serif; }’; $htmlContent .= ‘h1, h2 { color: #333; }’; $htmlContent .= ‘table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }’; $htmlContent .= ‘th, td { border: 1px solid #ddd; padding: 16px; text-align: left; }’; $htmlContent .= ‘th { background-color: #5A5A5A; color: white; border: 2px solid white; padding: 10px; text-align: left; font-weight: bold; }’; $htmlContent .= ‘</style>’; // Generate PDF from HTML content $pdf = new TCPDF(); $pdf->SetCreator(‘CREATOR’); $pdf->SetAuthor(‘AUTHOR’); $pdf->SetTitle(‘Weekly Report – Matomo.com’); $pdf->AddPage(); // Set font for the entire document $pdf->SetFont(‘helvetica’, ”, 12); // Set background color for the title $pdf->SetFillColor(198, 12, 60); // RGB color code for #c60c3c // Set text color to white $pdf->SetTextColor(255, 255, 255); // RGB color code for white // Set font for th (table header) with bold style $pdf->SetFont(‘helvetica’, ‘B’, 14); // Output table header with background color $pdf->Cell(0, 10, ‘Weekly Report – Matomo.com’, 1, 1, ‘C’, true); // RGB text color to black for the rest of the content $pdf->SetTextColor(0, 0, 0); // RGB color code for black // Reset font to normal for the rest of the content $pdf->SetFont(‘helvetica’, ”, 12); // Write the rest of your HTML content… $pdf->writeHTML($htmlContent, true, false, true, false); // Get PDF content $pdfContent = $pdf->Output(”, ‘S’); |
Here is another instance screenshot demonstrating a chunk of styling for the file.
Now, you need to put together the content material for the e-mail, along with the email subject, e-mail body, and attachment name. Additionally, make sure to have the SMTP server configuration in place.
// Prepare email content $emailSubject = ‘Matomo Weekly Report’; $emailBody = ‘Attached is the weekly Matomo report in PDF format for your review. Kindly find the details enclosed.’; // Prepare recipients $emailList = [‘me@gmail.com’,’you@gmail.com’]; // Send email with PDF attachment to each recipient foreach ($emailList as $email) { $mail = new PHPMailer(); // Configure PHPMailer… $mail->isSMTP(); $mail->Host = ‘smtp.gmail.com’; // Your SMTP server address $mail->SMTPAuth = true; $mail->Username = ‘SMTP-USER-GMAIL’; // Your SMTP username $mail->Password = ‘PASSWORD’; // Your SMTP password $mail->SMTPSecure = ‘ssl’; // Use TLS encryption $mail->Port = 465; // SMTP port (typically 587) // Set email details $mail->setFrom(‘SMTP-USER-GMAIL’, ‘NAME’); $mail->addAddress($email); $mail->Subject = $emailSubject; $mail->Body = $emailBody; $mail->AddStringAttachment($pdfContent, ‘Matomo_report.pdf’); // Send email if ($mail->send()) { echo “Email sent successfully to $email<br>”; } else { echo “An error occurred while sending the email to $email: ” . $mail->ErrorInfo . “<br>”; } } |
If you intend to send this PDF report to multiple recipients, you can add their Gmail addresses to the list below.
// Prepare recipients $emailList = [‘me@gmail.com’, ‘you@gmail.com’, ‘another@gmail.com’]; |
In the end, developing custom reviews the use of matomo API and sending them as PDF attachments thru emails the use of the TCPDF library, and PHPMailer gives an effective manner to customize analytics insights. With this technique, you can tailor reports consistent with precise needs and effortlessly percentage them through e-mail. When TCPDF is used, records can be formatted directly into a PDF document, and PHPMailer manages the email sending system. The ability to automate this procedure by creating a cron job adds even more excitement to it. This guarantees that the reports are disseminated consistently, providing a useful and efficient method of managing analytics data. Furthermore, this approach offers consumers the ease of having all of their important history data conveniently accessible on one platform.
After utilizing this method to create several custom reports, we intend to add Meta Facebook and Google Ads to our list of data sources in the future. We will be able to aggregate key metrics from several platforms, providing users a more comprehensive and consolidated view of their statistics. As we work to improve our reporting skills, stay tuned for additional updates.
MatomoExpert © 2023 All Rights Reserved
Chat Now