There are two ways to read a Google Sheet from PHP, and they sit at opposite ends of a security trade-off. You can publish the sheet to the web and fetch it as CSV, which is trivial to set up but makes the data readable by anyone who has (or guesses, or is shown) the URL. Or you can read it through the Sheets API with a service account, which is a little more setup but keeps the sheet private, scoped, and revocable. The code difference is small; the exposure difference is the whole decision.
The CSV way: publish to web
In the sheet, File → Share → Publish to web → CSV. Google hands you a URL that returns the tab as CSV, and PHP reads it in a few lines:
$url = 'https://docs.google.com/spreadsheets/d/e/<published-id>/pub?gid=0&single=true&output=csv';
$rows = array_map( 'str_getcsv', file( $url, FILE_IGNORE_NEW_LINES ) );
$head = array_shift( $rows );
foreach ( $rows as $row ) {
$data = array_combine( $head, $row );
// $data['post_id'], $data['new_currency'], ...
}No credentials, no tokens, no libraries. That is the appeal. The cost is in the second word of "publish to web": the sheet is now public. Anyone with that URL reads the data, the URL can be shared or leaked, and a published sheet is the kind of thing that turns up in logs, referrers, and the occasional search index. It is the right tool for data that is genuinely public anyway (a price list you'd put on the site regardless). It is the wrong tool for anything you would not paste into a public Gist.
The API way: a service account
The Sheets API reads the same data, but the sheet stays private. You share it with a service account (read-only), and your server authenticates as that account. The full token-minting and reading code is in bulk-updating WordPress fields from a Google Sheet; the short version is a signed JWT exchanged for an access token, then a GET with a Bearer header.
The security difference is not theoretical, and you can see it. The same private sheet, hit without a token, is refused; hit with the service account's token, it returns the rows:

That 403 is the point. With the API, the sheet is closed by default and opens only for an identity you control and can revoke. With a published CSV, the equivalent request from anyone returns 200. Same data, opposite default.
Which to use
| Publish-to-web CSV | Sheets API + service account | |
|---|---|---|
| Setup | one menu click | service account, key, share |
| Credentials | none | a JSON key on the server |
| Who can read the data | anyone with the URL | only the authenticated account |
| Revoke access | unpublish (and hope no copies) | delete the key or unshare |
| Right for | genuinely public data | anything private |
Reach for CSV only when the data is public anyway and you want the least possible machinery. For anything that lives behind a login, anything with personal data, or anything a competitor would enjoy reading, the service account is not optional, it is the baseline. The few minutes of setup buys you a private, auditable, revocable connection instead of a public URL you can never fully un-share.
Once you can read the sheet, applying it to WordPress is the pull/batch pattern; wrap the write in the safe batch updater's dry-run and change-only rails so a bad row is recoverable.
Sources
Authoritative references this article was fact-checked against.
- Publish a file to the web - Google Docs Editors Helpsupport.google.com
- spreadsheets.values.get - Google Sheets APIdevelopers.google.com
- str_getcsv - PHP Manualphp.net
- OAuth 2.0 for service accounts - Google Identitydevelopers.google.com





