{
  "name": "YouTube Video Comment & Reply Thread Scraper - clean export",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "days",
              "daysInterval": 1,
              "triggerAtHour": 8,
              "triggerAtMinute": 0
            }
          ]
        }
      },
      "id": "3eeada8d-f4c2-4ee6-adfc-000015767e60",
      "name": "Daily trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -80,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "// Edit this object for your run. The Actor's full input schema is linked in the sticky note.\nconst input = {\n  \"videoUrls\": [\n    \"https://www.youtube.com/watch?v=dQw4w9WgXcQ\"\n  ],\n  \"maxComments\": 25,\n  \"maxRepliesPerComment\": 3,\n  \"maxTotalRows\": 20\n};\n\nreturn [{ json: input }];"
      },
      "id": "b30f3a01-860b-4e62-a0aa-99294f1ad803",
      "name": "Configure input",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        160,
        0
      ],
      "notes": "Edit the input object for your city, keyword, URLs or limit."
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.apify.com/v2/acts/nimbryxo-studio~youtube-video-comment-and-reply-thread-scraper/run-sync-get-dataset-items",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($json) }}",
        "options": {
          "timeout": 300000
        }
      },
      "id": "b1a1529c-cd46-400d-afea-69c8aa5b2dd9",
      "name": "Run Apify Actor",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        400,
        0
      ],
      "notes": "Select the Apify API Token (Query Auth) credential."
    },
    {
      "parameters": {
        "jsCode": "const rows = $input.all().flatMap((item) => (Array.isArray(item.json) ? item.json : [item.json]));\nconst failed = rows.find((row) => row && row.error && typeof row.error === 'object' && Object.keys(row).length === 1);\nif (failed) throw new Error(`Apify: ${failed.error.type || 'error'} ${failed.error.message || ''}`.trim());\nconst keyField = \"videoId\"; // a stable identity from the Actor output\nconst kindField = rows.some((row) => row && row.recordType !== undefined) ? 'recordType' : null;\nconst scrapedAt = new Date().toISOString();\nconst seen = new Set();\nconst clean = [];\nfor (const row of rows) {\n  if (!row || typeof row !== 'object') continue;\n  const out = {};\n  for (const [k, v] of Object.entries(row)) {\n    if (v === null || v === undefined) continue;\n    out[k] = typeof v === 'string' ? v.trim() : v;\n  }\n  const value = keyField ? out[keyField] : undefined;\n  const id = (kindField ? `${out[kindField]}|` : '') + (value === undefined || value === '' ? JSON.stringify(out) : String(value).toLowerCase());\n  if (seen.has(id)) continue;\n  seen.add(id);\n  out._source = \"https://apify.com/nimbryxo-studio/youtube-video-comment-and-reply-thread-scraper\";\n  out._scrapedAt = scrapedAt;\n  clean.push(out);\n}\nreturn clean.map((json) => ({ json }));"
      },
      "id": "f2882ef9-b71d-4f85-a1ae-5779e72a1d8d",
      "name": "Normalize + dedupe",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        640,
        0
      ]
    },
    {
      "parameters": {},
      "id": "fe02b9bb-aaa3-48cf-a5ba-360317d8e178",
      "name": "Rows ready",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        880,
        0
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "YOUR_GOOGLE_SHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Rows",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {}
        },
        "options": {}
      },
      "id": "a9333e6f-9d03-40d4-a1c9-83f1bc0be262",
      "name": "Google Sheets (optional)",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1120,
        0
      ],
      "disabled": true,
      "notes": "Enable this after adding your Google Sheets credential and sheet ID."
    },
    {
      "parameters": {
        "content": "## YouTube Video Comment & Reply Thread Scraper\n\n**Actor:** [YouTube Video Comment & Reply Thread Scraper](https://apify.com/nimbryxo-studio/youtube-video-comment-and-reply-thread-scraper)\n\n**Docs:** https://scraperfinder.com/n8n/youtube-video-comment-and-reply-thread-scraper/\n\n### Setup\n1. Create an Apify account and copy an API token.\n2. In n8n, create a **Query Auth** credential:\n   - Name: `Apify API Token`\n   - Query parameter name: `token`\n   - Value: your Apify token\n3. Open **Run Apify Actor** and select that credential.\n4. Click **Execute workflow**. The rows appear in **Rows ready**.\n5. Optional: enable **Google Sheets** and set your document and sheet.\n\n### Notes\n- This calls the public Actor endpoint. No API token is stored in this file.\n- `run-sync-get-dataset-items` waits up to 300 seconds. For large runs, switch to the async run + dataset endpoint.\n- Public data only. Respect the target site's terms and local law.\n\n### Input fields\n- `videoUrls` (array)\n- `channelUrls` (array)\n- `playlistUrls` (array)\n- `searchQueries` (array)\n- `maxVideosPerSource` (integer)\n- `maxComments` (integer)\n- `includeReplies` (boolean)\n- `maxRepliesPerComment` (integer)\n- `maxReplyDepth` (integer)\n- `maxTotalRows` (integer)\n- `sortBy` (string)\n- `commentsSince` (string)\n\n### Output fields\n- `commentId` (Comment ID)\n- `authorName` (Author)\n- `text` (Text)\n- `likeCount` (Likes)\n- `replyCount` (Replies)\n- `isHearted` (Hearted)\n- `videoTitle` (Video)",
        "height": 640,
        "width": 560,
        "color": 4
      },
      "id": "48d3a5c9-a172-475e-a3db-a5cce7460080",
      "name": "Setup notes",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -80,
        -700
      ]
    }
  ],
  "connections": {
    "Daily trigger": {
      "main": [
        [
          {
            "node": "Configure input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Configure input": {
      "main": [
        [
          {
            "node": "Run Apify Actor",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run Apify Actor": {
      "main": [
        [
          {
            "node": "Normalize + dedupe",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize + dedupe": {
      "main": [
        [
          {
            "node": "Rows ready",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rows ready": {
      "main": [
        [
          {
            "node": "Google Sheets (optional)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {},
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": [],
  "versionId": "c1723e40-994e-4ad9-a924-9aa832e8207c",
  "description": "YouTube video comment scraper: runs the YouTube Video Comment & Reply Thread Scraper Apify Actor, returns authorName, text, likeCount, replyCount, isHearted and more, then normalizes and dedupes the rows before an optional Google Sheets append."
}
