Automations

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.

Quick answer

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.

01

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.

02

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.

Read rows with googleapisPostGate API
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 ?? [];
03

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.

Map and publishPostGate API
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']] },
    });
  }
}
04

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.

05

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.

Build it with PostGate

One API for every social platform your customers care about — sandboxed, documented, and ready today.

Start building Read the docs