Google Sheets to Social Media: Publish Rows via API
Read rows from a Google Sheet with the Google Sheets API and publish each one as a social post through PostGate’s API.
To post from Google Sheets to social media, read rows with the Google Sheets API using a service account shared on the sheet as Editor, map each row to a POST /api/posts request to PostGate, and write a posted flag back — or fetch the sheet’s public CSV export URL read-only. Run it on cron, GitHub Actions, or a serverless timer.
A spreadsheet is one of the simplest possible content queues, and there's no PostGate add-on in the Google Workspace Marketplace to wire it up automatically. Two real ways to read it: the official Google Sheets API, which also lets you write back a "posted" flag, or a plain CSV export URL when the sheet is public and you don't need to write anything back. This guide covers both.
How do I connect a service account to the sheet?
In Google Cloud Console, enable the Google Sheets API on a project, then create a service account and download its JSON key. Open the target spreadsheet and share it with the service account's email address as an Editor — that share is what actually grants access; the key alone doesn't.
How do I read rows with the Sheets API?
Google's official googleapis Node.js client handles the service-account auth exchange for you, so you never manually sign a JWT. Point spreadsheets.values.get at a range that skips the header row.
import { google } from 'googleapis';
const auth = new google.auth.GoogleAuth({
keyFile: 'service-account.json',
scopes: ['https://www.googleapis.com/auth/spreadsheets'],
});
const sheets = google.sheets({ version: 'v4', auth });
const { data } = await sheets.spreadsheets.values.get({
spreadsheetId: process.env.SHEET_ID,
range: 'Posts!A2:D', // Body | ImageURL | Profiles | Posted
});
const rows = data.values ?? [];How do I map a row to a post?
With columns Body, ImageURL, Profiles, and Posted, skip any row already marked TRUE and publish the rest. The row's own 1-based sheet position (offset by the header row) doubles as a stable Idempotency-Key.
for (const [i, row] of rows.entries()) {
const [body, imageUrl, profilesCsv, posted] = row;
if (posted === 'TRUE') continue;
const rowNumber = i + 2; // header is row 1
const res = await fetch(
`https://postgate.studio/api/posts?profile_group_id=grp_acme123`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.POSTGATE_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': `sheet-row-${rowNumber}`,
},
body: JSON.stringify({
post: { body },
profiles: profilesCsv?.split(',') ?? ['pro_instagram123'],
media: imageUrl ? [imageUrl] : undefined,
}),
}
);
if (res.ok) {
await sheets.spreadsheets.values.update({
spreadsheetId: process.env.SHEET_ID,
range: `Posts!D${rowNumber}`,
valueInputOption: 'RAW',
requestBody: { values: [['TRUE']] },
});
}
}Can I use the CSV export URL instead?
For a public sheet where you don't need to write a "posted" flag back, Google exposes each sheet tab as a downloadable CSV with no authentication at all:
https://docs.google.com/spreadsheets/d/{spreadsheetId}/export?format=csv&gid={sheetGid}
Fetch that URL, parse it with any CSV library, and track which rows you've already published yourself — the same seen-set approach used in the RSS guide — since there's no field to write "posted" back to. This only works for sheets shared as "Anyone with the link can view"; anything private needs the service-account approach above.
How do I run it on a schedule?
Either version is a script you run on a timer — cron, a scheduled GitHub Actions workflow, or a serverless function. For a no-code path instead, all three tools in our n8n/Make/Zapier guide have a native Google Sheets trigger you can pair with the same HTTP request shown there.
Frequently asked questions
Do I need the Google Sheets API to post from a spreadsheet?
Only if the sheet is private or you want to write a “posted” flag back. For a sheet shared as “Anyone with the link can view,” Google exposes each tab as a downloadable CSV at /export?format=csv with no authentication at all — fetch it, parse it with any CSV library, and track which rows you’ve already published yourself, since there’s no field to write back to.
How does a service account get access to my Google Sheet?
In Google Cloud Console, enable the Google Sheets API on a project, create a service account, and download its JSON key. Then open the target spreadsheet and share it with the service account’s email address as an Editor — that share is what actually grants access; the key alone doesn’t.
How do I avoid posting the same spreadsheet row twice?
Two mechanisms: skip any row already marked TRUE in the Posted column, and send each publish request with a stable Idempotency-Key derived from the row’s 1-based sheet position (offset by the header row). On a successful publish, write TRUE back to the row’s Posted cell with spreadsheets.values.update.
Can I connect Google Sheets to social media without writing code?
Yes — n8n, Make, and Zapier all have a native Google Sheets trigger you can pair with the same HTTP request to PostGate. There’s no PostGate add-on in the Google Workspace Marketplace, so the trigger-plus-HTTP-request pattern is the no-code path.
