AutoFlowLab
← Tutorials

Automate Social Media Posting With AI: One Post, Every Platform

Step-by-step Make build that reads a Google Sheets content calendar, uses AI to adapt one message for LinkedIn, X, and Facebook, and schedules it via Buffer.

May 19, 2026 · beginner · 50 minutes setup

Writing one piece of content is work. Rewriting it three times — a 1,200-character LinkedIn post, a 280-character X post, a chattier Facebook version — is the kind of work that makes small teams quietly stop posting. The fix is a content calendar with one base message per row, and an automation that does the platform adaptation, scheduling, and logging for you.

This tutorial builds that in Make: a Google Sheets calendar feeds an OpenAI module that rewrites the base message once per platform, Buffer handles the actual scheduling, and the results get logged back to the sheet so you always know what went out and where. Setup is about 50 minutes; after that, your weekly social workload is “fill in one column.”

What you’re building

  1. Trigger — Make checks your Google Sheets calendar every morning for rows where post_date is today and status is empty.
  2. AI adaptation — one OpenAI call returns all three platform variants as a single JSON object (one call instead of three keeps cost and latency down).
  3. Scheduling — three Buffer modules (or native LinkedIn/Facebook modules — covered below) queue each variant at your chosen times.
  4. Logging — Make writes the generated texts and a scheduled status back to the row, so reruns never double-post.

Free template · make

AI Social Media Autoposter

social-autopost-make.json

Download JSON

Prerequisites

  • A Make account (this scenario uses 6–8 operations per post; the free tier’s 1,000/month is fine for daily posting — as of mid-2026, check current pricing)
  • An OpenAI API key
  • A Buffer account with LinkedIn, X, and Facebook channels connected (Buffer’s free plan covers 3 channels and ~10 queued posts per channel as of mid-2026 — check current limits)
  • A Google Sheet set up as below

Step 1: Build the content calendar

Create a sheet named Content Calendar with these columns in row 1:

ColumnPurpose
post_dateYYYY-MM-DD the post should go out
topic2–4 word label, for your own filtering
base_messageThe one message you actually write — 2–5 sentences, platform-neutral
linkOptional URL to append
linkedin_text / x_text / facebook_textFilled by the automation
statusFilled by the automation: scheduled or error

The whole system rests on base_message being substance, not finished copy. Write “We shipped CSV import. Biggest request from onboarding calls all quarter. Works with exports from QuickBooks and Xero.” — and let the AI handle tone, hashtags, and length per platform.

Airtable works identically here; swap the Google Sheets modules for Airtable > Search Records and Airtable > Update a Record and use a view filtered to today’s unposted rows. Airtable’s typed fields make the date filter slightly cleaner, but don’t migrate just for this.

  1. Create a new scenario. Add Google Sheets > Search Rows as the first module (not “Watch Rows” — watch triggers fire on new rows, but you’ll often fill the calendar weeks ahead; searching by date is the robust pattern).
  2. Spreadsheet / Sheet: your calendar.
  3. Filter: post_date Equal to {{formatDate(now; "YYYY-MM-DD")}} AND status Equal to (empty).
  4. Maximum number of returned rows: 10.
  5. Set the scenario schedule (clock icon, bottom left) to Every day at 07:00 in your timezone.

Each matching row flows through the rest of the scenario as its own bundle — Make iterates search results automatically, no explicit Iterator module needed.

Step 3: One AI call, three platform variants

  1. Add OpenAI > Create a Completion. Model: gpt-4o-mini (platform adaptation is a rewriting task; you don’t need a frontier model). Response format: JSON object. Temperature: 0.7 — unlike data extraction, you want some voice here. Max tokens: 1500.
  2. System and user messages:
SYSTEM:
You are a social media copywriter for a small B2B software company. Voice: direct, concrete, lightly informal. Never use the words "game-changer", "unlock", "elevate", or "thrilled". No emoji walls. You return ONLY valid JSON, no markdown fences.

USER:
Adapt this base message for three platforms.

BASE MESSAGE:
{{base_message}}

LINK (append where the platform rules say to): {{link}}

PLATFORM RULES:
- linkedin: 900-1300 characters. Open with a one-line hook on its own line. Short paragraphs, line breaks between them. End with one question to invite comments. 3 hashtags max, at the end. Append the link on its own line before hashtags.
- x: HARD LIMIT 270 characters including the link (assume the link costs 24 characters). One idea only — cut everything else. No hashtags unless one fits naturally. Punchy, no preamble.
- facebook: 400-700 characters. Conversational, first person plural. One emoji maximum. Append the link at the end. No hashtags.

Return exactly:
{
  "linkedin": "...",
  "x": "...",
  "facebook": "..."
}
  1. Add JSON > Parse JSON and map the completion result into it. Generate the data structure from the sample shape above.

Why a prompt-per-platform table matters

The platform rules block above is the highest-leverage text in this build. Tune it once and every future post inherits the fix. A reference table for adjusting constraints:

PlatformLength targetToneHashtagsLink handlingFailure mode to guard against
LinkedIn900–1,300 charsProfessional, first person≤3, at endOwn line before hashtagsAI padding with corporate filler — ban specific words in the system prompt
X≤270 chars incl. linkPunchy, zero preamble0–1Inline at end, budget 24 charsOverruns — state the limit as HARD and tell it what to cut
Facebook400–700 charsConversational, warmNoneAt endEmoji spam — cap it explicitly

When you add Instagram or Threads later, you add one line to the rules, one key to the JSON shape, and one scheduling module. The architecture doesn’t change.

Step 4: Schedule via Buffer

  1. Add three Buffer > Create an Update modules in sequence (they’re cheap, sequential is fine).
  2. In each: Profile = the matching channel (your LinkedIn page, X account, Facebook page). Text = the corresponding field from Parse JSON (linkedin, x, facebook).
  3. Scheduling: leave “Share now” off and either set Scheduled at per platform — a sane default is LinkedIn 09:00, X 12:30, Facebook 17:00 local time, using {{parseDate(post_date + " 09:00"; "YYYY-MM-DD HH:mm")}} — or let Buffer’s own queue slots decide by omitting the time. Queue slots are easier; explicit times give the sheet full control.

Native modules instead of Buffer

If you’d rather skip Buffer: Make has LinkedIn > Create an Organization Text Post, X (Twitter) > Create a Post, and Facebook Pages > Create a Page Post modules. Two caveats. First, native modules post immediately, so you’d move scheduling into Make itself — set the scenario to run at posting time, or add Tools > Sleep between branches (max 5 minutes, so really: schedule the scenario for the earliest slot). Second, the X connection requires an X Developer App, and the free API tier’s posting limits are tight (as of mid-2026, check current limits — this has changed repeatedly). Buffer abstracts all of that away for the price of one more account, which is why it’s the primary path here.

Try it yourself

Make

One scenario run fans out to three platforms and logs back to your sheet — this branching is where Make earns its keep over linear tools.

Start with Make

Step 5: Log results back to the sheet

  1. Add Google Sheets > Update a Row. Row number: map from the Search Rows module.
  2. Map linkedin_text, x_text, facebook_text from Parse JSON, and set status to scheduled.
  3. Add an error handler on each Buffer module (right-click > Add error handler) with a Resume directive, and route errors to a second Update a Row that sets status to error: {{error.message}}. Now a failed post is a red cell in your sheet, not a silent gap in your feed.

The status write-back is also your idempotency guard: because step 2 filters on empty status, a rerun — manual or scheduled — can never double-post a row.

Run it

Put one test row in the sheet dated today, run the scenario once manually, and check Buffer’s queue. Read all three variants — you’re checking the prompt, not the plumbing. Expect to tweak the platform rules two or three times in the first week (typical fixes: “stop starting LinkedIn posts with a question,” “X posts read like ads, ban exclamation marks”). Edit the prompt, not the outputs; per-post hand-editing in Buffer means the automation isn’t finished yet.

Which platform should you use?

  • Make — the right call for this task, full stop. The fan-out to three platforms, JSON parsing, and sheet write-back are all first-class, and a day’s posting costs single-digit operations.
  • Zapier — works (Sheets trigger → ChatGPT → three Buffer actions), and its Buffer integration is solid. But each post burns 5–6 tasks, which on social-media volume pushes you up plan tiers fast (as of mid-2026, check current pricing). Choose it only if everything else you run is already there.
  • n8n — choose it when you outgrow this design: posting from multiple brands, A/B testing hooks, or pulling performance data back in for the AI to learn from. That’s a code-node job. Full breakdown in Make vs Zapier vs n8n.

Common errors and fixes

X rejects the post: “Text exceeds character limit.” The model overran despite the instruction — it happens a few percent of the time at temperature 0.7. Belt-and-suspenders fix: wrap the mapping in Make’s substring function, {{substring(x_text; 0; 270)}}, on the Buffer/X module. Better fix: add “Count characters before answering. If over 270, rewrite shorter” to the prompt, which drops the failure rate to near zero.

Buffer module fails with 403 “insufficient scope.” Your Buffer connection predates the channel you’re posting to, or was authorized without that channel selected. Reconnect Buffer in Make and tick every channel in the OAuth screen.

Facebook posts fail with ”(#200) permissions error.” You connected a personal profile, not a Page. Meta’s API only allows automated posting to Pages, and the token needs the pages_manage_posts scope — re-do the connection and select the Page explicitly.

Parse JSON throws “Invalid JSON” intermittently. Either the response-format flag got cleared (re-check it after any model change) or the completion hit max tokens mid-object. 1500 tokens is enough for three variants; if you add platforms, raise it.

The scenario runs but finds zero rows. Timezone mismatch: now in Make uses your organization’s timezone setting (Profile > Organization > Time zone), which defaults to UTC. If your sheet says 2026-05-19 but Make thinks it’s still the 18th, today’s posts silently skip. Set the org timezone once.

Posts go out with literal {{link}} in the text. The row had an empty link cell and the model echoed the placeholder. Guard it in the mapping: {{if(link = ""; "none"; link)}} — and the prompt’s “append where the platform rules say to” handles “none” gracefully.

Where to take it next

The obvious upgrade is closing the loop on content creation: our AI content pipeline tutorial generates the base_message drafts themselves from a topic backlog, which turns this sheet into a review queue instead of a writing task. And the weekly results — what posted, what errored — slot neatly into the automated weekly reports build.

Write the message once. Let the scenario argue with the character limits.