This workflow corresponds to n8n.io template #11615 — we link there as the canonical source.
This workflow follows the Apifyn8N Nodes Apify → Google Sheets recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "J5OfeDgU2EoYYjzw",
"name": "TikTok Ads Monitoring Loop 1.2",
"tags": [],
"nodes": [
{
"id": "204f29b8-31d7-4c68-bef8-0f46b882376a",
"name": "Set Parameters",
"type": "n8n-nodes-base.set",
"position": [
208,
-96
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "c39dfde7-66b4-4ca2-97b2-d134b4f24502",
"name": "Ad target country",
"type": "string",
"value": "all"
},
{
"id": "92724e8e-d4b6-4a27-8460-dc9d0073c124",
"name": "Ad published date From (DD/MM/YYYY)",
"type": "string",
"value": "={{ $now.minus({ days: 1 }).toFormat('dd/MM/yyyy') }}"
},
{
"id": "30f957d6-f3e2-46f8-8f35-2a836d3f5fcd",
"name": "Ad published To (DD/MM/YYYY)",
"type": "string",
"value": "={{ $now.toFormat('dd/MM/yyyy') }}"
},
{
"id": "32bf3fc0-d6c1-469c-908a-84feb0653b71",
"name": "Advertiser name or keyword",
"type": "string",
"value": ""
},
{
"id": "83371530-fba9-456e-9a50-28aa52981106",
"name": "adv_biz_ids",
"type": "string",
"value": "692017+1234567890"
},
{
"id": "06bdf732-62ca-409a-a38a-2df29e58dbbb",
"name": "Ad limit",
"type": "string",
"value": ""
}
]
}
},
"typeVersion": 3.4
},
{
"id": "1e175c48-7bbd-4b5b-bed4-b7f16a78b6f6",
"name": "Convert Dates to Unix",
"type": "n8n-nodes-base.code",
"position": [
416,
-96
],
"parameters": {
"jsCode": "// Convert dates from DD/MM/YYYY to Unix timestamp (milliseconds)\nconst input = $input.first().json;\n\nfunction dateToUnix(dateStr) {\n if (!dateStr || dateStr.trim() === '') return null;\n \n // Parse DD/MM/YYYY format\n const parts = dateStr.split('/');\n if (parts.length !== 3) {\n throw new Error(`Invalid date format: ${dateStr}. Expected DD/MM/YYYY`);\n }\n \n const day = parseInt(parts[0], 10);\n const month = parseInt(parts[1], 10) - 1; // Month is 0-indexed in JavaScript\n const year = parseInt(parts[2], 10);\n \n // Create date object and convert to Unix timestamp in milliseconds\n const date = new Date(year, month, day);\n return date.getTime();\n}\n\nconst startTimeUnix = dateToUnix(input['Ad published date From (DD/MM/YYYY)']);\nconst endTimeUnix = dateToUnix(input['Ad published To (DD/MM/YYYY)']);\n\n// Return all original fields plus converted timestamps\nreturn {\n json: {\n ...input,\n start_time_unix: startTimeUnix,\n end_time_unix: endTimeUnix\n }\n};"
},
"typeVersion": 2
},
{
"id": "ea56c4d8-d916-41f4-92ab-4fa2f92081f7",
"name": "Build Apify Body",
"type": "n8n-nodes-base.code",
"position": [
624,
-96
],
"parameters": {
"jsCode": "// Build customBody JSON with conditional resultsLimit\nconst input = $input.first().json;\n\n// Get adv_biz_ids from input\nconst advBizIds = input.adv_biz_ids || '';\n\n// Build URL with conditional adv_biz_ids parameter\nlet url = `https://library.tiktok.com/ads?region=${input['Ad target country']}&start_time=${input.start_time_unix}&end_time=${input.end_time_unix}&adv_name=${input['Advertiser name or keyword']}`;\n\n// Add adv_biz_ids only if it's not empty\nif (advBizIds && advBizIds.trim() !== '') {\n url += `&adv_biz_ids=${advBizIds}`;\n} else {\n url += `&adv_biz_ids=`;\n}\n\nurl += `&query_type=1&sort_type=last_shown_date,desc`;\n\n// Build the base object\nconst body = {\n skipDetails: false,\n startUrls: [\n {\n url: url\n }\n ]\n};\n\n// Add resultsLimit only if Ad limit is not empty\nif (input['Ad limit'] && input['Ad limit'].trim() !== '') {\n const limit = parseInt(input['Ad limit'], 10);\n if (!isNaN(limit) && limit > 0) {\n body.resultsLimit = limit;\n }\n}\n\n// Return the JSON string for customBody\nreturn {\n json: {\n ...input,\n customBody: JSON.stringify(body, null, 2)\n }\n};"
},
"typeVersion": 2
},
{
"id": "55a900ff-ca28-49a7-b662-d542b5a2e39e",
"name": "Prepare Data for Sheets",
"type": "n8n-nodes-base.code",
"position": [
1040,
-96
],
"parameters": {
"jsCode": "// Safely extract video data and prepare for Google Sheets\n// Process all input items\nconst items = $input.all();\n\nreturn items.map(item => {\n const input = item.json;\n \n // Safely get first video URL\n const videoUrl = (input.videos && Array.isArray(input.videos) && input.videos.length > 0 && input.videos[0].url) \n ? input.videos[0].url \n : '';\n \n // Safely get cover image URL\n const coverImageUrl = (input.videos && Array.isArray(input.videos) && input.videos.length > 0 && input.videos[0].coverImageUrl) \n ? input.videos[0].coverImageUrl \n : '';\n \n // Safely get tiktokUser username\n const tiktokUsername = (input.tiktokUser && input.tiktokUser.username) \n ? input.tiktokUser.username \n : '';\n \n // Return all original data plus safely extracted fields\n return {\n json: {\n ...input,\n videoUrl: videoUrl,\n coverImageUrl: coverImageUrl,\n tiktokUsername: tiktokUsername\n }\n };\n});"
},
"typeVersion": 2
},
{
"id": "3804ed9d-b6ea-44d5-af30-ad22998e4e5c",
"name": "Read existing IDs",
"type": "n8n-nodes-base.googleSheets",
"position": [
1248,
144
],
"parameters": {
"options": {
"dataLocationOnSheet": {
"values": {
"range": "K:K",
"rangeDefinition": "specifyRangeA1"
}
}
},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/15mF_dwFrTTAaEALIo0xpRHc3_eKUKBFucmBv5lZTzUA/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "15mF_dwFrTTAaEALIo0xpRHc3_eKUKBFucmBv5lZTzUA",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/15mF_dwFrTTAaEALIo0xpRHc3_eKUKBFucmBv5lZTzUA/edit?usp=drivesdk",
"cachedResultName": "TikTok Ads library research"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "2df43f3d-b374-4fe4-ac0c-609147c6b566",
"name": "Collect ID list",
"type": "n8n-nodes-base.code",
"position": [
1456,
144
],
"parameters": {
"jsCode": "// Collect existing IDs from Google Sheets\nconst items = $input.all();\n\nconst ids = items\n .map(item => item.json.adId)\n // Cast all values to strings to avoid mismatches like \"123\" vs 123\n .map(id => id != null ? String(id) : undefined)\n .filter((id, index, arr) => id && arr.indexOf(id) === index);\n\nreturn [{ json: { existingIds: ids } }];"
},
"typeVersion": 2
},
{
"id": "7d08dbfc-2d45-4098-92f1-900a6301eb80",
"name": "Attach existing ids",
"type": "n8n-nodes-base.merge",
"position": [
1664,
-96
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineAll"
},
"typeVersion": 3.2
},
{
"id": "f96de8ba-6796-4772-9100-213a4112568d",
"name": "Filter new creatives",
"type": "n8n-nodes-base.code",
"position": [
1872,
-96
],
"parameters": {
"jsCode": "// Filter new ads that don't exist in Google Sheets\nconst items = $input.all();\nconst input = items[0].json;\n\n// Get existing IDs from merged data\nconst existingIds = (input.existingIds || []).map(id => id != null ? String(id) : undefined).filter(Boolean);\nconst seen = new Set(existingIds);\n\n// Get all ads from items (skip first item which contains existingIds)\nconst ads = items.slice(1).map(item => item.json);\nconst newAds = [];\n\nfor (const ad of ads) {\n if (!ad?.adId) continue;\n const adId = String(ad.adId);\n if (!seen.has(adId)) {\n newAds.push(ad);\n seen.add(adId); // protect against duplicates within a single run\n }\n}\n\n// Return new ads as separate items\nreturn newAds.map(ad => ({ json: ad }));"
},
"typeVersion": 2
},
{
"id": "4821abfe-854f-41a4-afce-1a4cb09805fb",
"name": "Count new ads",
"type": "n8n-nodes-base.code",
"position": [
2080,
64
],
"parameters": {
"jsCode": "// Count new ads\nconst items = $input.all();\nreturn [{\n json: {\n newCount: items.length\n }\n}];"
},
"typeVersion": 2
},
{
"id": "e352dc0a-0fb9-42a1-8110-aa6ceeda45b0",
"name": "Any new ads?",
"type": "n8n-nodes-base.if",
"position": [
2288,
64
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "d0e1f2a3-b4c5-6789-0123-456789012345",
"operator": {
"type": "number",
"operation": "gt"
},
"leftValue": "={{ $json.newCount }}",
"rightValue": 0
}
]
}
},
"typeVersion": 2.2
},
{
"id": "25aae637-70ec-4a31-81fd-974b6e47ffcc",
"name": "Append or update row in sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
2496,
-96
],
"parameters": {
"columns": {
"value": {
"adId": "={{ $json.adId }}",
"adName": "={{ $json.adName }}",
"paidBy": "={{ $json.paidBy }}",
"videos": "={{ $json.videoUrl }}",
"startURL": "={{ $json.startUrl }}",
"targeting": "={{ $json.targeting }}",
"tiktokUser": "={{ $json.tiktokUsername }}",
"Impressions": "={{ $json.impressions }}",
"regionStats": "={{ $json.regionStats }}",
"advertiserID": "={{ $json.advertiserId }}",
"coverImageURL": "={{ $json.coverImageUrl ? `=IMAGE(\"${$json.coverImageUrl}\"; 1)` : '' }}",
"advertiserName": "={{ $json.advertiserName }}"
},
"schema": [
{
"id": "coverImageURL",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "coverImageURL",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "videos",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "videos",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "adName",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "adName",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Impressions",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Impressions",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "regionStats",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "regionStats",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "targeting",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "targeting",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "tiktokUser",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "tiktokUser",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "startURL",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "startURL",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "paidBy",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "paidBy",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "advertiserName",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "advertiserName",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "adId",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "adId",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "advertiserID",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "advertiserID",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"adId"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "appendOrUpdate",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/15mF_dwFrTTAaEALIo0xpRHc3_eKUKBFucmBv5lZTzUA/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "15mF_dwFrTTAaEALIo0xpRHc3_eKUKBFucmBv5lZTzUA",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/15mF_dwFrTTAaEALIo0xpRHc3_eKUKBFucmBv5lZTzUA/edit?usp=drivesdk",
"cachedResultName": "TikTok Ads library research"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "3ac92c9f-5416-462a-a0f1-c5efc68217f1",
"name": "Send a message",
"type": "n8n-nodes-base.slack",
"position": [
2704,
48
],
"parameters": {
"text": "=Hello!\n{{$json.newCount}} of TikTok ads were found today!\nYou can see full list here \u2014 https://docs.google.com/spreadsheets/d/15mF_dwFrTTAaEALIo0xpRHc3_eKUKBFucmBv5lZTzUA/edit?usp=sharing",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": ""
},
"otherOptions": {}
},
"typeVersion": 2.3
},
{
"id": "183b249c-fdcc-40ca-a489-1a09f07c608c",
"name": "Send a text message",
"type": "n8n-nodes-base.telegram",
"position": [
2704,
208
],
"parameters": {
"text": "=Hello!\n{{$json.newCount}} of TikTok ads were found today!\nYou can see full list here \u2014 https://docs.google.com/spreadsheets/d/15mF_dwFrTTAaEALIo0xpRHc3_eKUKBFucmBv5lZTzUA/edit?usp=sharing",
"chatId": "6666666",
"additionalFields": {}
},
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.2
},
{
"id": "00ea4eb6-6dbd-401b-a865-de40421a3ca2",
"name": "Get TT Ads through Apify",
"type": "@apify/n8n-nodes-apify.apify",
"position": [
832,
-96
],
"parameters": {
"actorId": {
"__rl": true,
"mode": "list",
"value": "AovGexsTSmlalAFzp",
"cachedResultUrl": "https://console.apify.com/actors/AovGexsTSmlalAFzp/input",
"cachedResultName": "Tiktok Ads Scraper (silva95gustavo/tiktok-ads-scraper)"
},
"operation": "Run actor and get dataset",
"customBody": "={{ $json.customBody }}"
},
"credentials": {
"apifyApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "7c1b1a25-2ce6-48a4-9226-7d0158b3e98c",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-16,
-96
],
"parameters": {
"rule": {
"interval": [
{}
]
}
},
"typeVersion": 1.2
},
{
"id": "6661c32d-8143-4017-ad37-bfc957cd1874",
"name": "Sticky Note - Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
-480,
-720
],
"parameters": {
"width": 652,
"height": 592,
"content": "# TikTok Ads Monitoring Loop\n\n## How it works\n\nAutomatically monitors TikTok Ads creatives from specific advertisers or keyword searches, saves them to Google Sheets, and sends notifications to Telegram or Slack every time it finds new creatives.\n\n**Setup**\n- Create an account on [Apify](https://apify.com), and choose [Tiktok Ads Scraper](https://console.apify.com/actors/AovGexsTSmlalAFzp/input)\n- Connect Google Sheets, Slack, or Telegram\n- Add advertiserID of your competitor or keyword, and choose the countries you want to monitor\n- Update spreadsheet IDs, the Slack channel, or the Telegram chat ID\n\n**Benefits**\n- Never miss new creatives from your competitors\n- Collect a creative dataset for competitors or relevant keywords\n- See the timeline of how creatives change over time\n\n\nBuilt by Kirill Khatkevich\n[Connect on LinkedIn](https://www.linkedin.com/in/kirill-khatkevich/)"
},
"typeVersion": 1
},
{
"id": "cd97b62a-9015-46ef-8036-9f706f3f2ea3",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
96,
80
],
"parameters": {
"color": 5,
"width": 304,
"height": 288,
"content": "## Set your parameters\n- Target Country (use ISO 3166 country codes)\n- Date From (by default it is yesterday)\n- Date To (by default it is today)\n- Advertiser name or keyword\n- ID of advertiser (you can use both fields: this and previous)\n- Set limit (if you don't want to scrape all)"
},
"typeVersion": 1
},
{
"id": "8eef6a2d-e6a8-4c6e-ba93-d7fa5688e1ea",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
368,
-208
],
"parameters": {
"color": 5,
"width": 176,
"content": "Transforms date format from DD/MM/YYYY to Unix timestamp"
},
"typeVersion": 1
},
{
"id": "944f84bd-7431-4436-b050-0010166f1ca8",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
592,
-208
],
"parameters": {
"color": 5,
"width": 176,
"content": "Creates JSON body for Apify if you set a result limit"
},
"typeVersion": 1
},
{
"id": "23fa9a0f-2c9d-44a7-9652-1d60e66c268d",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
800,
-240
],
"parameters": {
"color": 5,
"width": 176,
"content": "This node gets data from the Apify scraper. Add your credentials and choose an actor"
},
"typeVersion": 1
},
{
"id": "aa018f2a-45c9-4170-a407-1226bf6d4da1",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
1040,
-224
],
"parameters": {
"color": 5,
"width": 176,
"content": "Safely extract video data and prepare for Google Sheets"
},
"typeVersion": 1
},
{
"id": "783ea6f3-67e2-4c17-910e-eaca4d6f093d",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
1200,
224
],
"parameters": {
"color": 5,
"width": 192,
"height": 208,
"content": "\n\n\n\n\n\nCheck which creative IDs already exist in Google Sheets so we only send a notification when new ones are found"
},
"typeVersion": 1
},
{
"id": "60532ca5-8981-456d-ae0f-6476f94618a7",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
1440,
224
],
"parameters": {
"color": 5,
"width": 192,
"content": "\n\n\n\n\n\nCollect IDs for future matching"
},
"typeVersion": 1
},
{
"id": "7ed1f991-e023-44e5-9b9f-10f63a31d7af",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
1616,
-32
],
"parameters": {
"color": 5,
"width": 192,
"content": "\n\n\n\n\n\nMatch creatives from the response with those already stored in Google Sheets"
},
"typeVersion": 1
},
{
"id": "ddd65036-e582-44d1-8ddd-08273f02b8e4",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
1824,
-32
],
"parameters": {
"color": 5,
"width": 192,
"content": "\n\n\n\n\n\nMatch creatives from the API response with those already stored in Google Sheets"
},
"typeVersion": 1
},
{
"id": "5ac9f0ae-7287-440a-bb73-875ff87ee30d",
"name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote",
"position": [
2448,
-240
],
"parameters": {
"color": 5,
"width": 192,
"content": "\n\n\n\nAdd creatives to Google Sheets. Be careful to keep the column order and names in sync."
},
"typeVersion": 1
},
{
"id": "8ac753cc-8490-49f5-8adf-0a5d30fba72c",
"name": "Sticky Note10",
"type": "n8n-nodes-base.stickyNote",
"position": [
2032,
144
],
"parameters": {
"color": 5,
"width": 192,
"height": 192,
"content": "\n\n\n\nCount how many new creatives were found for a more compact message. Use {{$json.newCount}} in the Slack or Telegram message."
},
"typeVersion": 1
},
{
"id": "9830260b-1192-4dee-9990-257a5616870f",
"name": "Sticky Note11",
"type": "n8n-nodes-base.stickyNote",
"position": [
2256,
160
],
"parameters": {
"color": 5,
"width": 192,
"content": "\n\n\n\nCheck if there are any new ads before sending notifications"
},
"typeVersion": 1
},
{
"id": "3f751641-e785-4797-9929-8c8dec0a0f44",
"name": "Sticky Note13",
"type": "n8n-nodes-base.stickyNote",
"position": [
2656,
304
],
"parameters": {
"color": 5,
"width": 192,
"content": "\n\n\n\nSend Telegram notification about new creatives"
},
"typeVersion": 1
},
{
"id": "70956887-5b1e-400e-b940-5995bd78d10c",
"name": "Sticky Note12",
"type": "n8n-nodes-base.stickyNote",
"position": [
2704,
-80
],
"parameters": {
"color": 5,
"width": 192,
"content": "\n\n\n\nSend Slack notification about new creatives"
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "148c5640-d53d-48d7-8fdd-83ee2da3899c",
"connections": {
"Any new ads?": {
"main": [
[
{
"node": "Send a text message",
"type": "main",
"index": 0
},
{
"node": "Send a message",
"type": "main",
"index": 0
}
]
]
},
"Count new ads": {
"main": [
[
{
"node": "Any new ads?",
"type": "main",
"index": 0
}
]
]
},
"Set Parameters": {
"main": [
[
{
"node": "Convert Dates to Unix",
"type": "main",
"index": 0
}
]
]
},
"Collect ID list": {
"main": [
[
{
"node": "Attach existing ids",
"type": "main",
"index": 1
}
]
]
},
"Build Apify Body": {
"main": [
[
{
"node": "Get TT Ads through Apify",
"type": "main",
"index": 0
}
]
]
},
"Schedule Trigger": {
"main": [
[
{
"node": "Set Parameters",
"type": "main",
"index": 0
}
]
]
},
"Read existing IDs": {
"main": [
[
{
"node": "Collect ID list",
"type": "main",
"index": 0
}
]
]
},
"Attach existing ids": {
"main": [
[
{
"node": "Filter new creatives",
"type": "main",
"index": 0
}
]
]
},
"Filter new creatives": {
"main": [
[
{
"node": "Append or update row in sheet",
"type": "main",
"index": 0
},
{
"node": "Count new ads",
"type": "main",
"index": 0
}
]
]
},
"Convert Dates to Unix": {
"main": [
[
{
"node": "Build Apify Body",
"type": "main",
"index": 0
}
]
]
},
"Prepare Data for Sheets": {
"main": [
[
{
"node": "Read existing IDs",
"type": "main",
"index": 0
},
{
"node": "Attach existing ids",
"type": "main",
"index": 0
}
]
]
},
"Get TT Ads through Apify": {
"main": [
[
{
"node": "Prepare Data for 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.
apifyApigoogleSheetsOAuth2ApitelegramApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow continuously monitors the TikTok Ads Library for new creatives from specific advertisers or keyword searches, scrapes them via Apify, logs them into Google Sheets, and sends concise notifications to Telegram or Slack with the number of newly discovered ads. It is…
Source: https://n8n.io/workflows/11615/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Automatically monitor multiple websites every 5 minutes, log downtime, notify your team instantly via multiple channels, and track uptime/downtime in a Google Sheet—without relying on expensive monito
This workflow monitors your Gmail support inbox every minute, automatically sending each unread email to OpenAI for intelligent analysis. The AI evaluates sentiment (Positive/Neutral/Negative/Critical
This workflow automates plant care reminders and records using Google Sheets, Telegram, and OpenWeather API.
Apollo Data Enrichment Using Company Id to automatically finds contacts for companies listed in your Google Sheet, enriches each person with emails and phone numbers via Apollo’s API, and writes verif
🌸 Affirmation Sender + Weekly Gratitude Digest v2