01 Nutze den AI-Media-Buyer zur Analyse der Facebook-Werbeanzeigenleistung und sende Erkenntnisse an Google Sheets
Dies ist ein Market Research, AI Summarization-Bereich Automatisierungsworkflow mit 34 Nodes. Hauptsächlich werden If, Set, Code, Sort, Merge und andere Nodes verwendet. Facebook-Werbung mit Gemini AI analysieren und Erkenntnisse in Google Sheets senden
- •Möglicherweise sind Ziel-API-Anmeldedaten erforderlich
- •Google Sheets API-Anmeldedaten
- •Google Gemini API Key
Verwendete Nodes (34)
Kategorie
{
"id": "e1a3hFH38UDCqquQ",
"meta": {
"instanceId": "ba66afb641da72f937c53df420ad492fd21f2a9f2c92857275062b3d7831e1b1"
},
"name": "01 Analyze Facebook Ad Performance with an AI Media Buyer and Send Insights to Google Sheets",
"tags": [],
"nodes": [
{
"id": "ce48c496-4d57-41c3-992e-046c1da7d6f6",
"name": "Bei Klick auf 'Workflow testen'",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-3184,
768
],
"parameters": {},
"typeVersion": 1
},
{
"id": "89a2936e-7719-4cd8-8638-7db0d13e1a3f",
"name": "Alles in String umwandeln",
"type": "n8n-nodes-base.code",
"position": [
-96,
448
],
"parameters": {
"jsCode": "// --- START OF CODE NODE 4 SCRIPT (Stringify Output) ---\n// Mode: Run Once for All Items\n// Input: The array of aggregated ad creative performance objects\n\nconst allAggregatedItems = $input.all(); // This is an array of n8n items\n\n// Extract the .json part from each n8n item to get the actual data objects\nconst dataToConvert = allAggregatedItems.map(item => item.json);\n\n// Stringify the array of data objects\n// The 'null, 2' arguments pretty-print the JSON string with an indent of 2 spaces,\n// which can be helpful for readability if you're inspecting it, but not strictly necessary for an LLM.\n// If the LLM prefers a more compact JSON, you can omit 'null, 2'.\nconst jsonString = JSON.stringify(dataToConvert, null, 2);\n\n// Output a single item containing this string\nreturn [{ json: { all_ads_data_string: jsonString } }];\n// --- END OF CODE NODE 4 SCRIPT ---"
},
"typeVersion": 2
},
{
"id": "48d96bc5-b537-4fb0-9f09-e8c75f57a1c2",
"name": "Google Gemini Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
560,
720
],
"parameters": {
"options": {},
"modelName": "models/gemini-2.5-pro-preview-06-05"
},
"credentials": {
"googlePalmApi": {
"id": "T2fKW04zFKdQvjyO",
"name": "Google Gemini(PaLM) Api account"
}
},
"typeVersion": 1
},
{
"id": "e5ce198d-0740-456f-9160-6f7b253cdbe0",
"name": "Structured Output Parser",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
752,
720
],
"parameters": {
"jsonSchemaExample": "[\n {\n \"ad_id\": \"XXX\",\n \"ad_name\": \"Insert Ad Name\",\n \"total_spend_creative\": 490.01,\n \"performance_category\": \"HELL YES\",\n \"justification\": \"This ad is a 'HELL YES' performer. With a significant spend of $490.01 driving 93 purchases, its ROAS of 19.82 massively outperforms the benchmark ROAS of 8.5. Furthermore, its Cost Per Purchase of $5.27 is well below the benchmark $7.50, and its 5.26% conversion rate is substantially higher than the benchmark 3.1%. The combination of high volume, exceptional ROAS, and efficiency makes it a clear top driver.\",\n \"key_performance_indicators\": {\n \"ad_spend\": 490.01,\n \"ad_total_purchases\": 93,\n \"ad_total_purchase_value\": 9709.6,\n \"ad_roas\": 19.82,\n \"benchmark_roas\": 8.5,\n \"ad_conversion_rate\": \"5.26%\",\n \"benchmark_conversion_rate\": \"3.1%\",\n \"ad_cost_per_purchase\": 5.27,\n \"benchmark_cost_per_purchase\": 7.50\n },\n \"recommendation\": \"Aggressively Scale Budget & Explore New Audiences\"\n }\n]"
},
"typeVersion": 1.2
},
{
"id": "733a2556-a9d9-4892-9acb-6fcdb32a326e",
"name": "Aufteilen",
"type": "n8n-nodes-base.splitOut",
"position": [
944,
528
],
"parameters": {
"options": {},
"fieldToSplitOut": "output"
},
"typeVersion": 1
},
{
"id": "aa9a05cc-532f-4f6f-844b-5511178b1722",
"name": "Rohe Daten an ein Google Sheet senden",
"type": "n8n-nodes-base.googleSheets",
"position": [
272,
-16
],
"parameters": {
"operation": "appendOrUpdate",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultUrl": "",
"cachedResultName": ""
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "XXXX"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "8dzgEvzMeMhDaVGY",
"name": "Google Sheets account"
}
},
"typeVersion": 4.5
},
{
"id": "0cbd26d6-c758-47f8-bca1-190d4af58626",
"name": "Haftnotiz1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2992,
448
],
"parameters": {
"color": 4,
"width": 1740,
"height": 756,
"content": "### Step 1: Securely Manage Your Facebook API Token\n\nThis section retrieves your long-term access token and automatically refreshes it if it's about to expire, ensuring the workflow always has valid credentials.\n\n**➡️ Action Required:**\n- Configure the NocoDB nodes with your database details.\n- Alternatively, replace this entire section with your preferred credential management system (e.g., n8n's own credential store)."
},
"typeVersion": 1
},
{
"id": "7b2463ab-0eac-4bd5-8185-744ea231ef1d",
"name": "Langfristiges Token abrufen",
"type": "n8n-nodes-base.nocoDb",
"position": [
-2928,
768
],
"parameters": {
"table": "mlkymmbtoa2iz72",
"options": {},
"operation": "getAll",
"projectId": "pfhk9mz7b66t5s2",
"authentication": "nocoDbApiToken"
},
"credentials": {
"nocoDbApiToken": {
"id": "J7sjAo2FWhiSTa4M",
"name": "NocoDB Token account"
}
},
"typeVersion": 3
},
{
"id": "e0d95e0a-4cc2-42a7-9f6d-337bb1dc68b9",
"name": "Ist eine Token-Aktualisierung nötig?",
"type": "n8n-nodes-base.code",
"position": [
-2704,
768
],
"parameters": {
"jsCode": "// Get the first input item (assumes NocoDB returns an array)\nconst tokenData = $input.first()?.json || {};\n\n// Get current time in milliseconds\nconst now = new Date().getTime();\n\n// Parse the token's end date from NocoDB\nconst endDate = new Date(tokenData.end_date).getTime();\n\n// Define 3 days in milliseconds\nconst threeDaysInMs = 3 * 24 * 60 * 60 * 1000;\n\n// Calculate the difference between end date and now\nconst timeToExpiry = endDate - now;\n\n// Determine if the token is within 3 days of expiry and not already expired\nconst needsRefresh = timeToExpiry > 0 && timeToExpiry <= threeDaysInMs;\n\n// Return both the result and debug info\nreturn [{\n json: {\n needs_refresh: needsRefresh,\n time_to_expiry_ms: timeToExpiry,\n expires_at: tokenData.end_date,\n checked_at: new Date().toISOString(),\n }\n}];\n"
},
"typeVersion": 2
},
{
"id": "5f27af56-537f-42f3-9c11-52189918c631",
"name": "Langfristiges Zugriffstoken abrufen1",
"type": "n8n-nodes-base.httpRequest",
"position": [
-2240,
704
],
"parameters": {
"url": "https://graph.facebook.com/v22.0/oauth/access_token",
"method": "POST",
"options": {},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "grant_type",
"value": "fb_exchange_token"
},
{
"name": "client_id"
},
{
"name": "client_secret"
},
{
"name": "fb_exchange_token",
"value": "="
}
]
}
},
"typeVersion": 4.2
},
{
"id": "72f99e3f-1c6c-4186-b0d8-1c10d5c32fa7",
"name": "Ablaufdatum des Tokens berechnen1",
"type": "n8n-nodes-base.code",
"position": [
-2016,
704
],
"parameters": {
"jsCode": "// Input from the previous node\nconst input = $input.first();\nconst tokenData = input.json || {};\n\n// Current time in milliseconds\nconst now = new Date().getTime();\n\n// Token details from input\nconst accessToken = tokenData.access_token || '';\nconst expiresIn = tokenData.expires_in || 5184000; // Default to 60 days (5184000 seconds) if not provided\nconst issuedAt = tokenData.issued_at ? new Date(tokenData.issued_at).getTime() : now; // Use now if issued_at is missing\nconst expiryTime = issuedAt + (expiresIn * 1000); // Convert expires_in to milliseconds\nconst endDate = new Date(expiryTime).toISOString(); // Calculated end date\n\n// Output the end date and access token\nreturn [{\n json: {\n access_token: accessToken,\n end_date: endDate,\n current_time: new Date().toISOString()\n }\n}];"
},
"typeVersion": 2
},
{
"id": "c53eca75-6433-46fb-95ac-3b85a1b6420a",
"name": "Token aktualisieren",
"type": "n8n-nodes-base.nocoDb",
"position": [
-1824,
704
],
"parameters": {
"table": "mlkymmbtoa2iz72",
"fieldsUi": {
"fieldValues": [
{
"fieldName": "=Id",
"fieldValue": "={{ $('Getting Long-Term Token').item.json.Id }}"
},
{
"fieldName": "longTermAccessToken",
"fieldValue": "={{ $json.access_token }}"
},
{
"fieldName": "end_date",
"fieldValue": "={{ $json.end_date }}"
},
{
"fieldName": "current_time",
"fieldValue": "={{ $json.current_time }}"
}
]
},
"operation": "update",
"projectId": "pfhk9mz7b66t5s2",
"authentication": "nocoDbApiToken"
},
"credentials": {
"nocoDbApiToken": {
"id": "J7sjAo2FWhiSTa4M",
"name": "NocoDB Token account"
}
},
"typeVersion": 3
},
{
"id": "d82dc7c6-9dc2-427d-9164-0073ee5b611d",
"name": "Haftnotiz2",
"type": "n8n-nodes-base.stickyNote",
"position": [
48,
-352
],
"parameters": {
"width": 752,
"height": 540,
"content": "### Step 3a: Log Processed Data to Google Sheets\n\nThis node takes the cleaned and calculated performance data for each ad creative and sends it to your Google Sheet.\n\n**Why this step is important:**\nIt creates a complete record of your ad metrics and populates the rows that the AI will analyze and update in a later step.\n\n**➡️ Action Required:**\n- **Connect Credentials**: Ensure your Google Sheets account is connected.\n- **Document ID**: Replace `XXXX` in the *Document ID* field with the ID of your Google Sheet (found in the URL).\n- **Sheet Name**: Select the correct sheet where the data should be sent."
},
"typeVersion": 1
},
{
"id": "fe5643c3-3012-4573-84b7-d11c93261bee",
"name": "Langfristiges Token abrufen1",
"type": "n8n-nodes-base.set",
"position": [
-1872,
976
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "f3604846-252c-438b-8266-36ec0819d3fa",
"name": "longAccessToken",
"type": "string",
"value": "={{ $('Getting Long-Term Token').item.json.longTermAccessToken }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "ce1cff8f-e076-4ec5-9a86-58db01605894",
"name": "Im Tabellenformat aufteilen",
"type": "n8n-nodes-base.code",
"position": [
-896,
528
],
"parameters": {
"jsCode": "// --- START OF CODE NODE 1 SCRIPT (Process Raw Data) ---\n// Mode: Run Once for All Items\n\nconst allN8NInputItems = $input.all(); // Gets all n8n items (usually just one from HTTP Request if pagination combines, or one per page)\nconst finalOutputItems = [];\n\n// Iterate over each n8n item received (typically one, or one per page if HTTP node outputs that way)\nfor (const n8nItem of allN8NInputItems) {\n const facebookResponse = n8nItem.json; // This is an object like: { \"data\": [...], \"paging\": ... }\n\n // Check if the 'data' array exists in the Facebook response\n if (facebookResponse && facebookResponse.data && Array.isArray(facebookResponse.data)) {\n const adsInsightsArray = facebookResponse.data; // This is the array of actual ad insight objects\n\n // Loop through each ad insight object within the 'data' array\n for (const fbData of adsInsightsArray) {\n const output = {};\n\n // Basic Info\n output.ad_id = fbData.ad_id; // <<< --- ADDING AD_ID HERE ---\n output.date_start = fbData.date_start;\n output.date_stop = fbData.date_stop;\n output.campaign_name = fbData.campaign_name;\n output.adset_name = fbData.adset_name;\n output.ad_name = fbData.ad_name;\n output.objective = fbData.objective;\n output.spend = parseFloat(fbData.spend || 0);\n output.impressions = parseInt(fbData.impressions || 0);\n output.clicks = parseInt(fbData.clicks || 0);\n\n // Initialize specific e-commerce metrics\n output.add_to_carts_count = 0;\n output.checkouts_initiated_count = 0;\n output.purchases_count = 0;\n output.purchase_value_total = 0.0;\n\n // --- Define your PREFERRED canonical action_types in order of preference ---\n const PREFERRED_ACTION_ORDER = {\n atc: ['omni_add_to_cart', 'offsite_conversion.fb_pixel_add_to_cart', 'add_to_cart', 'onsite_web_app_add_to_cart', 'onsite_web_add_to_cart'],\n checkout: ['omni_initiated_checkout', 'offsite_conversion.fb_pixel_initiate_checkout', 'initiate_checkout', 'onsite_web_initiate_checkout'],\n purchase_count: ['omni_purchase', 'offsite_conversion.fb_pixel_purchase', 'purchase', 'web_in_store_purchase', 'onsite_web_purchase', 'onsite_web_app_purchase'],\n purchase_value: ['omni_purchase', 'offsite_conversion.fb_pixel_purchase', 'purchase', 'web_in_store_purchase', 'onsite_web_purchase', 'onsite_web_app_purchase']\n };\n\n // --- Helper function to get COUNT for a specific metric from 'actions' ---\n function getActionCount(actionsData, preferredTypes) {\n if (!actionsData) return 0;\n let actionsArray = [];\n if (Array.isArray(actionsData)) {\n actionsArray = actionsData;\n } else if (typeof actionsData === 'object' && actionsData !== null && typeof actionsData.action_type !== 'undefined') {\n actionsArray = [actionsData]; // Handle if 'actions' is a single object\n } else {\n return 0; // Invalid actionsData structure or empty\n }\n\n for (const type of preferredTypes) {\n const action = actionsArray.find(a => a.action_type === type);\n if (action) {\n return parseInt(action.value || 0);\n }\n }\n return 0;\n }\n\n // --- Helper function to get MONETARY VALUE for purchases from 'action_values' ---\n function getPurchaseMonetaryValue(actionValuesData, preferredTypes) {\n if (!actionValuesData || !Array.isArray(actionValuesData)) return 0.0;\n for (const type of preferredTypes) {\n const action = actionValuesData.find(a => a.action_type === type);\n if (action) {\n if (type === 'web_app_in_store_purchase' && parseFloat(action.value || 0) < 1.00) {\n const hasOtherMoreValuablePurchaseType = preferredTypes.some(pt =>\n pt !== 'web_app_in_store_purchase' &&\n actionValuesData.find(av => av.action_type === pt && parseFloat(av.value || 0) >= 1.00)\n );\n if (hasOtherMoreValuablePurchaseType) {\n continue;\n }\n }\n return parseFloat(action.value || 0);\n }\n }\n return 0.0;\n }\n\n // Populate the e-commerce metrics\n output.add_to_carts_count = getActionCount(fbData.actions, PREFERRED_ACTION_ORDER.atc);\n output.checkouts_initiated_count = getActionCount(fbData.actions, PREFERRED_ACTION_ORDER.checkout);\n output.purchases_count = getActionCount(fbData.actions, PREFERRED_ACTION_ORDER.purchase_count);\n output.purchase_value_total = getPurchaseMonetaryValue(fbData.action_values, PREFERRED_ACTION_ORDER.purchase_value);\n \n finalOutputItems.push({ json: output });\n }\n } else {\n console.error(\"Input data structure is not as expected from HTTP Request. 'data' array not found or not an array.\", facebookResponse);\n finalOutputItems.push({ json: { error: \"Facebook response parsing error\", details: \"Input 'data' array not found.\" } });\n }\n}\n\nreturn finalOutputItems;\n// --- END OF CODE NODE 1 SCRIPT (Process Raw Data) ---"
},
"typeVersion": 2
},
{
"id": "83a6b0a8-8422-4705-9576-deca22f7f092",
"name": "Sortieren basierend auf Ausgaben",
"type": "n8n-nodes-base.sort",
"position": [
-304,
448
],
"parameters": {
"options": {},
"sortFieldsUi": {
"sortField": [
{
"order": "descending",
"fieldName": "total_spend"
}
]
}
},
"typeVersion": 1
},
{
"id": "c8bda278-bb77-423b-b2bc-49f23d6d3eb5",
"name": "Haftnotiz3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1072,
272
],
"parameters": {
"width": 512,
"height": 620,
"content": "### Step 6: Update Google Sheets with AI Insights\n\nThis final step takes the AI's analysis for each ad and updates your Google Sheet, matching the ad by its unique `ad_id`.\n\n**➡️ Action Required:**\n- Configure both Google Sheets nodes (**Sending Raw Data To A Google Sheet** and this one) with your **Google Sheet ID**.\n- The first node populates the sheet with raw data, and this node adds the AI analysis columns."
},
"typeVersion": 1
},
{
"id": "fdb93589-5381-4dfc-a0ec-7dbd01b2bc64",
"name": "Haftnotiz4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1232,
272
],
"parameters": {
"color": 3,
"width": 320,
"height": 524,
"content": "### Step 2: Fetch Facebook Ad Data\n\nThis node calls the Facebook Graph API to get performance data for all your ads from the last 28 days.\n\n**➡️ Action Required:**\n- In the URL parameter, replace `act_XXXXXX` with your **Facebook Ad Account ID**."
},
"typeVersion": 1
},
{
"id": "5b88a75e-945c-46b2-92d0-a8ddd399433a",
"name": "Haftnotiz6",
"type": "n8n-nodes-base.stickyNote",
"position": [
-592,
208
],
"parameters": {
"width": 680,
"height": 744,
"content": "### Step 3: Calculate Ad & Benchmark KPIs\n\nThe workflow splits here to perform two crucial calculations in parallel:\n- **Top Path**: Calculates performance metrics for each *individual ad creative*.\n- **Bottom Path**: Calculates the *overall account average* for all sales campaigns to use as a benchmark.\n\nThis comparison is the core of the AI analysis."
},
"typeVersion": 1
},
{
"id": "2fdd3e2f-92fa-4034-841a-500dad9f91bc",
"name": "Nur Sales-Kampagnen filtern",
"type": "n8n-nodes-base.filter",
"position": [
-704,
528
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "7e4103af-52c4-4035-97e5-4f4356312ed6",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.objective }}",
"rightValue": "OUTCOME_SALES"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "b2f42335-8b1e-4d00-a9a8-f7f631f1927d",
"name": "Daten der letzten 28 Tage abrufen, segmentiert nach Kampagne, Adset und Anzeige",
"type": "n8n-nodes-base.httpRequest",
"position": [
-1120,
528
],
"parameters": {
"url": "=https://graph.facebook.com/v22.0/act_XXXXXX/insights",
"options": {
"response": {
"response": {
"responseFormat": "json"
}
}
},
"sendQuery": true,
"sendHeaders": true,
"queryParameters": {
"parameters": [
{
"name": "level",
"value": "ad"
},
{
"name": "fields",
"value": "campaign_name,adset_name,ad_name,ad_id,objective,spend,impressions,clicks,actions,action_values,date_start,date_stop"
},
{
"name": "date_preset",
"value": "=last_28d"
},
{
"name": "limit",
"value": "500"
}
]
},
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "=Bearer {{ $json.accessToken }}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "2dfd3d0c-0988-42df-8dfc-c545188d4ea2",
"name": "Senior Facebook Ads Media Buyer",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
608,
528
],
"parameters": {
"text": "=**Role:** You are a Senior Facebook Ads Media Buyer with extensive experience in e-commerce campaign optimization. Your primary goal is to maximize Return on Ad Spend (ROAS) and overall profitability by identifying high-impact ad creatives and categorizing their performance.\n\n**Context:**\nYou are analyzing ad creative performance for an e-commerce client. You have been provided with two sets of data for the past 14 days:\n1. **\"Ad Creative Performance Data\"**: A JSON string representing an array of objects. Each object details a unique ad creative and its performance metrics (including `total_spend`, `total_purchases`, `total_purchase_value`).\n2. **\"Overall Account Benchmark Data\"**: A JSON string representing a single object. This object contains the aggregated performance and KPIs for all ads in the account over the same period, serving as crucial benchmark values.\n\n**Task:**\nYour task is to rigorously evaluate each ad creative from the \"Ad Creative Performance Data\" against the \"Overall Account Benchmark Data.\" **For every ad creative you evaluate, you MUST compare its key calculated metrics (CTR, CPC, CPM, Cost Per Add To Cart, Cost Per Checkout, Cost Per Purchase, Conversion Rate, ROAS, Average Order Value) directly against the corresponding values in the benchmark data.**\n\nBased on this comparison and the spend rules below, categorize the performance of **each ad creative**.\n\n**Performance Categorization Rules & Spend Thresholds:**\n\n1. **Spend Significance:**\n * Any ad creative with a `total_spend` of **less than $50** CANNOT be categorized as \"HELL YES\", \"YES\", or even \"MAYBE\", regardless of its other metrics. At best, it can be \"NOT REALLY\" if ratios are poor, or \"INSUFFICIENT DATA/SPEND\" if ratios look promising but spend is too low for a definitive positive categorization.\n * To be considered for \"HELL YES\" or \"YES\" categories, an ad creative **MUST have a `total_spend` of $100 or more.**\n\n2. **Performance Categories (evaluate against benchmarks AFTER spend rules):**\n * **\"HELL YES\":** Significant spend (>= $100) AND dramatically outperforms benchmarks across ROAS, CVR, and Cost Per Purchase. Clear 80/20 driver.\n * **\"YES\":** Significant spend (>= $100) AND clearly outperforms benchmarks on ROAS, CVR, and Cost Per Purchase. Reliably contributing.\n * **\"MAYBE\":** Shows promise or mixed results. Spend >= $50. Marginally outperforms benchmarks, or strong on some key metrics but not others. Or, spend < $100 but > $50 with very strong ratios. Requires observation/optimization.\n * **\"NOT REALLY\":** Underperforming. Metrics generally at/below benchmarks. Or, spend < $50 with unpromising ratios. Not a clear winner.\n * **\"WE WASTED MONEY\":** Performing very poorly. Likely spent > $30-$50 (use judgment) with significantly worse metrics than benchmarks (very low ROAS, very high CPP). Pause candidate.\n * **\"INSUFFICIENT DATA/SPEND\":** Spend < $50. Ratios might look okay/good, but not enough data for confident positive categorization (\"MAYBE\", \"YES\", \"HELL YES\").\n\n**Input Data:**\n\n**1. Ad Creative Performance Data (JSON String - LLM needs to parse this):**\n\n{{ $json.data[0].all_ads_data_string }}\n\n2. Overall Account Benchmark Data (JSON String - LLM needs to parse this):\n{{ $json.data[1].benchmarkData }}\n",
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 1.6
},
{
"id": "6256c78d-67fb-4bc1-afb9-07b6191e254d",
"name": "Anzeigendaten und Benchmark-Daten kombinieren",
"type": "n8n-nodes-base.merge",
"position": [
224,
512
],
"parameters": {},
"typeVersion": 3.1
},
{
"id": "88276317-cd5c-4529-8329-51f1fc9bcfb0",
"name": "Haftnotiz7",
"type": "n8n-nodes-base.stickyNote",
"position": [
160,
272
],
"parameters": {
"color": 3,
"width": 356,
"height": 544,
"content": "### Step 4: Prepare Data for AI Analysis\n\nThese nodes take the individual ad data and the overall benchmark data, convert them into clean JSON strings, and merge them together. This prepares a complete package of information to be sent to the AI in a single, context-rich prompt."
},
"typeVersion": 1
},
{
"id": "f44bb155-47a1-422d-b857-6007bbcb9844",
"name": "Anzeigen-Insights in Google Sheets aktualisieren",
"type": "n8n-nodes-base.googleSheets",
"position": [
1184,
528
],
"parameters": {
"columns": {
"value": {
"ad_id": "={{ $json.ad_id }}",
"Justification": "={{ $json.justification }}",
"Recommendation": "={{ $json.recommendation }}",
"Best Performing Ad": "={{ $json.performance_category }}"
},
"schema": [
{
"id": "ad_name",
"type": "string",
"display": true,
"required": false,
"displayName": "ad_name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "ad_id",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "ad_id",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "objective",
"type": "string",
"display": true,
"required": false,
"displayName": "objective",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "total_spend",
"type": "string",
"display": true,
"required": false,
"displayName": "total_spend",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "total_impressions",
"type": "string",
"display": true,
"required": false,
"displayName": "total_impressions",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "total_clicks",
"type": "string",
"display": true,
"required": false,
"displayName": "total_clicks",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "total_add_to_carts",
"type": "string",
"display": true,
"required": false,
"displayName": "total_add_to_carts",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "total_checkouts_initiated",
"type": "string",
"display": true,
"required": false,
"displayName": "total_checkouts_initiated",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "total_purchases",
"type": "string",
"display": true,
"required": false,
"displayName": "total_purchases",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "total_purchase_value",
"type": "string",
"display": true,
"required": false,
"displayName": "total_purchase_value",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "ctr",
"type": "string",
"display": true,
"required": false,
"displayName": "ctr",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "cpc",
"type": "string",
"display": true,
"required": false,
"displayName": "cpc",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "cpm",
"type": "string",
"display": true,
"required": false,
"displayName": "cpm",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "cost_per_add_to_cart",
"type": "string",
"display": true,
"required": false,
"displayName": "cost_per_add_to_cart",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "cost_per_checkout",
"type": "string",
"display": true,
"required": false,
"displayName": "cost_per_checkout",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "cost_per_purchase",
"type": "string",
"display": true,
"required": false,
"displayName": "cost_per_purchase",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "roas",
"type": "string",
"display": true,
"required": false,
"displayName": "roas",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "average_order_value",
"type": "string",
"display": true,
"required": false,
"displayName": "average_order_value",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "conversion_rate",
"type": "string",
"display": true,
"required": false,
"displayName": "conversion_rate",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Best Performing Ad",
"type": "string",
"display": true,
"required": false,
"displayName": "Best Performing Ad",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Justification",
"type": "string",
"display": true,
"required": false,
"displayName": "Justification",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Recommendation",
"type": "string",
"display": true,
"required": false,
"displayName": "Recommendation",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Source_URL",
"type": "string",
"display": true,
"required": false,
"displayName": "Source_URL",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Asset Type",
"type": "string",
"display": true,
"required": false,
"displayName": "Asset Type",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Transcription",
"type": "string",
"display": true,
"required": false,
"displayName": "Transcription",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "hook",
"type": "string",
"display": true,
"required": false,
"displayName": "hook",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "purpose",
"type": "string",
"display": true,
"required": false,
"displayName": "purpose",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "text_captions",
"type": "string",
"display": true,
"required": false,
"displayName": "text_captions",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "disclaimer",
"type": "string",
"display": true,
"required": false,
"displayName": "disclaimer",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Status",
"type": "string",
"display": true,
"required": false,
"displayName": "Status",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "focal_point",
"type": "string",
"display": true,
"required": false,
"displayName": "focal_point",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "colours",
"type": "string",
"display": true,
"required": false,
"displayName": "colours",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "layout",
"type": "string",
"display": true,
"required": false,
"displayName": "layout",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "text_elements",
"type": "string",
"display": true,
"required": false,
"displayName": "text_elements",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "row_number",
"type": "string",
"display": true,
"removed": true,
"readOnly": true,
"required": false,
"displayName": "row_number",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"ad_id"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_9fnWQm3ipnWg3DvP6XD-EnT2e5vZRvVlQyVA0rbNMQ/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "XXXXXX"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "8dzgEvzMeMhDaVGY",
"name": "Google Sheets account"
}
},
"typeVersion": 4.5
},
{
"id": "21dcc80e-ac2f-40ce-9890-6c8ba2ee463c",
"name": "Muss Token aktualisiert werden?",
"type": "n8n-nodes-base.if",
"position": [
-2464,
768
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "73f5e309-6f35-413f-a891-a0bc7185ca60",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.needs_refresh }}",
"rightValue": "true"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "96584b1b-2f7f-46d1-96c7-268c0ea9603b",
"name": "Haftnotiz",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3856,
176
],
"parameters": {
"width": 608,
"height": 1024,
"content": "## AI-Powered Facebook Ad Analysis for E-commerce\n\nThis workflow automates the analysis of your Facebook ad performance, acting as an AI-powered media buyer to give you actionable insights. It fetches your ad data, calculates account-wide benchmarks, and then uses a Large Language Model (LLM) to categorize each ad creative's performance, providing clear justifications and recommendations.\n\n### Who is it for?\nThis template is perfect for:\n- E-commerce Store Owners\n- Digital Marketing Agencies\n- Facebook Ads Media Buyers\n\n### What it does\n1. **Secure Token Management**: Automatically retrieves and refreshes your Facebook long-term access token.\n2. **Fetch Ad Data**: Pulls the last 28 days of ad-level performance data from your Facebook Ads account.\n3. **Process & Clean**: Parses the raw data, standardizes metrics (like purchases and ROAS), and filters for sales-focused campaigns.\n4. **Benchmark Calculation**: Aggregates all data to create an overall performance benchmark for your account (e.g., average Cost Per Purchase, average ROAS).\n5. **AI Analysis**: For each ad, it sends its performance data and the account benchmark to an AI. The AI then evaluates the ad, categorizing it as \"HELL YES,\" \"YES,\" \"MAYBE,\" or \"WE WASTED MONEY\" based on a detailed prompt.\n6. **Output to Google Sheets**: Updates a Google Sheet with the raw performance data and the new AI-generated insights, including the performance category, justification, and recommendation.\n\n### How to set up\n1. **Facebook Credentials**:\n - This workflow uses NocoDB to store and refresh the Facebook token. Set up the **Getting Long-Term Token** and **Updating Token** nodes with your NocoDB credentials, or replace this section with your preferred method for storing credentials.\n2. **Google Credentials**:\n - Configure the **Google Sheets** and **Google Gemini** nodes with your respective API credentials.\n3. **Update Your IDs**:\n - In the **Getting Data For the Past 28 Days...** node, replace `act_XXXXXX` in the URL with your Facebook Ad Account ID.\n - In both Google Sheets nodes (**Sending Raw Data...** and **Updating Ad Insights...**), update the *Document ID* with your Google Sheet's ID.\n4. **Run the Workflow**: Once configured, click 'Test workflow' to run the analysis!"
},
"typeVersion": 1
},
{
"id": "5ce47343-8189-4d1d-9038-f0e0bcaf9802",
"name": "Langfristiges Token extrahieren",
"type": "n8n-nodes-base.set",
"position": [
-1600,
704
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "fe9ab593-0caa-415b-958c-5afea549b1d2",
"name": "longAccessToken",
"type": "string",
"value": "={{ $json.longTermAccessToken }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "5e3dc574-4049-4bee-ae81-8a899b553615",
"name": "Haftnotiz5",
"type": "n8n-nodes-base.stickyNote",
"position": [
528,
272
],
"parameters": {
"color": 7,
"width": 528,
"height": 624,
"content": "### Step 5: AI-Powered Ad Creative Analysis\n\nA powerful LLM (Google Gemini) acts as a Senior Media Buyer. It compares each ad's performance against the account benchmarks and categorizes it with a justification and recommendation.\n\n**➡️ Action Required:**\n- Ensure your Google Gemini credentials are correctly configured in the **Google Gemini Chat Model** node."
},
"typeVersion": 1
},
{
"id": "2eaabe3c-fec3-4804-8f2b-db39a00cb0c4",
"name": "Account-Benchmarks berechnen",
"type": "n8n-nodes-base.code",
"position": [
-464,
784
],
"parameters": {
"jsCode": "// --- START OF CODE NODE 3 SCRIPT ---\n// Input: Output from Code Node 1 (array of processed ad records)\n// Mode: Run Once for All Items\n\nconst allProcessedAdRecords = $input.all(); // Array of n8n items from Code Node 1\n\n// Initialize overall totals\nconst overallTotals = {\n total_spend: 0,\n total_impressions: 0,\n total_clicks: 0,\n total_add_to_carts: 0,\n total_checkouts_initiated: 0,\n total_purchases: 0,\n total_purchase_value: 0,\n // We can't really sum 'objective' or 'ad_name' in a meaningful way for an overall summary,\n // so we'll omit them or set them to a generic value.\n // We also won't sum date_start/date_stop, but we can determine the overall period.\n};\n\nlet minDateStart = null;\nlet maxDateStop = null;\n\n// Step 1: Sum up fundamental metrics from all processed ad records\nfor (const n8nItem of allProcessedAdRecords) {\n const adRecord = n8nItem.json; // Each adRecord is an output from Code Node 1\n\n overallTotals.total_spend += parseFloat(adRecord.spend || 0);\n overallTotals.total_impressions += parseInt(adRecord.impressions || 0);\n overallTotals.total_clicks += parseInt(adRecord.clicks || 0);\n overallTotals.total_add_to_carts += parseInt(adRecord.add_to_carts_count || 0);\n overallTotals.total_checkouts_initiated += parseInt(adRecord.checkouts_initiated_count || 0);\n overallTotals.total_purchases += parseInt(adRecord.purchases_count || 0);\n overallTotals.total_purchase_value += parseFloat(adRecord.purchase_value_total || 0);\n\n // Determine overall date range\n if (adRecord.date_start) {\n const currentStartDate = new Date(adRecord.date_start);\n if (!minDateStart || currentStartDate < minDateStart) {\n minDateStart = currentStartDate;\n }\n }\n if (adRecord.date_stop) {\n const currentStopDate = new Date(adRecord.date_stop);\n if (!maxDateStop || currentStopDate > maxDateStop) {\n maxDateStop = currentStopDate;\n }\n }\n}\n\n// Add overall date range to the output\nif (minDateStart) {\n overallTotals.overall_date_start = minDateStart.toISOString().split('T')[0]; // YYYY-MM-DD\n}\nif (maxDateStop) {\n overallTotals.overall_date_stop = maxDateStop.toISOString().split('T')[0]; // YYYY-MM-DD\n}\n\n\n// Step 2: Calculate overall derived KPIs based on the grand totals\nconst overallKPIs = { ...overallTotals }; // Start with the summed totals\n\noverallKPIs.ctr = overallTotals.total_impressions > 0 ? (overallTotals.total_clicks / overallTotals.total_impressions) : 0;\noverallKPIs.cpc = overallTotals.total_clicks > 0 ? (overallTotals.total_spend / overallTotals.total_clicks) : 0;\noverallKPIs.cpm = overallTotals.total_impressions > 0 ? (overallTotals.total_spend / overallTotals.total_impressions * 1000) : 0;\n\noverallKPIs.cost_per_add_to_cart = overallTotals.total_add_to_carts > 0 ? (overallTotals.total_spend / overallTotals.total_add_to_carts) : 0;\noverallKPIs.cost_per_checkout = overallTotals.total_checkouts_initiated > 0 ? (overallTotals.total_spend / overallTotals.total_checkouts_initiated) : 0;\noverallKPIs.cost_per_purchase = overallTotals.total_purchases > 0 ? (overallTotals.total_spend / overallTotals.total_purchases) : 0;\n\noverallKPIs.roas = overallTotals.total_spend > 0 ? (overallTotals.total_purchase_value / overallTotals.total_spend) : 0;\n\nlet conversionRateDecimal = overallTotals.total_clicks > 0 ? (overallTotals.total_purchases / overallTotals.total_clicks) : 0;\noverallKPIs.average_order_value = overallTotals.total_purchases > 0 ? (overallTotals.total_purchase_value / overallTotals.total_purchases) : 0;\n\n// --- Format numbers for the overall summary ---\noverallKPIs.total_spend = parseFloat(overallKPIs.total_spend.toFixed(2));\noverallKPIs.total_purchase_value = parseFloat(overallKPIs.total_purchase_value.toFixed(2));\n\noverallKPIs.ctr = parseFloat(overallKPIs.ctr.toFixed(4));\noverallKPIs.cpc = parseFloat(overallKPIs.cpc.toFixed(2));\noverallKPIs.cpm = parseFloat(overallKPIs.cpm.toFixed(2));\n\noverallKPIs.cost_per_add_to_cart = parseFloat(overallKPIs.cost_per_add_to_cart.toFixed(2));\noverallKPIs.cost_per_checkout = parseFloat(overallKPIs.cost_per_checkout.toFixed(2));\noverallKPIs.cost_per_purchase = parseFloat(overallKPIs.cost_per_purchase.toFixed(2));\n\noverallKPIs.roas = parseFloat(overallKPIs.roas.toFixed(2));\n\noverallKPIs.conversion_rate = (conversionRateDecimal * 100).toFixed(2) + '%'; // As percentage string\noverallKPIs.average_order_value = parseFloat(overallKPIs.average_order_value.toFixed(2));\n\n// This node will output a single item containing the overall summary\nreturn [{ json: overallKPIs }];\n// --- END OF CODE NODE 3 SCRIPT ---"
},
"typeVersion": 2
},
{
"id": "23411043-d16f-40f4-8fcc-3c6653f3595c",
"name": "Benchmark-Daten in String umwandeln",
"type": "n8n-nodes-base.code",
"position": [
-48,
784
],
"parameters": {
"jsCode": "// --- START OF CODE NODE 4 SCRIPT (Stringify Output) ---\n// Mode: Run Once for All Items\n// Input: The array of aggregated ad creative performance objects\n\nconst allAggregatedItems = $input.all(); // This is an array of n8n items\n\n// Extract the .json part from each n8n item to get the actual data objects\nconst dataToConvert = allAggregatedItems.map(item => item.json);\n\n// Stringify the array of data objects\n// The 'null, 2' arguments pretty-print the JSON string with an indent of 2 spaces,\n// which can be helpful for readability if you're inspecting it, but not strictly necessary for an LLM.\n// If the LLM prefers a more compact JSON, you can omit 'null, 2'.\nconst jsonString = JSON.stringify(dataToConvert, null, 2);\n\n// Output a single item containing this string\nreturn [{ json: { benchmarkData: jsonString } }];\n// --- END OF CODE NODE 4 SCRIPT ---"
},
"typeVersion": 2
},
{
"id": "cf388ebd-dff0-4658-b13a-59619c8cf701",
"name": "Metriken nach Anzeigen-Creative aggregieren",
"type": "n8n-nodes-base.code",
"position": [
-496,
448
],
"parameters": {
"jsCode": "// --- START OF CODE NODE 2 SCRIPT (Aggregate by Ad Creative & KPIs) ---\n// Mode: Run Once for All Items\n// Input: Output from Code Node 1 (which now includes ad_id)\n\nconst allInputItems = $input.all();\nconst aggregatedByAdName = {};\n\n// Step 1: Aggregate metrics by ad_name\nfor (const n8nItem of allInputItems) {\n const itemData = n8nItem.json; // Data from Code Node 1, includes ad_id\n const key = itemData.ad_name;\n\n if (!aggregatedByAdName[key]) {\n aggregatedByAdName[key] = {\n ad_name: itemData.ad_name,\n ad_id: itemData.ad_id, // <<< STORE THE AD_ID OF THE FIRST ENCOUNTERED ITEM\n objective: itemData.objective,\n // Initialize all sums\n total_spend: 0,\n total_impressions: 0,\n total_clicks: 0,\n total_add_to_carts: 0,\n total_checkouts_initiated: 0,\n total_purchases: 0,\n total_purchase_value: 0,\n };\n }\n\n // If the first itemData for an ad_name somehow missed an ad_id (unlikely if Code Node 1 is correct),\n // but a subsequent one has it, this ensures it gets populated.\n if (itemData.ad_id && !aggregatedByAdName[key].ad_id) {\n aggregatedByAdName[key].ad_id = itemData.ad_id;\n }\n // Same for objective, if needed\n if (itemData.objective && !aggregatedByAdName[key].objective) {\n aggregatedByAdName[key].objective = itemData.objective;\n }\n\n // Sum up the metrics\n aggregatedByAdName[key].total_spend += parseFloat(itemData.spend || 0);\n aggregatedByAdName[key].total_impressions += parseInt(itemData.impressions || 0);\n aggregatedByAdName[key].total_clicks += parseInt(itemData.clicks || 0);\n aggregatedByAdName[key].total_add_to_carts += parseInt(itemData.add_to_carts_count || 0);\n aggregatedByAdName[key].total_checkouts_initiated += parseInt(itemData.checkouts_initiated_count || 0);\n aggregatedByAdName[key].total_purchases += parseInt(itemData.purchases_count || 0);\n aggregatedByAdName[key].total_purchase_value += parseFloat(itemData.purchase_value_total || 0);\n}\n\n// Step 2: Calculate derived metrics and prepare final output\nconst finalOutputArray = [];\nfor (const key in aggregatedByAdName) {\n const adCreativeData = aggregatedByAdName[key];\n const outputItem = { ...adCreativeData }; // Includes ad_name, ad_id, objective, and all total_... fields\n\n // Calculate derived metrics, handling division by zero\n outputItem.ctr = adCreativeData.total_impressions > 0 ? (adCreativeData.total_clicks / adCreativeData.total_impressions) : 0;\n outputItem.cpc = adCreativeData.total_clicks > 0 ? (adCreativeData.total_spend / adCreativeData.total_clicks) : 0;\n outputItem.cpm = adCreativeData.total_impressions > 0 ? (adCreativeData.total_spend / adCreativeData.total_impressions * 1000) : 0;\n \n outputItem.cost_per_add_to_cart = adCreativeData.total_add_to_carts > 0 ? (adCreativeData.total_spend / adCreativeData.total_add_to_carts) : 0;\n outputItem.cost_per_checkout = adCreativeData.total_checkouts_initiated > 0 ? (adCreativeData.total_spend / adCreativeData.total_checkouts_initiated) : 0;\n outputItem.cost_per_purchase = adCreativeData.total_purchases > 0 ? (adCreativeData.total_spend / adCreativeData.total_purchases) : 0;\n \n outputItem.roas = adCreativeData.total_spend > 0 ? (adCreativeData.total_purchase_value / adCreativeData.total_spend) : 0;\n\n let conversionRateDecimal = adCreativeData.total_clicks > 0 ? (adCreativeData.total_purchases / adCreativeData.total_clicks) : 0;\n outputItem.average_order_value = adCreativeData.total_purchases > 0 ? (adCreativeData.total_purchase_value / adCreativeData.total_purchases) : 0;\n\n // Format numbers\n outputItem.total_spend = parseFloat(outputItem.total_spend.toFixed(2));\n outputItem.total_purchase_value = parseFloat(outputItem.total_purchase_value.toFixed(2));\n \n outputItem.ctr = parseFloat(outputItem.ctr.toFixed(4));\n outputItem.cpc = parseFloat(outputItem.cpc.toFixed(2));\n outputItem.cpm = parseFloat(outputItem.cpm.toFixed(2));\n \n outputItem.cost_per_add_to_cart = parseFloat(outputItem.cost_per_add_to_cart.toFixed(2));\n outputItem.cost_per_checkout = parseFloat(outputItem.cost_per_checkout.toFixed(2));\n outputItem.cost_per_purchase = parseFloat(outputItem.cost_per_purchase.toFixed(2));\n \n outputItem.roas = parseFloat(outputItem.roas.toFixed(2));\n\n outputItem.conversion_rate = (conversionRateDecimal * 100).toFixed(2) + '%';\n outputItem.average_order_value = parseFloat(outputItem.average_order_value.toFixed(2));\n\n finalOutputArray.push({ json: outputItem });\n}\n\nreturn finalOutputArray;\n// --- END OF CODE NODE 2 SCRIPT ---"
},
"typeVersion": 2
},
{
"id": "3b80f5a8-4aa8-4a65-882b-ebc391da066c",
"name": "Anzeigen- & Benchmark-Daten für LLM kombinieren",
"type": "n8n-nodes-base.aggregate",
"position": [
400,
512
],
"parameters": {
"options": {},
"aggregate": "aggregateAllItemData"
},
"typeVersion": 1
},
{
"id": "dbc2be05-df91-463b-bd18-d935809393a1",
"name": "Zugriffstoken extrahieren",
"type": "n8n-nodes-base.set",
"position": [
-1360,
800
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "a6e2f5e6-eebb-43ea-9c51-9016c673d4da",
"name": "accessToken",
"type": "string",
"value": "={{ $json.longAccessToken }}"
}
]
}
},
"typeVersion": 3.4
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "13e4e553-de65-4188-9e59-6517c3cdcb5c",
"connections": {
"733a2556-a9d9-4892-9acb-6fcdb32a326e": {
"main": [
[
{
"node": "f44bb155-47a1-422d-b857-6007bbcb9844",
"type": "main",
"index": 0
}
]
]
},
"c53eca75-6433-46fb-95ac-3b85a1b6420a": {
"main": [
[
{
"node": "5ce47343-8189-4d1d-9038-f0e0bcaf9802",
"type": "main",
"index": 0
}
]
]
},
"89a2936e-7719-4cd8-8638-7db0d13e1a3f": {
"main": [
[
{
"node": "6256c78d-67fb-4bc1-afb9-07b6191e254d",
"type": "main",
"index": 0
}
]
]
},
"dbc2be05-df91-463b-bd18-d935809393a1": {
"main": [
[
{
"node": "b2f42335-8b1e-4d00-a9a8-f7f631f1927d",
"type": "main",
"index": 0
}
]
]
},
"7b2463ab-0eac-4bd5-8185-744ea231ef1d": {
"main": [
[
{
"node": "e0d95e0a-4cc2-42a7-9f6d-337bb1dc68b9",
"type": "main",
"index": 0
}
]
]
},
"83a6b0a8-8422-4705-9576-deca22f7f092": {
"main": [
[
{
"node": "aa9a05cc-532f-4f6f-844b-5511178b1722",
"type": "main",
"index": 0
},
{
"node": "89a2936e-7719-4cd8-8638-7db0d13e1a3f",
"type": "main",
"index": 0
}
]
]
},
"fe5643c3-3012-4573-84b7-d11c93261bee": {
"main": [
[
{
"node": "dbc2be05-df91-463b-bd18-d935809393a1",
"type": "main",
"index": 0
}
]
]
},
"48d96bc5-b537-4fb0-9f09-e8c75f57a1c2": {
"ai_languageModel": [
[
{
"node": "2dfd3d0c-0988-42df-8dfc-c545188d4ea2",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"23411043-d16f-40f4-8fcc-3c6653f3595c": {
"main": [
[
{
"node": "6256c78d-67fb-4bc1-afb9-07b6191e254d",
"type": "main",
"index": 1
}
]
]
},
"e5ce198d-0740-456f-9160-6f7b253cdbe0": {
"ai_outputParser": [
[
{
"node": "2dfd3d0c-0988-42df-8dfc-c545188d4ea2",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"5ce47343-8189-4d1d-9038-f0e0bcaf9802": {
"main": [
[
{
"node": "dbc2be05-df91-463b-bd18-d935809393a1",
"type": "main",
"index": 0
}
]
]
},
"2eaabe3c-fec3-4804-8f2b-db39a00cb0c4": {
"main": [
[
{
"node": "23411043-d16f-40f4-8fcc-3c6653f3595c",
"type": "main",
"index": 0
}
]
]
},
"21dcc80e-ac2f-40ce-9890-6c8ba2ee463c": {
"main": [
[
{
"node": "5f27af56-537f-42f3-9c11-52189918c631",
"type": "main",
"index": 0
}
],
[
{
"node": "fe5643c3-3012-4573-84b7-d11c93261bee",
"type": "main",
"index": 0
}
]
]
},
"e0d95e0a-4cc2-42a7-9f6d-337bb1dc68b9": {
"main": [
[
{
"node": "21dcc80e-ac2f-40ce-9890-6c8ba2ee463c",
"type": "main",
"index": 0
}
]
]
},
"ce1cff8f-e076-4ec5-9a86-58db01605894": {
"main": [
[
{
"node": "2fdd3e2f-92fa-4034-841a-500dad9f91bc",
"type": "main",
"index": 0
}
]
]
},
"72f99e3f-1c6c-4186-b0d8-1c10d5c32fa7": {
"main": [
[
{
"node": "c53eca75-6433-46fb-95ac-3b85a1b6420a",
"type": "main",
"index": 0
}
]
]
},
"2dfd3d0c-0988-42df-8dfc-c545188d4ea2": {
"main": [
[
{
"node": "733a2556-a9d9-4892-9acb-6fcdb32a326e",
"type": "main",
"index": 0
}
]
]
},
"cf388ebd-dff0-4658-b13a-59619c8cf701": {
"main": [
[
{
"node": "83a6b0a8-8422-4705-9576-deca22f7f092",
"type": "main",
"index": 0
}
]
]
},
"5f27af56-537f-42f3-9c11-52189918c631": {
"main": [
[
{
"node": "72f99e3f-1c6c-4186-b0d8-1c10d5c32fa7",
"type": "main",
"index": 0
}
]
]
},
"ce48c496-4d57-41c3-992e-046c1da7d6f6": {
"main": [
[
{
"node": "7b2463ab-0eac-4bd5-8185-744ea231ef1d",
"type": "main",
"index": 0
}
]
]
},
"2fdd3e2f-92fa-4034-841a-500dad9f91bc": {
"main": [
[
{
"node": "cf388ebd-dff0-4658-b13a-59619c8cf701",
"type": "main",
"index": 0
},
{
"node": "2eaabe3c-fec3-4804-8f2b-db39a00cb0c4",
"type": "main",
"index": 0
}
]
]
},
"3b80f5a8-4aa8-4a65-882b-ebc391da066c": {
"main": [
[
{
"node": "2dfd3d0c-0988-42df-8dfc-c545188d4ea2",
"type": "main",
"index": 0
}
]
]
},
"6256c78d-67fb-4bc1-afb9-07b6191e254d": {
"main": [
[
{
"node": "3b80f5a8-4aa8-4a65-882b-ebc391da066c",
"type": "main",
"index": 0
}
]
]
},
"b2f42335-8b1e-4d00-a9a8-f7f631f1927d": {
"main": [
[
{
"node": "ce1cff8f-e076-4ec5-9a86-58db01605894",
"type": "main",
"index": 0
}
]
]
}
}
}Wie verwende ich diesen Workflow?
Kopieren Sie den obigen JSON-Code, erstellen Sie einen neuen Workflow in Ihrer n8n-Instanz und wählen Sie "Aus JSON importieren". Fügen Sie die Konfiguration ein und passen Sie die Anmeldedaten nach Bedarf an.
Für welche Szenarien ist dieser Workflow geeignet?
Experte - Marktforschung, KI-Zusammenfassung
Ist es kostenpflichtig?
Dieser Workflow ist völlig kostenlos. Beachten Sie jedoch, dass Drittanbieterdienste (wie OpenAI API), die im Workflow verwendet werden, möglicherweise kostenpflichtig sind.
Verwandte Workflows
JJ Tham
@jj-thamFounder of Osinity. I build AI-powered n8n automations that save businesses 10+ hours a week and grow their revenue. We guarantee results with a 30-day risk-free trial—you only pay when we hit your targets.
Diesen Workflow teilen