Sales Automation and Agents
Automate LinkedIn Posts with Images via Google Sheets
Build a Google Sheets-to-LinkedIn pipeline that ships approved posts with images on schedule. Faster ops, cleaner UTMs, fewer misses - without another bloated tool.
If posting on LinkedIn still means copy/paste, uploading images by hand, and chasing approvals in Slack, you’re paying an invisible tax in hours and missed reach. Here’s the operator’s way: one Google Sheet to draft, approve, and schedule posts; one automation to push text + images to LinkedIn (company or personal); clean UTM tracking; light governance. No bloated toolstack, no agency billables for basic ops.
What exactly you’ll automate (and why images matter on LinkedIn)
The goal is a single source of truth for LinkedIn posts that your team actually uses. Your Google Sheet holds the copy, hashtags, image URL, scheduled time, campaign tags, and approval status. The automation picks up approved rows and publishes to LinkedIn with the right media, on time, every time. That reduces context switching and errors, and lets leadership review the queue in seconds.
Images are not optional on LinkedIn. Visual posts consistently earn higher click-through and dwell time versus text-only updates. Image thumbnails in feed catch scrollers, and your first sentence does the rest. When you automate image attachments end-to-end, you stop skipping visuals “just this once” because nobody had time to save/export/upload the file.
Done correctly, this setup gives you a predictable cadence: plan weekly in the Sheet, ship daily on LinkedIn. That rhythm outperforms random bursts, and it makes performance comparisons easier because you’re controlling for volume and timing.
Architecture options: pick your tradeoffs before you build
There are three practical ways to wire Google Sheets to LinkedIn with images. Each has different complexity and control levels. Choose based on your team’s tolerance for OAuth setup, rate limits, and ongoing maintenance.
Option A: Google Apps Script + LinkedIn API. Full control, zero third-party cost. You’ll create a LinkedIn Developer app, handle OAuth 2.0, use the register-upload asset flow for images, then publish UGC posts. Upside: no per-run fees, extensible. Downside: initial setup time and staying within LinkedIn policy and rate limits.
Option B: Make.com or n8n. Fast to ship, visual builder, built-in LinkedIn and Google modules. Upside: lower engineering burden, robust error handling and scheduling. Downside: platform fees (Make) or self-hosting overhead (n8n), some vendor lock-in.
Option C: Scheduling tool that supports image posting via CSV/Sheet import. Easiest administratively, but you’ll often lose flexibility in custom fields, UTM logic, and dynamic conditions. Also watch out for extra seats and upsells that quietly balloon cost.
Sheet design: the minimal schema that scales
Don’t overcomplicate the sheet. You need just enough structure for approvals, scheduling, and analytics. Start with these columns and add only when you have a real use case.
Recommended columns: Status (Draft/Approved/Scheduled/Posted/Error), Scheduled Time (ISO or your local time with timezone noted in header), Post Owner (optional), Audience (Company Page/Personal), Copy (text), Hashtags (optional), Link (optional), UTM Source/Medium/Campaign (or a single UTM Template field), Image URL (direct file URL or Google Drive file link), Image Alt (optional), Posted URL (to capture the LinkedIn post URL), Error Log (automation notes).
Two simple rules prevent 80% of failures: 1) Require Status=Approved to publish. 2) Require either Copy or Link to be present (empty posts shouldn’t fire). Add data validation and color rules so non-technical teammates can see what’s needed at a glance.
Option A: Google Apps Script + LinkedIn API (images included)
This path is for teams comfortable with OAuth and APIs. You’ll own the whole stack and won’t depend on another SaaS vendor. The flow is: authenticate → register media upload → upload image bytes → create UGC post referencing the uploaded asset → update the Sheet with the result.
Step 1: Create a LinkedIn Developer App. Define the app name, logo, and permissions. For company page posts, request w_organization_social and r_organization_admin. For personal posts, request w_member_social. You’ll need redirect URIs for OAuth and to pass app review for extended usage.
Step 2: OAuth 2.0. Store client ID/secret in Apps Script Properties, not in code. Build an authorization URL for the admin to grant access, then capture the authorization code and exchange it for an access token. Implement token refresh and expire gracefully; don’t wait for “Expired token” errors in the wild.
Registering and uploading the image
LinkedIn image uploads use a two-step asset process. First, you register the upload to get an upload URL and asset ID. Then you PUT the image bytes to that URL. Keep the asset ID for the post payload. If your sheet stores Google Drive links, use Apps Script DriveApp to fetch the file blob, then upload that binary.
Practical guardrails: ensure your images are under size limits (ideally <5MB), use standard formats (JPEG/PNG), and include alt text if you want accessibility wins. If you’re resizing or compressing, do it before upload for consistent quality.
For company pages, make sure your authorized member has admin rights on the organization, or you’ll get cryptic 403 errors at publish time. Test with a single approved row before rolling into scheduling.
Creating the LinkedIn post (UGC or shares)
The UGC endpoint lets you post text with an attached image asset. You’ll reference either the organization URN or the person URN as the actor. Include your message, optional link, and the asset URN returned from the upload flow. If you want hashtags, include them in the message field; LinkedIn parses them automatically.
Scheduling is best handled by time-based triggers in Apps Script. Every 5–10 minutes, your script checks for rows where Status=Approved and Scheduled Time <= now, then posts. After success, set Status=Posted and capture the LinkedIn post URL for analytics. If an error fires, mark Status=Error and write the message to Error Log.
You can throttle to one post per run to avoid rate limit spikes, and stagger runs by 10–15 minutes between personal and company accounts if you’re posting to both.
Handling links, UTMs, and images from Drive
Use a consistent UTM template and compose the query string at runtime if the Link column is populated. For example, append utm_source=linkedin&utm_medium=social&utm_campaign=[campaign name] if none exists. Keep it predictable so downstream analytics can segment cleanly.
For Drive images, preview URLs won’t work. Fetch the file by ID via DriveApp.getFileById and push the blob to LinkedIn’s upload URL. If your team stores creative in a shared folder, lock permissions so only editors upload new files; avoid last-minute Renames that break references.
Log asset IDs and posted URLs so you can diagnose failures when the platform inevitably tightens policies or rate limits. If governance is strict, write basic audit logs to a second tab with timestamp, actor, post type, and row ID.
Option B: Make.com or n8n workflow (faster to ship, easier to maintain)
If you don’t want to manage OAuth and asset upload logic by hand, a scenario in Make.com or a workflow in n8n is the pragmatic middle ground. You’ll use a Google Sheets “Watch Rows” or scheduled “Search Rows,” filter by Approved + scheduled time, then a LinkedIn module to create a post. For images, most modules accept either a publicly accessible URL or a binary stream from a prior HTTP step.
Minimum viable scenario: 1) Google Sheets module pulls ready rows. 2) If Image URL exists, an HTTP module fetches the file and passes it as binary. 3) LinkedIn module creates the post (select Page vs Personal). 4) Google Sheets updates the row with Posted URL and Status=Posted (or Status=Error + message). Add a sleep/throttle step to avoid flooding.
This approach shines when non-technical marketers should own the calendar while ops maintain the scenario. The visual logs make troubleshooting obvious. If you prefer a managed partner to implement and harden this flow, this sits squarely in marketing automation workflows I build for growth-stage teams.
Content operations that make the automation worth it
Automation only amplifies what you put in. If your sheet is a mess, you’ll publish a mess faster. Treat the sheet like a production calendar with defined responsibilities. Assign a Content Owner for drafting, an Approver with final say, and a Publisher (the automation) with zero judgment - if Approved and due, it ships.
Image sourcing needs a single home. Either a Drive folder with a naming convention tied to the campaign (e.g., 2024-09-product-launch-01.jpg) or a DAM/S3 bucket with stable URLs. Don’t rely on pasting inline images into the Sheet; always store a URL in the Image URL field. If brand wants alt text, give it a column and make it optional until it isn’t.
Distribution matters too. Company pages build air cover; personal profiles spark comments. If you’re running leadership visibility alongside company updates, decide which posts go where and stagger publish times to avoid cannibalizing reach. For outreach-heavy motions, coordinate with your LinkedIn outreach to avoid same-day collisions between ads, posts, and DMs.
Governance, brand safety, and staying inside LinkedIn’s rules
LinkedIn is strict about automation that looks spammy or uses scraped credentials. Always use official APIs or authorized integrations, keep tokens secure, and respect rate limits. Don’t post the same image/copy repeatedly within short windows across multiple actors. Vary messages and cadence.
Use a non-personal, role-based Google account to host the Sheet and Apps Script to reduce dependency on any single employee. For company page posting via API, ensure at least two admins have granted permissions so coverage remains during PTO or role changes.
Finally, capture basic approvals in the sheet. The combination of Status=Approved and an Approver initial or email is usually enough for internal auditing. If your legal team needs more, add a Read-Only tab with an archive snapshot that the automation writes to at the moment of publish.
Cost and ROI: the quick math founders care about
Manual posting looks cheap until you count the time: copy from doc to LinkedIn, add tags, upload image, check link, publish, share internally - easily 8–12 minutes per post. At 5 posts/week across a page and a leader’s profile, that’s ~100 minutes weekly or 6–7 hours/month. Add error correction and missed windows, and it creeps further.
An automated pipeline reduces this to planning time plus light QA. Even if you keep a human approving, you’ll cut the publish effort to near-zero. That’s not just salary savings; it’s consistency. The schedule doesn’t slip because someone’s in a meeting.
Tooling costs vary: Apps Script is effectively free; Make.com might run $10–$60/month for this use case. Compare that with agency retainers that sneak “social scheduling” into a multi-thousand dollar bundle. If you want a clean view of economics by model, see how our flat-fee model stacks on the pricing page.
An operator model that ships without agency tax
Anton Chisliuk · AI Marketing Ops runs on a simple premise: one senior operator builds your pipeline, content, and systems instead of a layered agency team. It’s month-to-month after a 90-day build, and proof beats slideware. Example: Cleverence (B2B SaaS) grew from 0 to ~80K organic visits/month and ~2K inbound leads/month - then scaled paid leads at $2–$6.50 CPL. If you want a no-fluff assessment of where automation unlocks growth, book a short Growth Audit; you’ll get a prioritized plan, not a pitch.
Troubleshooting: the real-world edge cases you’ll hit
Google Drive links are public but still fail? Likely you’re using a preview link, not the file blob. In Apps Script, fetch via file ID and upload the binary. In Make/n8n, add an HTTP module to download the asset as binary before posting.
Company page posts failing while personal posts work? Check that the authenticated user is an admin of the organization in LinkedIn and that your app has the correct w_organization_social permission. Refresh tokens for all admins if one expired; your workflow might be using the wrong credential set.
Duplicate posts or posts firing early? Time zones. Standardize your sheet to UTC or a single local timezone and convert at runtime. Also, enforce a rule that each row posts once by writing a unique “Posted At” timestamp or post URL before the scenario ends.
When to DIY, hire an agency, or use a senior operator
DIY works if you have one marketer comfortable with Sheets and either Apps Script or a low-code platform. It’s cheapest and fastest to start, but knowledge sits with that one person, and maintenance can get neglected during launch crunches.
Agencies can implement this, but you’ll often pay for account management layers you don’t need for a compact workflow. If you’re already paying for creative and distribution strategy, sure - just be precise about scope. Otherwise, this is classic “operator work,” not something that benefits from a six-person Slack channel.
A senior operator builds the pipeline, ties it to campaigns, and hands you a working system with light docs and dashboards. It’s pragmatic: start in a week, not next quarter; measure by posts shipped and attributed pipeline, not by deck length or meeting count.
Conclusion
You don’t need another tool for social; you need an assembly line that takes approved content and publishes it - on time, with images, with clean UTMs. A single Google Sheet plus a lightweight automation (Apps Script, Make, or n8n) gets you there. Start with the minimal schema, wire the image upload path, add guardrails for approval and time zones, and you’ll have consistent LinkedIn presence without babysitting the platform.
If you want the first version built fast and hardened for scale, book a short Growth Audit - we’ll map the exact workflow, costs, and handoff you need.
Written by Anton Chisliuk, operator at Anton Chisliuk · AI Marketing Ops. Leads, sales, content, and systems - one senior operator.
FAQs
- Can I post to both a company page and personal profile from the same workflow?
Yes. Store a column for Audience (Company/Personal) and authenticate both actors. In Apps Script, use the correct URN per row; in Make/n8n, use separate LinkedIn connections and route based on the Audience field.
- Do I need public image URLs, or can I use Google Drive files?
You can use Drive files. For API-based posting, fetch the file as binary and upload it to LinkedIn’s asset endpoint. For low-code tools, add an HTTP download step to pass binary to the LinkedIn module. Avoid preview links; pull by file ID.
- How do I handle approvals without another tool?
Use a Status column with data validation and require Status=Approved before publish. Optionally capture Approver initials or email. The automation should ignore anything not Approved and log who approved what in a read-only audit tab.
- What if a token expires mid-campaign?
Build token refresh into your script or use a platform that manages OAuth refresh automatically. If posting to a company page, ensure at least two admins authorize the app so you’re not blocked by one person’s token expiring.
- Will this hurt reach compared to manual posting?
No - LinkedIn doesn’t penalize official API posting. Reach depends on content quality, timing, and engagement. Automation helps with consistency and error reduction; content still wins or loses attention.
- Can I auto-generate posts from a blog RSS and still attach images?
Yes. Parse RSS into the sheet, map featured image URLs into the Image URL column, then let the same publish workflow run. Add a human Approver step if quality varies.
- How many posts per day is safe?
For company pages, 1–2 quality posts daily is common. For personal profiles, avoid blasting multiple similar posts in short windows. Stagger and vary creative; the goal is sustained presence, not volume spikes.
Written by
Anton Chisliuk
Founder & Lead Operator · AI Marketing Operator
Anton Chisliuk is a GTM Engineer and Marketing Systems Architect. He built Cleverence's AI-native revenue OS (88 Make blueprints, MCP hub, 0→200K organic, 2.2K leads/mo) and now delivers outsourced GTM Engineering — CRM, automation, SEO, outbound — for growth-stage businesses via aimrktops.com.
About Anton →Ready to fix this — for real?
Book a free Growth Audit. No sales deck — a 30-minute conversation about your pipeline, stack, and what to ship first.