This workflow corresponds to n8n.io template #14710 — we link there as the canonical source.
This workflow follows the Google Sheets → HTTP Request recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "5MEc8sTlud67zx8f",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "LinkedIn Top Posts to Google Sheets Content Engine",
"tags": [],
"nodes": [
{
"id": "8d84cc77-aa9b-4b0b-b6ee-c28631e75a45",
"name": "Google Sheets Export",
"type": "n8n-nodes-base.stickyNote",
"position": [
768,
1488
],
"parameters": {
"color": 7,
"width": 596,
"height": 332,
"content": "## Google Sheets export"
},
"typeVersion": 1
},
{
"id": "4dfd7269-1bf3-4d1d-8b6d-31241ea48332",
"name": "Analysis and Generation",
"type": "n8n-nodes-base.stickyNote",
"position": [
208,
1488
],
"parameters": {
"color": 7,
"width": 544,
"height": 332,
"content": "## Analysis and generation"
},
"typeVersion": 1
},
{
"id": "2bf9ebb2-deeb-4dda-aa78-15e22137d07f",
"name": "Collection and Ranking",
"type": "n8n-nodes-base.stickyNote",
"position": [
-352,
1488
],
"parameters": {
"color": 7,
"width": 556,
"height": 332,
"content": "## Post collection and ranking\n"
},
"typeVersion": 1
},
{
"id": "930b7fb1-e8b5-40df-bb56-fb3c54a757af",
"name": "Input and Validation",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1056,
1488
],
"parameters": {
"color": 7,
"width": 692,
"height": 332,
"content": "## Input and validation"
},
"typeVersion": 1
},
{
"id": "9ef0a92e-5bbf-4d99-adc0-be95901e9411",
"name": "Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1616,
1200
],
"parameters": {
"width": 540,
"height": 1088,
"content": "# LinkedIn Top Posts to Google Sheets Content Engine\n\n### HOW IT WORKS:\n\nThis workflow helps you study what is already working on LinkedIn and turn those patterns into new post ideas.\n\nIt pulls posts from a LinkedIn source through Apify, ranks them by impressions, sends the strongest examples to OpenAI for analysis, and writes the results to Google Sheets.\n\n### HOW TO SET UP:\n\nAdd your Apify token, LinkedIn actor ID, and OpenAI API key in the \"Set Config\" step.\n\nReplace the example LinkedIn profile URL with the profile you want to analyze.\n\nIn the Google Sheets node, choose the spreadsheet and worksheet where you want the output to go.\n\nBefore running the workflow, make sure your sheet already has these columns:\n\nGenerated At\nSource Profile\nTop Post Rank\nTop Post Text\nTop Post Impressions\nInsight 1\nInsight 2\nInsight 3\nNew Post Title\nNew Post Hook\nNew Post Text\nNew Post Cta\nFormat Type\nWhy It Should Work\nTotal Posts Analyzed\nTop Post Reactions\nTop Post Comments\nTop Post Reposts\nInsight 4\nNew Post Number\n\nOnce that is in place, run the workflow and check the first output row to confirm everything is landing where you expect."
},
"typeVersion": 1
},
{
"id": "87804ce5-beea-4980-8fd6-06d94d561c7a",
"name": "Append Rows to Google Sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
1152,
1600
],
"parameters": {
"columns": {
"value": {
"Insight 1": "={{$json.insight_1}}",
"Insight 2": "={{$json.insight_2}}",
"Insight 3": "={{$json.insight_3}}",
"Insight 4": "={{$json.insight_4}}",
"Format Type": "={{$json.format_type}}",
"Generated At": "={{$json.generated_at}}",
"New Post Cta": "={{$json.new_post_cta}}",
"New Post Hook": "={{$json.new_post_hook}}",
"New Post Text": "={{$json.new_post_text}}",
"Top Post Rank": "={{$json.source_top_post_rank}}",
"Top Post Text": "={{$json.source_top_post_text}}",
"New Post Title": "={{$json.new_post_title}}",
"Source Profile": "={{$json.source_profile}}",
"New Post Number": "={{$json.new_post_number}}",
"Top Post Reposts": "={{$json.source_top_post_reposts}}",
"Top Post Comments": "={{$json.source_top_post_comments}}",
"Top Post Reactions": "={{$json.source_top_post_reactions}}",
"Why It Should Work": "={{$json.why_it_should_work}}",
"Top Post Impressions": "={{$json.source_top_post_impressions}}",
"Total Posts Analyzed": "={{$json.total_posts_analyzed}}"
},
"schema": [
{
"id": "Generated At",
"type": "string",
"display": true,
"required": false,
"displayName": "Generated At",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Source Profile",
"type": "string",
"display": true,
"required": false,
"displayName": "Source Profile",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Top Post Rank",
"type": "string",
"display": true,
"required": false,
"displayName": "Top Post Rank",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Top Post Text",
"type": "string",
"display": true,
"required": false,
"displayName": "Top Post Text",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Top Post Impressions",
"type": "string",
"display": true,
"required": false,
"displayName": "Top Post Impressions",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Insight 1",
"type": "string",
"display": true,
"required": false,
"displayName": "Insight 1",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Insight 2",
"type": "string",
"display": true,
"required": false,
"displayName": "Insight 2",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Insight 3",
"type": "string",
"display": true,
"required": false,
"displayName": "Insight 3",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "New Post Title",
"type": "string",
"display": true,
"required": false,
"displayName": "New Post Title",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "New Post Hook",
"type": "string",
"display": true,
"required": false,
"displayName": "New Post Hook",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "New Post Text",
"type": "string",
"display": true,
"required": false,
"displayName": "New Post Text",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "New Post Cta",
"type": "string",
"display": true,
"required": false,
"displayName": "New Post Cta",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Format Type",
"type": "string",
"display": true,
"required": false,
"displayName": "Format Type",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Why It Should Work",
"type": "string",
"display": true,
"required": false,
"displayName": "Why It Should Work",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Total Posts Analyzed",
"type": "string",
"display": true,
"required": false,
"displayName": "Total Posts Analyzed",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Top Post Reactions",
"type": "string",
"display": true,
"required": false,
"displayName": "Top Post Reactions",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Top Post Comments",
"type": "string",
"display": true,
"required": false,
"displayName": "Top Post Comments",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Top Post Reposts",
"type": "string",
"display": true,
"required": false,
"displayName": "Top Post Reposts",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Insight 4",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Insight 4",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "New Post Number",
"type": "string",
"display": true,
"required": false,
"displayName": "New Post Number",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1OnnfqCErNWrvGIGwL4VhLRDUYamUkUrzAvP96O7LfZs/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "https://docs.google.com/spreadsheets/d/1OnnfqCErNWrvGIGwL4VhLRDUYamUkUrzAvP96O7LfZs/edit?usp=drivesdk",
"__regex": "https:\\/\\/(?:drive|docs)\\.google\\.com(?:\\/.*|)\\/d\\/([0-9a-zA-Z\\-_]+)(?:\\/.*|)"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "d5f4387f-5f82-4e53-a018-3563041f82d6",
"name": "Prepare Google Sheets Rows",
"type": "n8n-nodes-base.code",
"position": [
864,
1600
],
"parameters": {
"jsCode": "const data = $input.first().json;\nconst topPosts = data.top_posts || [];\nconst insights = data.insights || [];\nconst posts = data.generated_posts || [];\nconst generatedAt = new Date().toISOString();\n\nreturn posts.map((post, index) => {\n const sourcePost = topPosts[index % Math.max(topPosts.length, 1)] || {};\n\n return {\n json: {\n generated_at: generatedAt,\n source_profile: data.source_profile,\n total_posts_analyzed: data.total_posts_found,\n source_top_post_rank: sourcePost.rank ?? '',\n source_top_post_text: sourcePost.text ?? '',\n source_top_post_impressions: sourcePost.impressions ?? '',\n source_top_post_reactions: sourcePost.reactions ?? '',\n source_top_post_comments: sourcePost.comments ?? '',\n source_top_post_reposts: sourcePost.reposts ?? '',\n insight_1: insights[0] ?? '',\n insight_2: insights[1] ?? '',\n insight_3: insights[2] ?? '',\n insight_4: insights[3] ?? '',\n new_post_number: index + 1,\n new_post_title: post.title ?? '',\n new_post_hook: post.hook ?? '',\n new_post_text: post.post_text ?? '',\n new_post_cta: post.cta ?? '',\n format_type: post.format_type ?? '',\n why_it_should_work: post.why_it_should_work ?? ''\n }\n };\n});"
},
"typeVersion": 2
},
{
"id": "4c0aca70-fedd-46f7-b269-a69d7015b1a8",
"name": "Parse OpenAI Output",
"type": "n8n-nodes-base.code",
"position": [
592,
1600
],
"parameters": {
"jsCode": "const response = $json;\n\nconst rawText = response?.output?.[0]?.content?.[0]?.text ?? response?.output_text ?? '';\n\nif (!rawText) {\n throw new Error('No usable text found in the OpenAI response.');\n}\n\nlet parsed;\ntry {\n parsed = JSON.parse(rawText);\n} catch (error) {\n throw new Error(`OpenAI JSON could not be parsed: ${error.message}\\n\\nRaw response:\\n${rawText}`);\n}\n\nconst source = $('Normalize + Rank LinkedIn Posts').first().json;\n\nreturn [\n {\n json: {\n source_profile: source.source_profile,\n total_posts_found: source.total_posts_found,\n top_posts: source.top_posts,\n insights: parsed.insights,\n generated_posts: parsed.posts,\n generated_count: parsed.posts.length\n }\n }\n];"
},
"typeVersion": 2
},
{
"id": "b1233856-aa7b-4ff4-94e2-69497559efad",
"name": "OpenAI Analyze + Generate",
"type": "n8n-nodes-base.httpRequest",
"position": [
304,
1600
],
"parameters": {
"url": "https://api.openai.com/v1/responses",
"method": "POST",
"options": {
"response": {
"response": {}
}
},
"jsonBody": "={{ {\n model: 'gpt-5.4-mini',\n input: [\n {\n role: 'system',\n content: 'You are a LinkedIn content strategist and copywriter. Analyze top-performing LinkedIn posts, extract useful patterns, and create original post ideas that fit the platform. Return only valid JSON matching the required schema.'\n },\n {\n role: 'user',\n content: $json.analysis_prompt\n }\n ],\n text: {\n format: {\n type: 'json_schema',\n name: 'linkedin_content_engine_output',\n strict: true,\n schema: {\n type: 'object',\n properties: {\n insights: {\n type: 'array',\n minItems: 3,\n maxItems: 10,\n items: { type: 'string' }\n },\n posts: {\n type: 'array',\n minItems: 7,\n maxItems: 7,\n items: {\n type: 'object',\n properties: {\n title: { type: 'string' },\n hook: { type: 'string' },\n post_text: { type: 'string' },\n cta: { type: 'string' },\n format_type: { type: 'string' },\n why_it_should_work: { type: 'string' }\n },\n required: ['title', 'hook', 'post_text', 'cta', 'format_type', 'why_it_should_work'],\n additionalProperties: false\n }\n }\n },\n required: ['insights', 'posts'],\n additionalProperties: false\n }\n }\n }\n} }}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "=Bearer {{$node[\"Set Config\"].json[\"openai_api_key\"]}}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "e28d19a2-b9df-495c-a6a3-b12df0fd19dd",
"name": "Normalize + Rank LinkedIn Posts",
"type": "n8n-nodes-base.code",
"position": [
32,
1600
],
"parameters": {
"jsCode": "const items = $input.all();\n\nconst posts = items\n .map((item, index) => {\n const p = item.json;\n\n const text = (\n p.text ??\n p.postText ??\n p.content ??\n p.caption ??\n p.description ??\n p.fullText ??\n p.body ??\n p.linkedinText ??\n p.postContent ??\n ''\n ).toString().trim();\n\n const impressions = Number(\n p.impressions ??\n p.impressionCount ??\n p.metrics?.impressions ??\n p.statistics?.impressions ??\n p.analytics?.impressions ??\n p.views ??\n p.viewCount ??\n p.engagementStats?.impressions ??\n 0\n );\n\n const reactions = Number(\n p.reactions ??\n p.reactionCount ??\n p.likes ??\n p.likeCount ??\n p.metrics?.reactions ??\n p.statistics?.reactions ??\n 0\n );\n\n const comments = Number(\n p.comments ??\n p.commentCount ??\n p.metrics?.comments ??\n p.statistics?.comments ??\n 0\n );\n\n const reposts = Number(\n p.reposts ??\n p.shares ??\n p.shareCount ??\n p.metrics?.shares ??\n p.statistics?.shares ??\n 0\n );\n\n return {\n rank: 0,\n id: p.id ?? p.postId ?? p.urn ?? `post_${index + 1}`,\n text,\n impressions,\n reactions,\n comments,\n reposts,\n post_url: p.url ?? p.postUrl ?? p.linkedinUrl ?? '',\n published_at: p.publishedAt ?? p.createdAt ?? p.timestamp ?? '',\n raw: p\n };\n })\n .filter((post) => post.text && !Number.isNaN(post.impressions));\n\nif (!posts.length) {\n throw new Error('No LinkedIn posts with usable text and impression data were found in the Apify response.');\n}\n\nposts.sort((a, b) => b.impressions - a.impressions);\nposts.forEach((post, index) => {\n post.rank = index + 1;\n});\n\nconst topPosts = posts.slice(0, Math.min(5, posts.length));\n\nconst summaryForPrompt = topPosts.map((post) => ({\n rank: post.rank,\n id: post.id,\n impressions: post.impressions,\n reactions: post.reactions,\n comments: post.comments,\n reposts: post.reposts,\n text: post.text\n}));\n\nconst analysisPrompt = `Analyze the following top-performing LinkedIn posts ranked by impressions.\\n\\nTop posts:\\n${JSON.stringify(summaryForPrompt, null, 2)}\\n\\nTasks:\\n1. Identify patterns in hooks, tone, structure, readability, CTA style, formatting, topic angle, and repeated language choices.\\n2. Summarize the most important success factors.\\n3. Write 7 completely new LinkedIn posts inspired by these patterns, but do not copy phrasing or structure too closely.\\n4. Write in natural, polished English for a professional audience.\\n5. Make the posts feel native to LinkedIn: strong opening line, short readable paragraphs, clear idea progression, and thoughtful closing line.\\n6. Avoid filler, generic motivation, and repeated wording.\\n7. Return only JSON matching the schema.`;\n\nreturn [\n {\n json: {\n source_profile: $('Validate Config + Build Actor Input').first().json.linkedin_profile_url,\n total_posts_found: posts.length,\n top_posts: topPosts,\n summary_for_prompt: summaryForPrompt,\n analysis_prompt: analysisPrompt\n }\n }\n];"
},
"typeVersion": 2
},
{
"id": "b60aabbb-da40-4375-aa04-c0d1f5e6801f",
"name": "Apify Get LinkedIn Posts",
"type": "n8n-nodes-base.httpRequest",
"position": [
-256,
1600
],
"parameters": {
"url": "=https://api.apify.com/v2/acts/{{$json.apify_actor_id}}/run-sync-get-dataset-items?token={{$json.apify_token}}&clean=true&format=json",
"method": "POST",
"options": {
"response": {
"response": {}
}
},
"jsonBody": "={{ $json.actor_input }}",
"sendBody": true,
"specifyBody": "json"
},
"typeVersion": 4.2
},
{
"id": "cd361e5b-c1ca-448d-ab98-ec9c4b55a608",
"name": "Validate Config + Build Actor Input",
"type": "n8n-nodes-base.code",
"position": [
-544,
1600
],
"parameters": {
"jsCode": "const config = $input.first().json;\n\nconst requiredFields = ['apify_token', 'apify_actor_id', 'openai_api_key', 'linkedin_profile_url'];\nconst missing = requiredFields.filter((field) => {\n const value = config[field];\n return value === undefined || value === null || value === '' || String(value).startsWith('replace_with_your_');\n});\n\nif (missing.length) {\n throw new Error(`Missing required configuration fields: ${missing.join(', ')}`);\n}\n\nreturn [\n {\n json: {\n ...config,\n actor_input: {\n profileUrls: [config.linkedin_profile_url],\n resultsLimit: Number(config.results_limit || 20)\n }\n }\n }\n];"
},
"typeVersion": 2
},
{
"id": "19a507b5-03d2-4196-bc5b-ec050fdb9bf2",
"name": "Set Config",
"type": "n8n-nodes-base.set",
"position": [
-784,
1600
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "cfg-apify-token",
"name": "apify_token",
"type": "string",
"value": "replace_with_your_apify_token"
},
{
"id": "cfg-apify-actor-id",
"name": "apify_actor_id",
"type": "string",
"value": "replace_with_your_linkedin_actor_id"
},
{
"id": "cfg-openai-key",
"name": "openai_api_key",
"type": "string",
"value": "replace_with_your_openai_api_key"
},
{
"id": "cfg-linkedin-profile-url",
"name": "linkedin_profile_url",
"type": "string",
"value": "https://www.linkedin.com/in/your-profile/"
},
{
"id": "cfg-results-limit",
"name": "results_limit",
"type": "number",
"value": 20
}
]
}
},
"typeVersion": 3.4
},
{
"id": "17179595-096a-44fe-a18b-e6ffa6f585b4",
"name": "Manual Trigger",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-1008,
1600
],
"parameters": {},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "4ea02da9-1233-45c8-aaa8-be2c720ef809",
"connections": {
"Set Config": {
"main": [
[
{
"node": "Validate Config + Build Actor Input",
"type": "main",
"index": 0
}
]
]
},
"Manual Trigger": {
"main": [
[
{
"node": "Set Config",
"type": "main",
"index": 0
}
]
]
},
"Parse OpenAI Output": {
"main": [
[
{
"node": "Prepare Google Sheets Rows",
"type": "main",
"index": 0
}
]
]
},
"Apify Get LinkedIn Posts": {
"main": [
[
{
"node": "Normalize + Rank LinkedIn Posts",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Analyze + Generate": {
"main": [
[
{
"node": "Parse OpenAI Output",
"type": "main",
"index": 0
}
]
]
},
"Prepare Google Sheets Rows": {
"main": [
[
{
"node": "Append Rows to Google Sheet",
"type": "main",
"index": 0
}
]
]
},
"Normalize + Rank LinkedIn Posts": {
"main": [
[
{
"node": "OpenAI Analyze + Generate",
"type": "main",
"index": 0
}
]
]
},
"Validate Config + Build Actor Input": {
"main": [
[
{
"node": "Apify Get LinkedIn Posts",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
googleSheetsOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow helps you turn your best-performing LinkedIn posts into a repeatable content creation system. It pulls LinkedIn post data from Apify, normalizes and ranks the posts by impressions, identifies the strongest-performing content, and sends the top posts to OpenAI for…
Source: https://n8n.io/workflows/14710/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Automate LinkedIn lead generation by scraping comments from targeted posts and enriching profiles with detailed data
This automated n8n workflow scrapes job listings from Upwork using Apify, processes and cleans the data, and generates daily email reports with job summaries. The system uses Google Sheets for data st
Transform LinkedIn profile URLs into comprehensive enriched lead profiles, quickly and automatically.
Transform any website into a structured knowledge repository with this intelligent crawler that extracts hyperlinks from the homepage, intelligently filters images and content pages, and aggregates fu
Content creators, researchers, educators, and digital marketers who need to discover high-quality YouTube training videos on specific topics. Perfect for building curated learning resource lists, comp