https://developers.google.com/webmaster-tools
This Laravel package provides a comprehensive PHP wrapper for the Google Search Console API, enabling you to seamlessly integrate Search Console functionality into your Laravel applications. With this package, you can:
- Query Search Analytics Data: Retrieve search performance metrics including impressions, clicks, CTR, and average position
- Analyze Website Performance: Get detailed insights about your website's performance in Google Search results
- Filter by Dimensions: Query data by page, query, country, device, and more
- Manage Site Properties: List and manage your Search Console properties
- Flexible Authentication: Support for both OAuth 2.0 (user-based) and Service Account (server-to-server) authentication
- Laravel Integration: Built specifically for Laravel with facades, service providers, and Artisan commands
The package leverages the powerful revolution/laravel-google-sheets dependency for Google API client management and authentication, providing automatic token refresh and robust error handling.
Perfect for: SEO tools, analytics dashboards, automated reporting, website monitoring, and any application that needs to access Google Search Console data programmatically.
- PHP >= 8.2
- Laravel >= 11.0
composer require revolution/laravel-google-searchconsole
php artisan vendor:publish --tag="google-config"composer remove revolution/laravel-google-searchconsole
https://github.com/invokable/search-console-project
This package supports two authentication methods for accessing the Google Search Console API:
- OAuth 2.0: Recommended for user-based access when you need to access data on behalf of individual Google users
- Service Account: Ideal for server-to-server applications with automated access to specific properties
- Note: API Key authentication is NOT supported by Google Search Console API
- Go to the Google Cloud Console
- Create a new project or select an existing one
- Enable the Google Search Console API
- Create credentials based on your chosen authentication method
For OAuth authentication, this package works seamlessly with Laravel Socialite's official Google driver.
composer require laravel/socialiteIn Google Cloud Console:
- Go to "Credentials" → "Create Credentials" → "OAuth 2.0 Client IDs"
- Set application type to "Web application"
- Add your authorized redirect URIs (e.g.,
https://yourapp.com/auth/google/callback)
Add to config/services.php:
'google' => [
'client_id' => env('GOOGLE_CLIENT_ID'),
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
'redirect' => env('GOOGLE_REDIRECT_URI'),
],Add to config/google.php:
'client_id' => env('GOOGLE_CLIENT_ID'),
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
'redirect_uri' => env('GOOGLE_REDIRECT_URI'),
'scopes' => [\Google\Service\Webmasters::WEBMASTERS],
'access_type' => 'offline',
'prompt' => 'consent select_account',GOOGLE_CLIENT_ID=your_oauth_client_id
GOOGLE_CLIENT_SECRET=your_oauth_client_secret
GOOGLE_REDIRECT_URI=https://yourapp.com/auth/google/callbackService Accounts provide server-to-server authentication without user interaction. The Service Account email must be added as an owner or user in Search Console for each property you want to access.
In Google Cloud Console:
- Go to "Credentials" → "Create Credentials" → "Service Account"
- Fill in the service account details
- Create and download the JSON key file
The package automatically uses Service Account authentication when configured through the laravel-google-sheets dependency. Place your service account JSON file in your Laravel storage directory.
Even for service accounts, scopes must be set in config/google.php:
'scopes' => [\Google\Service\Webmasters::WEBMASTERS],GOOGLE_SERVICE_ENABLED=true
GOOGLE_SERVICE_ACCOUNT_JSON_LOCATION=/path/to/service-account.jsonGOOGLE_SERVICE_ACCOUNT_JSON_LOCATION can be an array other than a file path, so you can set it as a JSON string in .env and decode it to an array in config/google.php.
// config/google.php
'service' => [
'file' => json_decode(env('GOOGLE_SERVICE_ACCOUNT_JSON_LOCATION', ''), true),
],// .env
GOOGLE_SERVICE_ACCOUNT_JSON_LOCATION='{"type": "service_account", "project_id": "your-project", "private_key_id": "...", "private_key": "...", "client_email": "...", "client_id": "...", "auth_uri": "...", "token_uri": "...", "auth_provider_x509_cert_url": "...", "client_x509_cert_url": "..."}'This method is particularly well-suited for GitHub Actions deployment, as it allows the entire service account credentials to be stored as a single secret.
- Go to Google Search Console
- Select your property
- Go to Settings → Users and permissions
- Add your service account email as a user with "Full" permissions
All query classes extend Revolution\Google\SearchConsole\Query\AbstractQuery, which is a subclass of Google\Service\Webmasters\SearchAnalyticsQueryRequest. This provides access to all Google Search Console API query parameters.
Use the Artisan command to create new query classes in app/Search:
php artisan make:search:query YourQueryNameEach query class must implement the init() method where you define your query parameters:
<?php
namespace App\Search;
use Revolution\Google\SearchConsole\Query\AbstractQuery;
class YourQueryName extends AbstractQuery
{
public function init(): void
{
// Define your query parameters here
}
}You can use any of the following methods in your init() method:
$this->setStartDate('2024-01-01'); // Start date (YYYY-MM-DD)
$this->setEndDate('2024-01-31'); // End date (YYYY-MM-DD)
$this->setStartDate(now()->subDays(30)->toDateString()); // Dynamic dates$this->setDimensions(['query']); // Group by search queries
$this->setDimensions(['page']); // Group by pages
$this->setDimensions(['country']); // Group by countries
$this->setDimensions(['device']); // Group by device types
$this->setDimensions(['searchAppearance']); // Group by search appearance
$this->setDimensions(['query', 'page']); // Multiple dimensions// Filter by specific queries
$this->setDimensionFilterGroups([
[
'filters' => [
[
'dimension' => 'query',
'operator' => 'contains',
'expression' => 'laravel'
]
]
]
]);
// Filter by specific pages
$this->setDimensionFilterGroups([
[
'filters' => [
[
'dimension' => 'page',
'operator' => 'equals',
'expression' => 'https://example.com/page'
]
]
]
]);$this->setRowLimit(100); // Limit results (max 25,000)
$this->setStartRow(0); // Pagination offset
$this->setAggregationType(['auto']); // Aggregation type
$this->setDataState('final'); // 'final' or 'fresh' data<?php
namespace App\Search;
use Revolution\Google\SearchConsole\Query\AbstractQuery;
class TopPagesQuery extends AbstractQuery
{
public function init(): void
{
$this->setStartDate(now()->subDays(30)->toDateString());
$this->setEndDate(now()->toDateString());
$this->setDimensions(['page']);
$this->setRowLimit(50);
$this->setDataState('final');
}
}<?php
namespace App\Search;
use Revolution\Google\SearchConsole\Query\AbstractQuery;
class MobileQueriesQuery extends AbstractQuery
{
public function init(): void
{
$this->setStartDate(now()->subDays(7)->toDateString());
$this->setEndDate(now()->toDateString());
$this->setDimensions(['query']);
// Filter for mobile devices only
$this->setDimensionFilterGroups([
[
'filters' => [
[
'dimension' => 'device',
'operator' => 'equals',
'expression' => 'mobile'
]
]
]
]);
$this->setRowLimit(100);
}
}<?php
namespace App\Search;
use Revolution\Google\SearchConsole\Query\AbstractQuery;
class CountryPerformanceQuery extends AbstractQuery
{
public function init(): void
{
$this->setStartDate(now()->subMonths(3)->toDateString());
$this->setEndDate(now()->toDateString());
$this->setDimensions(['country', 'query']);
$this->setRowLimit(200);
$this->setAggregationType(['auto']);
}
}When using OAuth, you'll need to obtain access tokens through the Socialite authentication flow, then use setAccessToken() to authenticate your requests.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Laravel\Socialite\Facades\Socialite;
use Revolution\Google\SearchConsole\Facades\SearchConsole;
use App\Search\TopPagesQuery;
class GoogleSearchConsoleController extends Controller
{
public function redirectToGoogle()
{
return Socialite::driver('google')
->scopes(config('google.scopes'))
->with([
'access_type' => config('google.access_type'),
'prompt' => config('google.prompt'),
])
->redirect();
}
public function handleGoogleCallback()
{
$user = Socialite::driver('google')->user();
// Store tokens in your database
auth()->user()->update([
'google_access_token' => $user->token,
'google_refresh_token' => $user->refreshToken,
]);
return redirect()->route('dashboard');
}
public function getSearchConsoleData(Request $request)
{
$token = [
'access_token' => auth()->user()->google_access_token,
'refresh_token' => auth()->user()->google_refresh_token,
];
// Create and execute query
$query = new TopPagesQuery();
$result = SearchConsole::setAccessToken($token)
->query('https://example.com/', $query);
return response()->json($result);
}
}You can use the WithSearchConsole trait to integrate Search Console functionality directly into your models:
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Revolution\Google\SearchConsole\Traits\WithSearchConsole;
class User extends Authenticatable
{
use WithSearchConsole;
protected $fillable = [
'name', 'email', 'google_access_token', 'google_refresh_token'
];
protected function tokenForSearchConsole(): array
{
return [
'access_token' => $this->google_access_token,
'refresh_token' => $this->google_refresh_token,
];
}
}Then use it in your application:
use App\Search\TopPagesQuery;
$user = User::find(1);
$query = new TopPagesQuery();
// The searchconsole() method automatically handles authentication
$result = $user->searchconsole()->query('https://example.com/', $query);
$sites = $user->searchconsole()->listSites();With Service Account authentication, the package automatically handles authentication through the GoogleApiClient included in laravel-google-sheets. No manual token management is required.
<?php
namespace App\Services;
use Revolution\Google\SearchConsole\Facades\SearchConsole;
use App\Search\TopPagesQuery;
use App\Search\MobileQueriesQuery;
use App\Search\CountryPerformanceQuery;
class SearchConsoleService
{
public function getTopPages(string $siteUrl): object
{
$query = new TopPagesQuery();
// Service Account authentication is automatic
return SearchConsole::query($siteUrl, $query);
}
public function getMobileQueries(string $siteUrl): object
{
$query = new MobileQueriesQuery();
return SearchConsole::query($siteUrl, $query);
}
public function getCountryPerformance(string $siteUrl): object
{
$query = new CountryPerformanceQuery();
return SearchConsole::query($siteUrl, $query);
}
public function getAllSites(): object
{
return SearchConsole::listSites();
}
public function getSiteInfo(string $siteUrl): object
{
return SearchConsole::listSites(['siteUrl' => $siteUrl]);
}
}<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Revolution\Google\SearchConsole\Facades\SearchConsole;
use App\Search\TopPagesQuery;
class FetchSearchConsoleData extends Command
{
protected $signature = 'searchconsole:fetch {site_url}';
protected $description = 'Fetch Search Console data for a site';
public function handle()
{
$siteUrl = $this->argument('site_url');
$query = new TopPagesQuery();
try {
$result = SearchConsole::query($siteUrl, $query);
$this->info("Found {$result->rowCount} results:");
foreach ($result->rows as $row) {
$this->line("Page: {$row->keys[0]}");
$this->line(" Clicks: {$row->clicks}");
$this->line(" Impressions: {$row->impressions}");
$this->line(" CTR: " . round($row->ctr * 100, 2) . "%");
$this->line(" Position: " . round($row->position, 1));
$this->line("");
}
} catch (\Exception $e) {
$this->error("Error fetching data: " . $e->getMessage());
}
}
}use Revolution\Google\SearchConsole\Facades\SearchConsole;
// Set access token (OAuth only)
SearchConsole::setAccessToken($token);
// Execute a query
SearchConsole::query($siteUrl, $queryObject);
// List all sites
SearchConsole::listSites();
// List sites with parameters
SearchConsole::listSites(['siteUrl' => 'https://example.com/']);The API returns objects with the following structure:
$result = SearchConsole::query($siteUrl, $query);
// Access the data
echo $result->rowCount; // Number of results
echo $result->responseAggregationType; // Aggregation type used
foreach ($result->rows as $row) {
print_r($row->keys); // Array of dimension values
echo $row->clicks; // Number of clicks
echo $row->impressions; // Number of impressions
echo $row->ctr; // Click-through rate (0-1)
echo $row->position; // Average position (1+)
}The response objects work seamlessly with Laravel's helper functions, making data manipulation more convenient:
Using data_get() for Safe Property Access
$result = SearchConsole::query($siteUrl, $query);
// Safely access properties with default values
$rowCount = data_get($result, 'rowCount', 0);
$aggregationType = data_get($result, 'responseAggregationType', 'unknown');
// Access nested properties safely
foreach ($result->rows as $row) {
$clicks = data_get($row, 'clicks', 0);
$impressions = data_get($row, 'impressions', 0);
$ctr = data_get($row, 'ctr', 0.0);
$position = data_get($row, 'position', 0.0);
// Handle optional or nested properties
$firstKey = data_get($row, 'keys.0', 'N/A');
}Using collect() for Enhanced Data Processing
$result = SearchConsole::query($siteUrl, $query);
// Convert rows to a Laravel Collection for powerful data manipulation
$rows = collect($result->rows);
// Filter rows with high CTR
$highCtrRows = $rows->filter(fn($row) => $row->ctr > 0.05);
// Sort by impressions (descending)
$sortedRows = $rows->sortByDesc('impressions');
// Get top 10 pages by clicks
$topPages = $rows->sortByDesc('clicks')->take(10);
// Transform data structure
$pageData = $rows->map(function ($row) {
return [
'page' => data_get($row, 'keys.0', 'Unknown'),
'metrics' => [
'clicks' => $row->clicks,
'impressions' => $row->impressions,
'ctr' => round($row->ctr * 100, 2) . '%',
'position' => round($row->position, 1),
]
];
});
// Group by device type (if querying by device dimension)
$deviceGroups = $rows->groupBy(fn($row) => data_get($row, 'keys.0', 'unknown'));
// Calculate totals
$totalClicks = $rows->sum('clicks');
$totalImpressions = $rows->sum('impressions');
$averagePosition = $rows->average('position');MIT