{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "1816092b-c70c-45d8-b235-cb8165807037",
      "name": "Sticky Note - Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -624,
        256
      ],
      "parameters": {
        "width": 420,
        "height": 948,
        "content": "## Cross-Platform Competitive Visual Intelligence\n\n### What This Template Does\nAnalyze visual content from your brand and competitors across Instagram and TikTok using AI-powered image analysis. Get actionable insights on color palettes, composition styles, and visual trends.\n\n### How It Works\n1. **Form Input** - Enter your account and up to 3 competitor accounts\n2. **Platform Routing** - Automatically routes to selected platforms (Instagram/TikTok)\n3. **Content Scraping** - Fetches recent posts using Apify scrapers\n4. **AI Vision Analysis** - GPT-4o analyzes each image for visual attributes\n5. **Report Generation** - Creates comprehensive competitive comparison report\n6. **Logging** - Saves results to Google Sheets for tracking\n\n### Requirements\n- **Apify Account** with API access\n- **OpenAI API Key** (GPT-4o access required)\n- **Google Sheets** for logging results\n\n### Setup Steps\n1. Configure Apify credentials\n2. Add your OpenAI API key in the \"Workflow Configuration\" node\n3. Connect your Google Sheets account and create a sheet with columns: timestamp, own_account, competitors, platforms, posts_analyzed, summary\n4. Update the Google Sheets node with your document ID\n\n### Customization Tips\n- Adjust `postsCount` to analyze more or fewer posts\n- Modify the AI prompt in \"AI Image Analysis\" for different visual attributes\n- Add more platforms by extending the Platform Router"
      },
      "typeVersion": 1
    },
    {
      "id": "8ce94eca-606e-4b60-bf52-dec4bb6a8869",
      "name": "Sticky Note - Step 1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -160,
        528
      ],
      "parameters": {
        "color": 7,
        "width": 280,
        "content": "### Step 1: Form Input\nUsers enter their account handle and competitor accounts. Select which platforms to analyze and how many posts to fetch."
      },
      "typeVersion": 1
    },
    {
      "id": "d66a6c53-9753-46d8-994d-b0a49fa577b3",
      "name": "Sticky Note - Step 2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        896
      ],
      "parameters": {
        "color": 7,
        "height": 128,
        "content": "### Step 2: Configuration\nExtracts and formats input data. \n\n\u26a0\ufe0f **Add your OpenAI API key here**"
      },
      "typeVersion": 1
    },
    {
      "id": "259fac05-849f-4c4f-86c0-ba7e3a5b63fa",
      "name": "Sticky Note - Step 3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        208,
        528
      ],
      "parameters": {
        "color": 7,
        "height": 108,
        "content": "### Step 3: Platform Routing\nRoutes to appropriate scrapers based on selected platforms. Supports parallel execution."
      },
      "typeVersion": 1
    },
    {
      "id": "1076b2f8-16f3-495f-a59b-6f0255cf16ad",
      "name": "Sticky Note - Step 4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        480,
        448
      ],
      "parameters": {
        "color": 7,
        "width": 200,
        "height": 148,
        "content": "### Step 4: Content Scraping\nApify actors fetch posts from each platform. Adjust memory settings if needed for large batches."
      },
      "typeVersion": 1
    },
    {
      "id": "6e8bc7c1-05d0-4674-8764-e0082ceca4ee",
      "name": "Sticky Note - Step 5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        864,
        464
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 180,
        "content": "### Step 5: AI Analysis\nGPT-4o Vision analyzes each image for:\n- Color palette (hex codes)\n- Composition style\n- Mood/emotion\n- Text design elements"
      },
      "typeVersion": 1
    },
    {
      "id": "23ca3d55-fbcb-431f-baf6-03dd048a0ef3",
      "name": "Sticky Note - Step 6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1152,
        544
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 92,
        "content": "### Step 6: Report & Logging\nAggregates results and generates AI-powered competitive analysis report. Logs summary to Google Sheets."
      },
      "typeVersion": 1
    },
    {
      "id": "fa9f137e-b5e8-4a30-8320-f74f383bbd42",
      "name": "Scrape Instagram Posts",
      "type": "@apify/n8n-nodes-apify.apify",
      "position": [
        496,
        624
      ],
      "parameters": {
        "memory": 2048,
        "actorId": {
          "__rl": true,
          "mode": "url",
          "value": "https://console.apify.com/actors/shu8hvrXbJbY3Eb9W/input",
          "__regex": "https://console.apify.com/actors/([a-zA-Z0-9]+).*"
        },
        "timeout": {},
        "operation": "Run actor and get dataset",
        "customBody": "={{ {\n  \"directUrls\": [\n    \"https://www.instagram.com/\" + $('Workflow Configuration1').item.json[\"ownAccount\"] + \"/\",\n    \"https://www.instagram.com/\" + JSON.parse($('Workflow Configuration1').item.json.competitors)[0] + \"/\",\n    \"https://www.instagram.com/\" + JSON.parse($('Workflow Configuration1').item.json.competitors)[1] + \"/\"\n  ].filter(url => !url.includes(\"undefined\") && !url.includes(\"null\")),\n  \"resultsType\": \"posts\",\n  \"resultsLimit\": $('Workflow Configuration1').item.json.postsCount || 10\n} }}",
        "authentication": "apifyOAuth2Api"
      },
      "typeVersion": 1
    },
    {
      "id": "d2f7637e-8d91-465a-937d-ab94b2ce0ad0",
      "name": "Scrape TikTok Posts",
      "type": "@apify/n8n-nodes-apify.apify",
      "position": [
        496,
        800
      ],
      "parameters": {
        "memory": 2048,
        "actorId": {
          "__rl": true,
          "mode": "list",
          "value": "GdWCkxBtKWOsKjdch",
          "cachedResultUrl": "https://console.apify.com/actors/GdWCkxBtKWOsKjdch/input",
          "cachedResultName": "TikTok Scraper (clockworks/tiktok-scraper)"
        },
        "timeout": {},
        "operation": "Run actor and get dataset",
        "customBody": "={{ {\n  \"profiles\": [\n    $('Workflow Configuration1').item.json.ownAccount,\n    JSON.parse($('Workflow Configuration1').item.json.competitors)[0],\n    JSON.parse($('Workflow Configuration1').item.json.competitors)[1]\n  ].filter(p => p),\n  \"resultsPerPage\": $('Workflow Configuration1').item.json.postsCount || 10,\n  \"shouldDownloadVideos\": false\n} }}",
        "authentication": "apifyOAuth2Api"
      },
      "typeVersion": 1
    },
    {
      "id": "9fc5651a-1546-40f6-83b6-45692162aeee",
      "name": "Analyze Images with GPT-4o Vision",
      "type": "n8n-nodes-base.code",
      "position": [
        960,
        720
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Get OpenAI API key from Workflow Configuration node\nconst apiKey = $('Workflow Configuration1').item.json.openaiApiKey;\nconst imageUrl = $input.item.json.displayUrl || $input.item.json.coverUrl;\nconst accountName = $input.item.json.ownerUsername || $input.item.json.authorMeta?.name || 'unknown';\n\n// Detect platform based on data structure\nconst platform = $input.item.json.videoMeta ? 'TikTok' : 'Instagram';\n\n// Call OpenAI GPT-4o Vision API\nconst response = await this.helpers.httpRequest({\n  method: 'POST',\n  url: 'https://api.openai.com/v1/chat/completions',\n  headers: {\n    'Content-Type': 'application/json',\n    'Authorization': `Bearer ${apiKey}`\n  },\n  body: {\n    model: 'gpt-4o',\n    messages: [\n      {\n        role: 'user',\n        content: [\n          {\n            type: 'text',\n            text: 'Analyze this image and extract visual attributes in JSON format: color_palette (array of 3-5 dominant hex colors), composition_style (string), mood_emotion (string), text_design_elements (object with font_style, text_placement, text_content). Return only valid JSON.'\n          },\n          {\n            type: 'image_url',\n            image_url: {\n              url: imageUrl\n            }\n          }\n        ]\n      }\n    ],\n    max_tokens: 500\n  },\n  json: true\n});\n\n// Parse the AI response\nlet analysis;\ntry {\n  const content = response.choices[0].message.content;\n  const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\n  if (jsonMatch) {\n    analysis = JSON.parse(jsonMatch[0]);\n  } else {\n    analysis = JSON.parse(content);\n  }\n} catch (error) {\n  analysis = {\n    color_palette: [],\n    composition_style: 'Unable to analyze',\n    mood_emotion: 'Unable to analyze',\n    text_design_elements: {}\n  };\n}\n\n// Return analyzed image with metadata\nreturn {\n  json: {\n    imageUrl: imageUrl,\n    accountName: accountName,\n    platform: platform,\n    colorPalette: analysis.color_palette || [],\n    compositionStyle: analysis.composition_style || '',\n    moodEmotion: analysis.mood_emotion || '',\n    textDesignElements: analysis.text_design_elements || {},\n    rawAnalysis: response.choices[0].message.content\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "e795ee95-1dc4-42aa-99d3-2a35ce7cd319",
      "name": "Aggregate All Results",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        1184,
        720
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData",
        "destinationFieldName": "analysisResults"
      },
      "typeVersion": 1
    },
    {
      "id": "17c3d793-9833-4f54-9224-857d848034e7",
      "name": "Generate Competitive Analysis Report",
      "type": "n8n-nodes-base.code",
      "position": [
        1408,
        720
      ],
      "parameters": {
        "jsCode": "// Generate Comprehensive Comparison Report using OpenAI\n\nconst items = $input.all();\nconst aggregatedData = items[0].json;\n\n// Get OpenAI API key from Workflow Configuration\nconst openaiApiKey = $('Workflow Configuration1').item.json.openaiApiKey;\n\nif (!openaiApiKey) {\n  throw new Error('OpenAI API key not found in Workflow Configuration');\n}\n\n// Prepare data summary for OpenAI prompt\nconst dataSummary = JSON.stringify(aggregatedData, null, 2);\n\n// Create comprehensive prompt for OpenAI\nconst prompt = `You are a social media visual content strategy expert. Analyze the following data from multiple social media platforms (Instagram, Pinterest, TikTok) and generate a comprehensive comparison report.\n\nData:\n${dataSummary}\n\nPlease provide a detailed report with the following sections:\n\n1. COMPETITIVE COMPARISON MATRIX\n   - Create a matrix showing visual patterns by account\n   - Compare color schemes, composition styles, content types\n   - Highlight top-performing visual elements per account\n\n2. PLATFORM-SPECIFIC WINNING PATTERNS\n   - Instagram: What visual patterns drive the most engagement?\n   - Pinterest: What visual styles get the most saves/repins?\n   - TikTok: What visual elements capture attention?\n\n3. CROSS-PLATFORM INSIGHTS AND TRENDS\n   - Common visual themes across all platforms\n   - Platform-specific vs universal visual strategies\n   - Emerging visual trends identified\n\n4. RECOMMENDED ACTIONS\n   - Specific actionable recommendations to improve visual content strategy\n   - Quick wins that can be implemented immediately\n   - Long-term strategic visual content improvements\n\nFormat the report in a clear, structured manner with headers and bullet points.`;\n\n// Call OpenAI API\nconst response = await this.helpers.httpRequest({\n  method: 'POST',\n  url: 'https://api.openai.com/v1/chat/completions',\n  headers: {\n    'Authorization': `Bearer ${openaiApiKey}`,\n    'Content-Type': 'application/json'\n  },\n  body: {\n    model: 'gpt-4',\n    messages: [\n      {\n        role: 'system',\n        content: 'You are a social media visual content strategy expert specializing in competitive analysis and actionable insights.'\n      },\n      {\n        role: 'user',\n        content: prompt\n      }\n    ],\n    temperature: 0.7,\n    max_tokens: 2500\n  },\n  json: true\n});\n\n// Extract the generated report\nconst report = response.choices[0].message.content;\n\n// Parse report into sections\nconst sections = {\n  fullReport: report,\n  competitiveMatrix: extractSection(report, 'COMPETITIVE COMPARISON MATRIX', 'PLATFORM-SPECIFIC WINNING PATTERNS'),\n  platformPatterns: extractSection(report, 'PLATFORM-SPECIFIC WINNING PATTERNS', 'CROSS-PLATFORM INSIGHTS'),\n  crossPlatformInsights: extractSection(report, 'CROSS-PLATFORM INSIGHTS', 'RECOMMENDED ACTIONS'),\n  recommendedActions: extractSection(report, 'RECOMMENDED ACTIONS', null),\n  generatedAt: new Date().toISOString(),\n  dataAnalyzed: {\n    totalAccounts: aggregatedData.totalAccounts || 0,\n    totalImages: aggregatedData.totalImages || 0,\n    platforms: aggregatedData.platforms || []\n  }\n};\n\n// Helper function to extract sections\nfunction extractSection(text, startMarker, endMarker) {\n  const startIndex = text.indexOf(startMarker);\n  if (startIndex === -1) return '';\n  \n  const contentStart = startIndex + startMarker.length;\n  \n  if (endMarker) {\n    const endIndex = text.indexOf(endMarker, contentStart);\n    if (endIndex === -1) return text.substring(contentStart).trim();\n    return text.substring(contentStart, endIndex).trim();\n  }\n  \n  return text.substring(contentStart).trim();\n}\n\n// Return formatted report with all sections\nreturn [{\n  json: sections\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "907d303b-36c2-48e8-ac06-291d0de9313d",
      "name": "Log Results to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1616,
        720
      ],
      "parameters": {
        "columns": {
          "value": {
            "summary": "={{ $json.fullReport.substring(0, 500) }}",
            "platforms": "={{ $('Workflow Configuration1').item.json.platforms }}",
            "timestamp": "={{ $now.toISO() }}",
            "competitors": "={{ $('Workflow Configuration1').item.json.competitors }}",
            "own_account": "={{ $('Workflow Configuration1').item.json.ownAccount }}",
            "posts_analyzed": "={{ $('Aggregate All Results').item.json.analysisResults.length }}"
          },
          "schema": [
            {
              "id": "timestamp",
              "required": false,
              "displayName": "timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "own_account",
              "required": false,
              "displayName": "own_account",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "competitors",
              "required": false,
              "displayName": "competitors",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "platforms",
              "required": false,
              "displayName": "platforms",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "posts_analyzed",
              "required": false,
              "displayName": "posts_analyzed",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "summary",
              "required": false,
              "displayName": "summary",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "timestamp"
          ]
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "f8bd3bbf-f924-49d2-b0c2-2b2536b30d97",
      "name": "Form Trigger1",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        -160,
        720
      ],
      "parameters": {
        "options": {
          "appendAttribution": false
        },
        "formTitle": "Cross-Platform Competitive Visual Intelligence",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Your Account/Hashtag",
              "requiredField": true
            },
            {
              "fieldLabel": "Competitor 1 Account"
            },
            {
              "fieldLabel": "Competitor 2 Account"
            },
            {
              "fieldLabel": "Competitor 3 Account"
            },
            {
              "fieldType": "checkbox",
              "fieldLabel": "Select Platforms",
              "fieldOptions": {
                "values": [
                  {
                    "option": "Instagram"
                  },
                  {
                    "option": "Pinterest"
                  },
                  {
                    "option": "TikTok"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldType": "number",
              "fieldLabel": "Number of Posts to Analyze",
              "placeholder": "10",
              "requiredField": true
            }
          ]
        },
        "formDescription": "Analyze visual content from your brand and competitors across Instagram, Pinterest, and TikTok"
      },
      "typeVersion": 2.3
    },
    {
      "id": "b61645e7-9be0-4791-ab99-c9cd3b5bcaca",
      "name": "Workflow Configuration1",
      "type": "n8n-nodes-base.set",
      "position": [
        64,
        720
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "ownAccount",
              "type": "string",
              "value": "={{ $json[\"Your Account/Hashtag\"] }}"
            },
            {
              "id": "id-2",
              "name": "competitors",
              "type": "array",
              "value": "={{ JSON.stringify([$json[\"Competitor 1 Account\"], $json[\"Competitor 2 Account\"], $json[\"Competitor 3 Account\"]].filter(c => c)) }}"
            },
            {
              "id": "id-3",
              "name": "platforms",
              "type": "array",
              "value": "={{ $json['Select Platforms'] }}"
            },
            {
              "id": "id-4",
              "name": "postsCount",
              "type": "number",
              "value": "={{ $json['Number of Posts to Analyze'] }}"
            },
            {
              "id": "id-5",
              "name": "openaiApiKey",
              "type": "string",
              "value": "YOUR_OPENAI_API_KEY"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "23c3d08b-e38e-469f-ac24-7bcfdfdc9305",
      "name": "Platform Router1",
      "type": "n8n-nodes-base.switch",
      "position": [
        288,
        704
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "42237866-d2cb-4b01-9447-d1d0ab6584b6",
                    "operator": {
                      "type": "array",
                      "operation": "contains",
                      "rightType": "any"
                    },
                    "leftValue": "={{ $json['Select Platforms']}}",
                    "rightValue": "Instagram"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "98782347-1e0c-48c4-8232-00ccf1780f9f",
                    "operator": {
                      "type": "array",
                      "operation": "contains",
                      "rightType": "any"
                    },
                    "leftValue": "={{ $json['Select Platforms']}}",
                    "rightValue": "TikTok"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "ignoreCase": true,
          "fallbackOutput": "none",
          "allMatchingOutputs": true
        },
        "looseTypeValidation": true
      },
      "typeVersion": 3.3,
      "alwaysOutputData": true
    },
    {
      "id": "63607524-f0e1-459d-a18e-7000d8d3608d",
      "name": "Filter Images Only1",
      "type": "n8n-nodes-base.filter",
      "position": [
        736,
        720
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "id-1",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.type }}",
              "rightValue": "Image"
            },
            {
              "id": "id-2",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              },
              "leftValue": "={{ $json.displayUrl || $json.imageUrl }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    }
  ],
  "connections": {
    "Form Trigger1": {
      "main": [
        [
          {
            "node": "Workflow Configuration1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Platform Router1": {
      "main": [
        [
          {
            "node": "Scrape Instagram Posts",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Scrape TikTok Posts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Images Only1": {
      "main": [
        [
          {
            "node": "Analyze Images with GPT-4o Vision",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape TikTok Posts": {
      "main": [
        [
          {
            "node": "Filter Images Only1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate All Results": {
      "main": [
        [
          {
            "node": "Generate Competitive Analysis Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape Instagram Posts": {
      "main": [
        [
          {
            "node": "Filter Images Only1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Workflow Configuration1": {
      "main": [
        [
          {
            "node": "Platform Router1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyze Images with GPT-4o Vision": {
      "main": [
        [
          {
            "node": "Aggregate All Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Competitive Analysis Report": {
      "main": [
        [
          {
            "node": "Log Results to Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}