This workflow corresponds to n8n.io template #17897 — we link there as the canonical source.
This workflow follows the Facebookgraphapi → Google Sheets 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": "rYAWl7zx5rK3nUzt",
"meta": {
"builderVariant": "mcp",
"aiBuilderAssisted": true
},
"name": "Generate Facebook ad sets and ads from Google Sheets rows",
"tags": [],
"nodes": [
{
"id": "b8294a2c-1f40-4b3c-8d20-2d1f41646924",
"name": "Watch Ad Rows in Google Sheets",
"type": "n8n-nodes-base.googleSheetsTrigger",
"position": [
16,
432
],
"parameters": {
"event": "rowUpdate",
"options": {},
"pollTimes": {
"item": [
{
"mode": "everyX",
"unit": "minutes",
"value": 5
}
]
},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultName": "Facebook Ads Queue"
}
},
"credentials": {
"googleSheetsTriggerOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "898faa4e-add2-4536-b681-32252030b9f5",
"name": "Check Row Marked For Generation",
"type": "n8n-nodes-base.if",
"position": [
320,
432
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": false,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $('Watch Ad Rows in Google Sheets').item.json['Generate Ad'] }}",
"rightValue": "generate"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "c4c5588d-1859-43fa-ae32-762ce80b488a",
"name": "Configuration",
"type": "n8n-nodes-base.set",
"position": [
752,
416
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "hooks",
"name": "hooks",
"type": "string",
"value": "={{ $('Watch Ad Rows in Google Sheets').item.json['Hooks'] }}"
},
{
"id": "render_url",
"name": "render_url",
"type": "string",
"value": "={{ $('Watch Ad Rows in Google Sheets').item.json['Render URL'] }}"
},
{
"id": "row_number",
"name": "row_number",
"type": "number",
"value": "={{ $('Watch Ad Rows in Google Sheets').item.json.row_number }}"
},
{
"id": "facebook_ad_account_id",
"name": "facebook_ad_account_id",
"type": "string",
"value": "<__PLACEHOLDER_VALUE__Your Facebook Ad Account ID, including the act_ prefix (e.g. act_1234567890)__>"
},
{
"id": "facebook_page_id",
"name": "facebook_page_id",
"type": "string",
"value": "<__PLACEHOLDER_VALUE__Your Facebook Page ID__>"
},
{
"id": "campaign_id",
"name": "campaign_id",
"type": "string",
"value": "<__PLACEHOLDER_VALUE__Existing Facebook campaign ID this ad set will belong to__>"
},
{
"id": "pixel_id",
"name": "pixel_id",
"type": "string",
"value": "<__PLACEHOLDER_VALUE__Optional: Meta Pixel ID for conversion tracking (leave blank if unused)__>"
},
{
"id": "custom_event_type",
"name": "custom_event_type",
"type": "string",
"value": "PURCHASE"
},
{
"id": "graph_api_version",
"name": "graph_api_version",
"type": "string",
"value": "v21.0"
},
{
"id": "daily_budget_cents",
"name": "daily_budget_cents",
"type": "number",
"value": 1000
},
{
"id": "age_min",
"name": "age_min",
"type": "number",
"value": 18
},
{
"id": "age_max",
"name": "age_max",
"type": "number",
"value": 65
},
{
"id": "target_genders",
"name": "target_genders",
"type": "array",
"value": "={{ [1, 2] }}"
},
{
"id": "target_countries",
"name": "target_countries",
"type": "array",
"value": "={{ [\"US\"] }}"
},
{
"id": "optimization_goal",
"name": "optimization_goal",
"type": "string",
"value": "LINK_CLICKS"
},
{
"id": "billing_event",
"name": "billing_event",
"type": "string",
"value": "IMPRESSIONS"
},
{
"id": "bid_strategy",
"name": "bid_strategy",
"type": "string",
"value": "LOWEST_COST_WITHOUT_CAP"
},
{
"id": "destination_url",
"name": "destination_url",
"type": "string",
"value": "<__PLACEHOLDER_VALUE__https://yourwebsite.com/landing-page__>"
},
{
"id": "cta_type",
"name": "cta_type",
"type": "string",
"value": "LEARN_MORE"
},
{
"id": "ad_set_name_prefix",
"name": "ad_set_name_prefix",
"type": "string",
"value": "AI Generated Ad Set"
},
{
"id": "ad_status",
"name": "ad_status",
"type": "string",
"value": "PAUSED"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "0e058c90-1b04-471c-bc08-77a35168c26c",
"name": "Create Facebook Ad Set",
"type": "n8n-nodes-base.facebookGraphApi",
"onError": "continueErrorOutput",
"maxTries": 3,
"position": [
944,
352
],
"parameters": {
"edge": "adsets",
"node": "={{ $('Configuration').item.json.facebook_ad_account_id }}",
"options": {
"queryParametersJson": "={{\n // Build the ad set payload from the Configuration node.\n (() => {\n const cfg = $(\"Configuration\").item.json;\n const targeting = {\n age_min: cfg.age_min,\n age_max: cfg.age_max,\n genders: cfg.target_genders,\n geo_locations: { countries: cfg.target_countries }\n };\n const payload = {\n name: cfg.ad_set_name_prefix + \" - \" + String(cfg.hooks).slice(0, 40),\n campaign_id: cfg.campaign_id,\n daily_budget: cfg.daily_budget_cents,\n billing_event: cfg.billing_event,\n optimization_goal: cfg.optimization_goal,\n bid_strategy: cfg.bid_strategy,\n targeting: JSON.stringify(targeting),\n status: cfg.ad_status\n };\n if (cfg.pixel_id) {\n payload.promoted_object = JSON.stringify({ pixel_id: cfg.pixel_id, custom_event_type: cfg.custom_event_type });\n }\n return payload;\n })()\n}}"
},
"graphApiVersion": "={{ $('Configuration').item.json.graph_api_version }}",
"httpRequestMethod": "POST"
},
"retryOnFail": true,
"typeVersion": 1,
"waitBetweenTries": 5000
},
{
"id": "824f92a9-4e62-4a53-899d-b3ac4c8e9f4f",
"name": "Upload Ad Image to Facebook",
"type": "n8n-nodes-base.facebookGraphApi",
"onError": "continueErrorOutput",
"maxTries": 3,
"position": [
1184,
336
],
"parameters": {
"edge": "adimages",
"node": "={{ $('Configuration').item.json.facebook_ad_account_id }}",
"options": {
"queryParameters": {
"parameter": [
{
"name": "url",
"value": "={{ $('Configuration').item.json.render_url }}"
}
]
}
},
"graphApiVersion": "={{ $('Configuration').item.json.graph_api_version }}",
"httpRequestMethod": "POST"
},
"retryOnFail": true,
"typeVersion": 1,
"waitBetweenTries": 5000
},
{
"id": "acef61f2-5ff9-44c7-9945-da3fb8113c55",
"name": "Create Facebook Ad Creative",
"type": "n8n-nodes-base.facebookGraphApi",
"onError": "continueErrorOutput",
"maxTries": 3,
"position": [
1472,
320
],
"parameters": {
"edge": "adcreatives",
"node": "={{ $('Configuration').item.json.facebook_ad_account_id }}",
"options": {
"queryParametersJson": "={{\n // Build the ad creative payload, pulling the uploaded image hash.\n (() => {\n const cfg = $(\"Configuration\").item.json;\n const imagesObj = $(\"Upload Ad Image to Facebook\").item.json.images || {};\n const firstImage = Object.values(imagesObj)[0];\n const imageHash = firstImage ? firstImage.hash : \"\";\n const story = {\n page_id: cfg.facebook_page_id,\n link_data: {\n message: cfg.hooks,\n link: cfg.destination_url,\n image_hash: imageHash,\n call_to_action: { type: cfg.cta_type, value: { link: cfg.destination_url } }\n }\n };\n return {\n name: \"AI Creative - \" + String(cfg.hooks).slice(0, 40),\n object_story_spec: JSON.stringify(story)\n };\n })()\n}}"
},
"graphApiVersion": "={{ $('Configuration').item.json.graph_api_version }}",
"httpRequestMethod": "POST"
},
"retryOnFail": true,
"typeVersion": 1,
"waitBetweenTries": 5000
},
{
"id": "9156c0c2-570b-4143-9f8a-2f8134d37143",
"name": "Create Facebook Ad",
"type": "n8n-nodes-base.facebookGraphApi",
"onError": "continueErrorOutput",
"maxTries": 3,
"position": [
1872,
304
],
"parameters": {
"edge": "ads",
"node": "={{ $('Configuration').item.json.facebook_ad_account_id }}",
"options": {
"queryParametersJson": "={{\n (() => {\n const cfg = $(\"Configuration\").item.json;\n return {\n name: \"AI Ad - \" + String(cfg.hooks).slice(0, 40),\n adset_id: $(\"Create Facebook Ad Set\").item.json.id,\n creative: JSON.stringify({ creative_id: $(\"Create Facebook Ad Creative\").item.json.id }),\n status: cfg.ad_status\n };\n })()\n}}"
},
"graphApiVersion": "={{ $('Configuration').item.json.graph_api_version }}",
"httpRequestMethod": "POST"
},
"retryOnFail": true,
"typeVersion": 1,
"waitBetweenTries": 5000
},
{
"id": "80ce5e54-06ac-44e6-8724-bb20f218fefc",
"name": "Update Sheet - Ad Generated",
"type": "n8n-nodes-base.googleSheets",
"position": [
2176,
368
],
"parameters": {
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultName": "Facebook Ads Queue"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "9ea2f084-9d80-41e5-b168-8840089b7881",
"name": "Combine Ad Creation Errors",
"type": "n8n-nodes-base.merge",
"position": [
1536,
736
],
"parameters": {
"numberInputs": 4
},
"typeVersion": 3.2
},
{
"id": "ccdeffd2-e7d3-46d2-bf28-d0898d5919d7",
"name": "Skip - Not Marked For Generation",
"type": "n8n-nodes-base.noOp",
"position": [
560,
448
],
"parameters": {},
"typeVersion": 1
},
{
"id": "01eaacd4-4fb0-4164-9df4-408f2d5f3156",
"name": "Update Sheet - Generation Error",
"type": "n8n-nodes-base.googleSheets",
"position": [
1776,
464
],
"parameters": {
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultName": "Facebook Ads Queue"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "4d72f201-1ab7-4da5-9d03-817305a38f1a",
"name": "Overview & Documentation",
"type": "n8n-nodes-base.stickyNote",
"position": [
-608,
-144
],
"parameters": {
"color": 4,
"width": 460,
"height": 1572,
"content": "# Generate Facebook ad sets and ads from Google Sheets rows\n# \ud83d\udce5 [Open full documentation on Notion](https://automatisation.notion.site/Course-Generate-Facebook-ad-sets-and-ads-from-Google-Sheets-rows-3a03d6550fd981ca954febe5dd9e7c49)\n\n## How it works\nUpdate a row in a Google Sheet (Hooks + Render URL + set \"Generate Ad\" to generate) and this workflow automatically creates a Facebook ad set, uploads the creative image, builds an ad creative, and launches a paused ad - then writes the result back into the sheet.\n\n## Setup\n1. Copy the Google Sheet template (columns: Hooks, Render URL, Generate Ad, Ad ID, Error Message).\n2. Connect your Google Sheets credential and pick your sheet on the trigger and both Update nodes.\n3. Create a Facebook Graph API credential (long-lived access token) and select it on the 4 Facebook nodes.\n4. Open the Configuration node and fill in your Ad Account ID, Page ID, Campaign ID and targeting defaults.\n\n## Requirements\n- A configured Facebook Business account, App, Page and Ad Account\n- A Facebook Graph API access token with ads_management permission\n- Real advertising costs apply once ads are unpaused in Ads Manager\n\n## Customization\n- Add interest-based targeting via a flexible_spec array in the Ad Set payload\n- Swap the single-image creative for a carousel or video creative\n- Wire a Telegram or Gmail node on the error branch for real-time alerts\n\nNeed help customizing?\nContact me for consulting and support : [Linkedin](https://www.linkedin.com/in/doctor-firass/)\n\n# MY NEW YOUTUBE CHANNEL\n\ud83d\udc49 [Subscribe to my new YouTube channel](https://www.youtube.com/@DrFiras_AI). Here I'll share videos and Shorts with practical tutorials and FREE templates for n8n.\n\n[](https://www.youtube.com/@DrFiras_AI)"
},
"typeVersion": 1
},
{
"id": "b39d4f19-0da5-4cf3-b11e-361f08fff8d5",
"name": "Sticky Note 4fccdb14",
"type": "n8n-nodes-base.stickyNote",
"position": [
-48,
256
],
"parameters": {
"color": 7,
"width": 556,
"height": 380,
"content": "### 1. Watch for rows ready to generate\nPolls the sheet and only continues when \"Generate Ad\" is set to generate."
},
"typeVersion": 1
},
{
"id": "df466c2d-38c5-478d-9d16-97125ac188e1",
"name": "Sticky Note fe06719a",
"type": "n8n-nodes-base.stickyNote",
"position": [
608,
80
],
"parameters": {
"color": 7,
"width": 300,
"height": 232,
"content": "### 2. Configuration\nEvery adjustable value lives here: Ad Account, Page, Campaign, targeting defaults, budget, CTA and ad status (defaults to PAUSED for safety)."
},
"typeVersion": 1
},
{
"id": "b0e4bfc5-1dea-4842-8af6-7ee3470e20f3",
"name": "Sticky Note 48b8f0e2",
"type": "n8n-nodes-base.stickyNote",
"position": [
944,
80
],
"parameters": {
"color": 7,
"width": 1044,
"height": 200,
"content": "### 3. Build & launch the ad\nCreates the ad set, uploads the image (Facebook fetches it directly from the Render URL), builds the creative, then creates the ad."
},
"typeVersion": 1
},
{
"id": "6cb0c3cd-6470-4faa-9033-acf5509aa7f2",
"name": "Sticky Note 8c358af9",
"type": "n8n-nodes-base.stickyNote",
"position": [
2048,
160
],
"parameters": {
"color": 7,
"width": 476,
"height": 480,
"content": "### 4. Write the result back\nSuccess writes the Ad ID and marks the row generated; any failure marks the row error with the message."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"availableInMCP": true,
"executionOrder": "v1"
},
"versionId": "d0b8653f-99dc-4a49-9071-0137f34d9b3a",
"nodeGroups": [],
"connections": {
"Configuration": {
"main": [
[
{
"node": "Create Facebook Ad Set",
"type": "main",
"index": 0
}
]
]
},
"Create Facebook Ad": {
"main": [
[
{
"node": "Update Sheet - Ad Generated",
"type": "main",
"index": 0
}
],
[
{
"node": "Combine Ad Creation Errors",
"type": "main",
"index": 3
}
]
]
},
"Create Facebook Ad Set": {
"main": [
[
{
"node": "Upload Ad Image to Facebook",
"type": "main",
"index": 0
}
],
[
{
"node": "Combine Ad Creation Errors",
"type": "main",
"index": 0
}
]
]
},
"Combine Ad Creation Errors": {
"main": [
[
{
"node": "Update Sheet - Generation Error",
"type": "main",
"index": 0
}
]
]
},
"Create Facebook Ad Creative": {
"main": [
[
{
"node": "Create Facebook Ad",
"type": "main",
"index": 0
}
],
[
{
"node": "Combine Ad Creation Errors",
"type": "main",
"index": 2
}
]
]
},
"Upload Ad Image to Facebook": {
"main": [
[
{
"node": "Create Facebook Ad Creative",
"type": "main",
"index": 0
}
],
[
{
"node": "Combine Ad Creation Errors",
"type": "main",
"index": 1
}
]
]
},
"Watch Ad Rows in Google Sheets": {
"main": [
[
{
"node": "Check Row Marked For Generation",
"type": "main",
"index": 0
}
]
]
},
"Check Row Marked For Generation": {
"main": [
[
{
"node": "Configuration",
"type": "main",
"index": 0
}
],
[
{
"node": "Skip - Not Marked For Generation",
"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.
googleSheetsOAuth2ApigoogleSheetsTriggerOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow watches a Google Sheets queue and, when a row is flagged, uses the Meta (Facebook) Graph API to create an ad set, upload an image from a URL, build an ad creative, and create a paused ad, then writes the result or errors back to the sheet. Polls Google Sheets every…
Source: https://n8n.io/workflows/17897/ — 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.
This workflow is a universal automation for all four Meta Detailed Targeting API endpoints: Search, Suggestions, Browse, and Validation. You use a single Google Sheets tab with an column; a Switch nod
d27-FB-post. Uses googleSheets, googleDrive, googleSheetsTrigger, facebookGraphApi. Event-driven trigger; 8 nodes.
This template is designed for Marketing Managers, Performance Marketers, and Ad Ops professionals who want to automate Facebook ad creation using structured data in Google Sheets. It’s ideal for teams
This n8n workflow template automates the entire process of publishing Instagram Reels from content stored in Google Sheets and Google Drive. It's designed for content creators, social media managers,
This n8n workflow automates the process of uploading video and image advertisements to Meta Ads Manager via the Meta Graph API (Facebook Ads) directly from Google Sheets and Google Drive. The workflow