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.