{
  "name": "Track competitor ads from the Google Ads Transparency Center in Google Sheets",
  "nodes": [
    {
      "id": "b7e2d914-0001-4c01-9001-000000000001",
      "name": "Every Monday morning",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        0,
        460
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 8
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "b7e2d914-0002-4c02-9002-000000000002",
      "name": "Set the competitors to watch",
      "type": "n8n-nodes-base.set",
      "position": [
        260,
        460
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "b7e2d914-0010-4c10-9010-+1234567890",
              "name": "advertiserIds",
              "type": "array",
              "value": "[\"AR0161401435+1234567890\"]"
            },
            {
              "id": "b7e2d914-0011-4c11-9011-+1234567890",
              "name": "maxAdsPerAdvertiser",
              "type": "number",
              "value": "20"
            },
            {
              "id": "b7e2d914-0012-4c12-9012-+1234567890",
              "name": "region",
              "type": "string",
              "value": ""
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "b7e2d914-0003-4c03-9003-000000000003",
      "name": "Fetch each advertiser's ads",
      "type": "@apify/n8n-nodes-apify.apify",
      "position": [
        520,
        460
      ],
      "parameters": {
        "memory": 128,
        "actorId": "johnvc~google-ads-transparency-api",
        "resource": "Actors",
        "operation": "Run actor and get dataset",
        "customBody": "={{ JSON.stringify({ advertiserIds: $json.advertiserIds, maxResultsPerAdvertiser: $json.maxAdsPerAdvertiser, region: $json.region }) }}"
      },
      "typeVersion": 1
    },
    {
      "id": "b7e2d914-0004-4c04-9004-000000000004",
      "name": "Keep rows that are real ads",
      "type": "n8n-nodes-base.filter",
      "position": [
        780,
        460
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "b7e2d914-0041-4c41-9041-+1234567890",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.result_type }}",
              "rightValue": "ad"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "b7e2d914-0005-4c05-9005-000000000005",
      "name": "Shape ad rows for the sheet",
      "type": "n8n-nodes-base.code",
      "position": [
        1040,
        460
      ],
      "parameters": {
        "jsCode": "// One row per competitor ad, with the dates made readable and the\n// long-running creatives flagged. A creative that has been live for months\n// is one the competitor keeps paying for, which is the useful signal here.\nconst weekOf = $now.toFormat('yyyy-MM-dd');\nconst asDate = (unix) =>\n  typeof unix === 'number' && unix > 0\n    ? new Date(unix * 1000).toISOString().slice(0, 10)\n    : '';\n\nreturn $input.all().map((item) => {\n  const ad = item.json;\n  const days = typeof ad.total_days_shown === 'number' ? ad.total_days_shown : '';\n  return {\n    json: {\n      'Week of': weekOf,\n      Advertiser: ad.advertiser || '',\n      'Ad format': ad.format || '',\n      'First shown': asDate(ad.first_shown),\n      'Days running': days,\n      'Long runner': days !== '' && days >= 90 ? 'YES' : '',\n      'Creative link': ad.details_link || '',\n      'Creative ID': ad.ad_creative_id || '',\n      'Advertiser ID': ad.advertiser_id || '',\n    },\n  };\n});\n"
      },
      "typeVersion": 2
    },
    {
      "id": "b7e2d914-0006-4c06-9006-000000000006",
      "name": "Append this week's ads",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1300,
        460
      ],
      "parameters": {
        "columns": {
          "value": {},
          "mappingMode": "autoMapInputData",
          "matchingColumns": []
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "b7e2d914-0090-4c90-9090-000000000090",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -620,
        40
      ],
      "parameters": {
        "width": 520,
        "height": 1140,
        "content": "## Track competitor ads from the Google Ads Transparency Center in Google Sheets\n\nNo ad spy subscription and no agency retainer. Every week this workflow reads the Google Ads Transparency Center for the advertisers you list and appends one row per ad to your sheet: advertiser, format, when it first ran, how many days it has been running, and a link to the creative itself.\n\nThe column that earns its keep is Days running. Anyone can see what a competitor is advertising today. Seeing that one creative has been live for 543 days tells you which message they keep paying for, which is the one worth studying.\n\n**Who's it for**\nPerformance marketers sizing up a competitor before a launch, agencies building a pitch deck, and founders who want to know what the funded competitor is actually saying.\n\n**How it works**\n1. A schedule trigger fires once a week\n2. One Set node holds the advertiser IDs you want to watch\n3. The Apify node pulls each advertiser's live ad library\n4. A Code node turns the timestamps into dates and flags the long runners\n\n**Setup**\n1. Create a free Apify account and add your API token to the Apify credential\n2. Connect your Google account in the Sheets node and pick a spreadsheet\n3. Open adstransparency.google.com, search each competitor, and copy the AR... code out of the URL into the config node. The example ships with one advertiser; add the rest of your set and the sheet becomes a side by side.\n\n**Good to know**\nAbout a third of a cent per ad, so three competitors at 20 ads each is roughly 20 cents a week.\nThe Transparency Center only lists ads an advertiser is running now, so every row is live. It does not publish spend or impressions.\nActor: https://apify.com/johnvc/google-ads-transparency-api?fpr=9n7kx3&fp_sid=n8n"
      },
      "typeVersion": 1
    },
    {
      "id": "b7e2d914-0091-4c91-9091-000000000091",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -40,
        60
      ],
      "parameters": {
        "color": 7,
        "width": 240,
        "height": 240,
        "content": "### 1. Schedule\nRuns every Monday morning. Change the cadence, or swap in a manual trigger while you test."
      },
      "typeVersion": 1
    },
    {
      "id": "b7e2d914-0092-4c92-9092-000000000092",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        220,
        60
      ],
      "parameters": {
        "color": 7,
        "width": 240,
        "height": 240,
        "content": "### 2. Your input\nThe advertiser IDs you want to watch, and how many ads to pull for each. This is the only node you edit."
      },
      "typeVersion": 1
    },
    {
      "id": "b7e2d914-0093-4c93-9093-000000000093",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        480,
        60
      ],
      "parameters": {
        "color": 7,
        "width": 240,
        "height": 240,
        "content": "### 3. Fetch\nReads each advertiser's library from the Google Ads Transparency Center. Add your Apify credential here."
      },
      "typeVersion": 1
    },
    {
      "id": "b7e2d914-0094-4c94-9094-000000000094",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        740,
        60
      ],
      "parameters": {
        "color": 7,
        "width": 240,
        "height": 240,
        "content": "### 4. Clean\nDrops any advertiser that returned an error, so only real ads reach the sheet."
      },
      "typeVersion": 1
    },
    {
      "id": "b7e2d914-0095-4c95-9095-000000000095",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1000,
        60
      ],
      "parameters": {
        "color": 7,
        "width": 240,
        "height": 240,
        "content": "### 5. Shape\nTurns the timestamps into readable dates and flags creatives running 90 days or more."
      },
      "typeVersion": 1
    },
    {
      "id": "b7e2d914-0096-4c96-9096-000000000096",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1260,
        60
      ],
      "parameters": {
        "color": 7,
        "width": 240,
        "height": 240,
        "content": "### 6. Deliver\nPick your spreadsheet and tab. Columns map automatically."
      },
      "typeVersion": 1
    },
    {
      "id": "b7e2d914-0080-4c80-9080-000000000080",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        220,
        760
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 240,
        "content": "### Where do I find an advertiser ID?\nGo to adstransparency.google.com, search the company, and open its page. The URL contains an `AR...` code. That code is the advertiser ID. One company can have several, one per legal entity."
      },
      "typeVersion": 1
    },
    {
      "id": "b7e2d914-0081-4c81-9081-000000000081",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        660,
        760
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 240,
        "content": "### Self-hosting n8n?\nThere is also a dedicated community node for this actor: `n8n-nodes-google-ads-transparency-api` on npm."
      },
      "typeVersion": 1
    }
  ],
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "Every Monday morning": {
      "main": [
        [
          {
            "node": "Set the competitors to watch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch each advertiser's ads": {
      "main": [
        [
          {
            "node": "Keep rows that are real ads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Keep rows that are real ads": {
      "main": [
        [
          {
            "node": "Shape ad rows for the sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Shape ad rows for the sheet": {
      "main": [
        [
          {
            "node": "Append this week's ads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set the competitors to watch": {
      "main": [
        [
          {
            "node": "Fetch each advertiser's ads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}