Read Google Sheets from PHP with a REST API
Use file_get_contents or Guzzle to fetch JSON from any Google Sheet - no OAuth, no SDK, no service account file required.
The OAuth problem with Google Sheets in PHP
The official Google Sheets PHP client is powerful, but getting it working requires a service account JSON file, enabling the Sheets API in Google Cloud Console, sharing the sheet with the service account email, and managing token refresh. For a read-only use case - displaying product data, a team roster, or a price list - this is too much ceremony.
GKit SheetsAPI strips all of that away. You connect your sheet once in the dashboard, and from that point on it's a plain REST endpoint secured with a Bearer token. Any HTTP client in any language - including vanilla PHP - can read it in five lines of code.
The simplest case: file_get_contents
PHP's built-in file_get_contents can make HTTP requests when given a stream context. For a quick
script or a small WordPress plugin, this is enough:
<?php
$userKey = 'YOUR_USER_KEY';
$sheetName = 'YOUR_SHEET_NAME';
$apiKey = 'sk_your_key_here';
$url = "https://sheetsapi.gkit.mreshank.com/api/spreadsheets/{$userKey}/{$sheetName}";
$context = stream_context_create([
'http' => [
'header' => "Authorization: Bearer {$apiKey}\r\n",
'method' => 'GET',
],
]);
$body = file_get_contents($url, false, $context);
$result = json_decode($body, true);
// $result['data'] - array of row objects
// $result['meta'] - ['total' => N, 'limit' => N, 'offset' => N]
foreach ($result['data'] as $row) {
echo $row['name'] . ' - ' . $row['plan'] . PHP_EOL;
}To add query parameters, append them to the URL before the request:
$params = http_build_query([
'search' => 'plan:pro',
'sort' => '-mrr',
'limit' => 10,
]);
$url = "https://sheetsapi.gkit.mreshank.com/api/spreadsheets/{$userKey}/{$sheetName}?{$params}";search=plan:pro returns only rows where the plan column equals pro. Prefix the sort field with
- for descending order. Add search_exact=1 if you need an exact match instead of a partial
(contains) search.
Guzzle for production code
file_get_contents is fine for scripts, but in a real application you want timeout control, proper
error handling, and a testable HTTP client. Guzzle is the standard choice:
<?php
use GuzzleHttp\Client;
use GuzzleHttp\RequestOptions;
$client = new Client([
'base_uri' => 'https://sheetsapi.gkit.mreshank.com',
'timeout' => 5.0,
]);
$response = $client->get('/api/spreadsheets/YOUR_USER_KEY/products', [
RequestOptions::HEADERS => [
'Authorization' => 'Bearer sk_your_key_here',
],
RequestOptions::QUERY => [
'sort' => '-price',
'limit' => 25,
'fields' => 'name,price,sku', // return only these columns
],
]);
$body = json_decode($response->getBody()->getContents(), true);
$rows = $body['data'];
$total = $body['meta']['total'];
echo "Fetched " . count($rows) . " of {$total} products\n";fields=name,price,sku is useful when your sheet has many columns but you only need a few - it
reduces payload size and speeds up the response.
WordPress: wp_remote_get
WordPress ships with its own HTTP API that respects proxy settings, handles SSL, and integrates with the WordPress transient cache. Use it instead of Guzzle inside plugins or themes:
<?php
function gkit_get_sheet_rows(string $sheet, array $params = []): array {
$apiKey = get_option('gkit_api_key'); // store the key in wp_options
$userKey = get_option('gkit_user_key');
$url = add_query_arg(
$params,
"https://sheetsapi.gkit.mreshank.com/api/spreadsheets/{$userKey}/{$sheet}"
);
$response = wp_remote_get($url, [
'headers' => ['Authorization' => "Bearer {$apiKey}"],
'timeout' => 10,
]);
if (is_wp_error($response)) {
return [];
}
$body = json_decode(wp_remote_retrieve_body($response), true);
return $body['data'] ?? [];
}Cache the result with transients to avoid hitting the API on every page load:
function gkit_get_sheet_rows_cached(string $sheet, int $ttl = 300): array {
$cacheKey = 'gkit_sheet_' . $sheet;
$cached = get_transient($cacheKey);
if ($cached !== false) {
return $cached;
}
$rows = gkit_get_sheet_rows($sheet, ['limit' => 100]);
set_transient($cacheKey, $rows, $ttl); // cache for 5 minutes by default
return $rows;
}Laravel: the Http facade
Laravel's Http facade wraps Guzzle with a fluent interface. withToken() handles the Bearer header,
and the json() method decodes the response body automatically:
<?php
use Illuminate\Support\Facades\Http;
$response = Http::withToken(config('services.sheetsapi.key'))
->get('https://sheetsapi.gkit.mreshank.com/api/spreadsheets/' .
config('services.sheetsapi.user_key') . '/inventory', [
'search' => 'status:in_stock',
'sort' => 'name',
'limit' => 50,
]);
$rows = $response->json('data'); // directly access the data key
$total = $response->json('meta.total');Add your keys to config/services.php:
'sheetsapi' => [
'key' => env('SHEETSAPI_KEY'),
'user_key' => env('SHEETSAPI_USER_KEY'),
],Caching in Laravel
For read-heavy pages, wrap the call with cache()->remember() so the sheet is fetched at most once
per cache TTL:
$rows = cache()->remember('inventory.in_stock', now()->addMinutes(5), function () {
return Http::withToken(config('services.sheetsapi.key'))
->get('https://sheetsapi.gkit.mreshank.com/api/spreadsheets/' .
config('services.sheetsapi.user_key') . '/inventory', [
'search' => 'status:in_stock',
'limit' => 100,
])
->json('data');
});The cache key should include any query parameters that vary per request. For example, if the search
term comes from user input, include it in the key: "inventory.search.{$term}".
APCu for non-framework PHP
Outside of Laravel or WordPress, APCu is a simple in-process cache that works with any PHP setup:
function fetch_sheet(string $sheet, array $params = []): array {
$cacheKey = 'sheet_' . $sheet . '_' . md5(serialize($params));
if (apcu_exists($cacheKey)) {
return apcu_fetch($cacheKey);
}
// ... Guzzle or file_get_contents call ...
$rows = []; // result from API
apcu_store($cacheKey, $rows, 300); // TTL in seconds
return $rows;
}Putting it together
The pattern is the same regardless of which HTTP client you use: send the Authorization: Bearer
header, append query parameters to the URL, and read $result['data'] for the rows and
$result['meta']['total'] for the total count. Everything else - search, sort, pagination, field
selection - is a query parameter away.
No OAuth dance. No SDK to install. No service account JSON to rotate. A sheet you update in Google Docs is live in your PHP app within seconds.
Next steps
GKit is currently undergoing platform maintenance
Be the first to know when all Google Workspace tools are back online, plus get our engineering deep-dives and product announcements directly to your inbox.