AutomationFlowsSlack & Telegram › Automate Google Ads Copy Optimization with Channable Feed and Relevance AI

Automate Google Ads Copy Optimization with Channable Feed and Relevance AI

ByNikan Noorafkan @nikkannoora on n8n.io

This workflow automatically analyzes your Google Ads performance every month, identifies top-performing themes and categories, and regenerates optimized ad copy using Relevance AI — powered by insights from your Channable product feed.

Cron / scheduled trigger★★★★☆ complexity16 nodesHTTP RequestGoogle SheetsSlack
Slack & Telegram Trigger: Cron / scheduled Nodes: 16 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #10058 — 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 →

Download .json
{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "cdb4f09e-e52a-4d5f-ad12-519db50c1234",
      "name": "Monthly Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "notes": "Triggers on 1st of every month at midnight for 30-day performance review",
      "position": [
        300,
        1720
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 0 1 * *"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "8e3a3528-4cce-49c8-8b9d-412d24a5a595",
      "name": "Get Google Ads Performance Data",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "CORRECTED: Uses HTTP Request with GAQL query instead of limited native Google Ads node. Returns all ads with min 100 impressions for statistical validity.",
      "position": [
        660,
        1740
      ],
      "parameters": {
        "url": "=https://googleads.googleapis.com/{{$env.GOOGLE_ADS_API_VERSION}}/customers/{{$env.GOOGLE_ADS_CUSTOMER_ID}}/googleAds:search",
        "method": "POST",
        "options": {
          "timeout": 60000
        },
        "jsonBody": "={\n  \"query\": \"SELECT ad_group_ad.ad.id, ad_group_ad.ad.responsive_search_ad.headlines, ad_group_ad.ad.responsive_search_ad.descriptions, ad_group.name, campaign.name, metrics.impressions, metrics.clicks, metrics.ctr, metrics.conversions, metrics.cost_micros FROM ad_group_ad WHERE segments.date DURING LAST_30_DAYS AND metrics.impressions > 100 ORDER BY metrics.clicks DESC LIMIT 10000\"\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleAdsOAuth2Api"
      },
      "credentials": {
        "googleAdsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "78baf9e9-5e1b-424c-ac5c-6bf33c3a9fa5",
      "name": "Calculate Performance Metrics",
      "type": "n8n-nodes-base.code",
      "notes": "Analyzes GAQL response to identify patterns by category and theme. Calculates CTR, conversion rates, top/bottom performers.",
      "position": [
        1060,
        1760
      ],
      "parameters": {
        "jsCode": "// Calculate performance metrics and identify patterns\n\nconst items = $input.all();\nconst results = items[0].json.results || [];\n\n// Group ads by category and theme\nconst categoryPerformance = {};\nconst themePerformance = {};\n\nresults.forEach(result => {\n  const adGroup = result.adGroup?.name || 'Unknown';\n  const headlines = result.adGroupAd?.ad?.responsiveSearchAd?.headlines || [];\n  const headline = headlines[0]?.text || '';\n  const ctr = parseFloat(result.metrics?.ctr || 0);\n  const impressions = parseInt(result.metrics?.impressions || 0);\n  const clicks = parseInt(result.metrics?.clicks || 0);\n  const conversions = parseFloat(result.metrics?.conversions || 0);\n  const cost = parseFloat(result.metrics?.costMicros || 0) / 1000000;\n  \n  // Only include ads with sufficient data\n  if (impressions < 100) return;\n  \n  // Aggregate by category (ad group)\n  if (!categoryPerformance[adGroup]) {\n    categoryPerformance[adGroup] = {\n      total_impressions: 0,\n      total_clicks: 0,\n      total_conversions: 0,\n      total_cost: 0,\n      ad_count: 0\n    };\n  }\n  \n  categoryPerformance[adGroup].total_impressions += impressions;\n  categoryPerformance[adGroup].total_clicks += clicks;\n  categoryPerformance[adGroup].total_conversions += conversions;\n  categoryPerformance[adGroup].total_cost += cost;\n  categoryPerformance[adGroup].ad_count += 1;\n  \n  // Extract themes from headlines\n  const themes = ['vegan', 'organic', 'natural', 'premium', 'budget', 'sale', 'new', 'bestseller', 'free shipping'];\n  const headlineLower = headline.toLowerCase();\n  \n  themes.forEach(theme => {\n    if (headlineLower.includes(theme)) {\n      if (!themePerformance[theme]) {\n        themePerformance[theme] = {\n          total_impressions: 0,\n          total_clicks: 0,\n          total_conversions: 0,\n          ad_count: 0\n        };\n      }\n      themePerformance[theme].total_impressions += impressions;\n      themePerformance[theme].total_clicks += clicks;\n      themePerformance[theme].total_conversions += conversions;\n      themePerformance[theme].ad_count += 1;\n    }\n  });\n});\n\n// Calculate averages\nconst categoryMetrics = Object.entries(categoryPerformance).map(([category, data]) => ({\n  category,\n  avg_ctr: ((data.total_clicks / data.total_impressions) * 100).toFixed(2),\n  total_impressions: data.total_impressions,\n  total_clicks: data.total_clicks,\n  conversion_rate: data.total_clicks > 0 ? ((data.total_conversions / data.total_clicks) * 100).toFixed(2) : '0.00',\n  avg_cost_per_click: data.total_clicks > 0 ? (data.total_cost / data.total_clicks).toFixed(2) : '0.00',\n  ad_count: data.ad_count\n}));\n\nconst themeMetrics = Object.entries(themePerformance).map(([theme, data]) => ({\n  theme,\n  avg_ctr: ((data.total_clicks / data.total_impressions) * 100).toFixed(2),\n  total_impressions: data.total_impressions,\n  total_clicks: data.total_clicks,\n  conversion_rate: data.total_clicks > 0 ? ((data.total_conversions / data.total_clicks) * 100).toFixed(2) : '0.00',\n  ad_count: data.ad_count\n}));\n\n// Sort by CTR\ncategoryMetrics.sort((a, b) => parseFloat(b.avg_ctr) - parseFloat(a.avg_ctr));\nthemeMetrics.sort((a, b) => parseFloat(b.avg_ctr) - parseFloat(a.avg_ctr));\n\n// Identify top 20% and bottom 20%\nconst topCategories = categoryMetrics.slice(0, Math.max(1, Math.ceil(categoryMetrics.length * 0.2)));\nconst bottomCategories = categoryMetrics.slice(-Math.max(1, Math.ceil(categoryMetrics.length * 0.2)));\n\nconst topThemes = themeMetrics.slice(0, Math.max(1, Math.ceil(themeMetrics.length * 0.2)));\nconst bottomThemes = themeMetrics.slice(-Math.max(1, Math.ceil(themeMetrics.length * 0.2)));\n\nreturn {\n  overall_metrics: {\n    total_ads_analyzed: results.length,\n    date_range: 'Last 30 days',\n    analysis_date: new Date().toISOString()\n  },\n  category_performance: categoryMetrics,\n  theme_performance: themeMetrics,\n  top_performers: {\n    categories: topCategories,\n    themes: topThemes\n  },\n  bottom_performers: {\n    categories: bottomCategories,\n    themes: bottomThemes\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "d5ad62d0-fff7-4d75-8ca4-128f21a3183c",
      "name": "AI Performance Analysis",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "CORRECTED: Uses /agents/trigger with agent_id. AI analyzes patterns and provides insights like 'vegan messaging +23% CTR vs natural'.",
      "position": [
        1560,
        1660
      ],
      "parameters": {
        "url": "={{$env.RELEVANCE_AI_API_URL}}/agents/trigger",
        "method": "POST",
        "options": {
          "timeout": 90000
        },
        "jsonBody": "={\n  \"message\": {\n    \"role\": \"user\",\n    \"content\": \"Analyze this performance data and provide actionable insights. Data: \" + JSON.stringify($json)\n  },\n  \"agent_id\": \"{{$env.RELEVANCE_AGENT_PERFORMANCE_ID}}\"\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "15b0f40c-3422-47f6-b8d8-e901444e5209",
      "name": "Update Knowledge Base",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "CORRECTED: Uses knowledge source ID from env var. Feeds insights to Relevance AI for future ad generation.",
      "position": [
        1840,
        1660
      ],
      "parameters": {
        "url": "={{$env.RELEVANCE_AI_API_URL}}/knowledge/{{$env.RELEVANCE_KNOWLEDGE_SOURCE_ID}}/update",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"data\": {\n    \"insights\": {{JSON.stringify($json.insights || $json.output)}},\n    \"updated_at\": \"{{$now.toISO()}}\",\n    \"top_themes\": {{JSON.stringify($node['Calculate Performance Metrics'].json.top_performers.themes)}}\n  }\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "d9352d9b-16d9-45e2-9ed5-294d49d2faee",
      "name": "Get Updated Product Feed",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Retrieves latest product feed for regenerating ads with performance insights",
      "position": [
        2140,
        1660
      ],
      "parameters": {
        "url": "={{$env.CHANNABLE_API_URL}}/companies/{{$env.CHANNABLE_COMPANY_ID}}/projects/{{$env.CHANNABLE_PROJECT_ID}}/feeds/{{$env.FEED_ID}}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "2a185395-90da-4a6c-a951-9cc8308df67e",
      "name": "Split Into Batches",
      "type": "n8n-nodes-base.splitInBatches",
      "notes": "Process 50 products at a time",
      "position": [
        2460,
        1660
      ],
      "parameters": {
        "options": {},
        "batchSize": 50
      },
      "typeVersion": 3
    },
    {
      "id": "7e26aa2c-0630-43c6-80a3-6e873d8614bb",
      "name": "Regenerate Ad Copy with Insights",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "CORRECTED: Uses /trigger endpoint. AI now automatically uses performance insights from knowledge base.",
      "position": [
        2760,
        1660
      ],
      "parameters": {
        "url": "={{$env.RELEVANCE_AI_API_URL}}/tools/{{$env.RELEVANCE_TOOL_AD_COPY_ID}}/trigger",
        "method": "POST",
        "options": {
          "timeout": 60000
        },
        "jsonBody": "={\n  \"params\": {\n    \"product_title\": \"{{$json.title}}\",\n    \"product_description\": \"{{$json.description}}\",\n    \"price\": \"{{$json.price}}\",\n    \"category\": \"{{$json.category}}\",\n    \"brand\": \"{{$json.brand}}\",\n    \"use_performance_insights\": true\n  }\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "9645459f-f2fe-4bd1-abdc-b393a2d0282d",
      "name": "Save Optimized Ads to Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "CORRECTED: Saves optimized ads to Google Sheets for review before publishing",
      "position": [
        3120,
        1660
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Optimized Ads"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{$env.GOOGLE_SHEET_ID}}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "e5484057-503a-4cf5-968c-000c64b0fd04",
      "name": "Generate Performance Report",
      "type": "n8n-nodes-base.code",
      "notes": "Creates detailed performance report with insights and changes made",
      "position": [
        3480,
        1660
      ],
      "parameters": {
        "jsCode": "// Generate performance report\nconst performanceData = $node['AI Performance Analysis'].json;\nconst metricsData = $node['Calculate Performance Metrics'].json;\n\nconst topThemes = metricsData.top_performers?.themes || [];\nconst bottomThemes = metricsData.bottom_performers?.themes || [];\n\nconst report = `# 30-Day Performance Optimization Report\n\n## Executive Summary\nDate: ${new Date().toISOString().split('T')[0]}\nAnalysis Period: Last 30 days\nAds Analyzed: ${metricsData.overall_metrics?.total_ads_analyzed || 0}\n\n## Top Performing Themes\n${topThemes.map((t, i) => `${i+1}. ${t.theme}: ${t.avg_ctr}% CTR (${t.ad_count} ads)`).join('\\n')}\n\n## Underperforming Themes\n${bottomThemes.map((t, i) => `${i+1}. ${t.theme}: ${t.avg_ctr}% CTR (${t.ad_count} ads)`).join('\\n')}\n\n## AI Insights\n${performanceData.output || performanceData.insights || 'Analysis complete'}\n\n## Next Optimization Cycle\nScheduled for: ${new Date(Date.now() + 30*24*60*60*1000).toISOString().split('T')[0]}\n`;\n\nreturn {\n  report_text: report,\n  report_html: report.replace(/\\n/g, '<br>'),\n  generated_at: new Date().toISOString(),\n  top_themes: topThemes,\n  bottom_themes: bottomThemes\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "f3096b96-3daa-4eee-bd52-666d5edb9d21",
      "name": "Email Performance Report",
      "type": "n8n-nodes-base.slack",
      "notes": "Sends performance report to team",
      "position": [
        3740,
        1660
      ],
      "parameters": {
        "text": "={{$json.report_text}}",
        "otherOptions": {}
      },
      "typeVersion": 2.1
    },
    {
      "id": "4dfc8a9d-039e-457e-96df-12811d2e5afb",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -860,
        1600
      ],
      "parameters": {
        "width": 1040,
        "height": 240,
        "content": "# \ud83e\udde0 Google Ads Monthly Optimization (Channable + Google Ads + Relevance AI)\nAutomates your monthly Google Ads optimization using Relevance AI and Channable.  \nAnalyzes ad performance, identifies top/bottom performers, generates AI insights, and refreshes ad copies with data-driven improvements.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "d6c75f5e-bd70-46c8-b02f-48176f69b347",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        280,
        1920
      ],
      "parameters": {
        "width": 960,
        "content": "## \ud83d\udfe8 Stage 1 \u2014 Data Collection & Trigger\n| \u23f0 Monthly Schedule Trigger | \ud83d\udcca Get Google Ads Performance Data | \ud83e\uddee Calculate Performance Metrics |\n|-----------------------------|-----------------------------------|--------------------------------|\n| Runs automatically on the 1st of each month at midnight to review the past 30 days. | Retrieves Google Ads data using GAQL via the API \u2014 includes impressions, clicks, CTR, conversions, and costs. | Processes raw data to compute performance metrics (CTR, conversion rate, CPC). Groups results by ad category and creative theme. |"
      },
      "typeVersion": 1
    },
    {
      "id": "c0bca487-73de-4b13-89dc-de8a59e3bdca",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1400,
        1400
      ],
      "parameters": {
        "width": 960,
        "content": "## \ud83d\udfe8 Stage 2 \u2014 AI Analysis & Knowledge Update\n| \ud83e\udd16 AI Performance Analysis | \ud83e\udde0 Update Knowledge Base | \ud83d\udce6 Get Updated Product Feed |\n|----------------------------|--------------------------|-----------------------------|\n| Uses Relevance AI\u2019s `/agents/trigger` endpoint to analyze metrics and extract actionable insights. Example: *\u201cVegan-themed ads show +23% CTR vs average.\u201d* | Feeds AI-generated insights back into your Relevance AI knowledge base for future ad optimization cycles. | Retrieves the latest Channable product feed to prepare updated ads using the new performance insights. |"
      },
      "typeVersion": 1
    },
    {
      "id": "65852982-9233-491d-b9ab-2a25cb712056",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2400,
        1880
      ],
      "parameters": {
        "width": 940,
        "height": 140,
        "content": "## \ud83d\udfe8 Stage 3 \u2014 Ad Copy Regeneration & Storage\n| \u2702\ufe0f Split Into Batches | \u270d\ufe0f Regenerate Ad Copy with Insights | \ud83d\udcc4 Save Optimized Ads to Sheets |\n|------------------------|------------------------------------|--------------------------------|\n| Splits the product feed into batches of 50 to optimize efficiently. | Uses Relevance AI `/tools/{id}/trigger` to rewrite ad copy for each product, incorporating fresh performance data. | Saves regenerated ads to a Google Sheet for QA review or manual approval before publishing. |"
      },
      "typeVersion": 1
    },
    {
      "id": "de84f6a1-a333-4df9-b6a4-d9684396596d",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3980,
        1640
      ],
      "parameters": {
        "width": 620,
        "height": 140,
        "content": "## \ud83d\udfe8 Stage 4 \u2014 Reporting & Communication\n| \ud83d\udcc8 Generate Performance Report | \ud83d\udce2 Email Performance Report |\n|-------------------------------|-----------------------------|\n| Generates a comprehensive monthly performance report summarizing top/bottom themes, CTR trends, and AI recommendations. | Sends a Slack message (or email) to the marketing team containing the full report and optimization insights. |"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Split Into Batches": {
      "main": [
        [
          {
            "node": "Regenerate Ad Copy with Insights",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Knowledge Base": {
      "main": [
        [
          {
            "node": "Get Updated Product Feed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Performance Analysis": {
      "main": [
        [
          {
            "node": "Update Knowledge Base",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Updated Product Feed": {
      "main": [
        [
          {
            "node": "Split Into Batches",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Monthly Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get Google Ads Performance Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Performance Report": {
      "main": [
        [
          {
            "node": "Email Performance Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Optimized Ads to Sheets": {
      "main": [
        [
          {
            "node": "Generate Performance Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Performance Metrics": {
      "main": [
        [
          {
            "node": "AI Performance Analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Google Ads Performance Data": {
      "main": [
        [
          {
            "node": "Calculate Performance Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Regenerate Ad Copy with Insights": {
      "main": [
        [
          {
            "node": "Save Optimized Ads to Sheets",
            "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.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow automatically analyzes your Google Ads performance every month, identifies top-performing themes and categories, and regenerates optimized ad copy using Relevance AI — powered by insights from your Channable product feed.

Source: https://n8n.io/workflows/10058/ — original creator credit. Request a take-down →

More Slack & Telegram workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Slack & Telegram

This workflow contains community nodes that are only compatible with the self-hosted version of n8n.

N8N Nodes Scrapegraphai, HTTP Request, Google Sheets +2
Slack & Telegram

Simplify financial oversight with this automated n8n workflow. Triggered daily, it fetches cash flow and expense data from a Google Sheet, analyzes inflows and outflows, validates records, and generat

HTTP Request, Google Sheets, Email Send +3
Slack & Telegram

Use cases are many: send recurring market updates to investors, distribute new listings context to buyers, or push periodic area snapshots to your client base — all without touching it manually after

HTTP Request, N8N Nodes Exa Official, Slack +1
Slack & Telegram

Automated garden and farm irrigation system that uses weather forecasts and evapotranspiration calculations to determine optimal watering schedules, preventing water waste while maintaining healthy pl

OpenWeatherMap, Google Sheets, HTTP Request +1
Slack & Telegram

This workflow automatically monitors competitor affiliate programs twice daily using Bright Data's web scraping API to extract commission rates, cookie durations, average order values, and payout term

HTTP Request, Google Sheets, Slack +1