{
  "name": "Keyword Research \u2014 Google Ads Keyword Metrics",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "30 0 * * *"
            }
          ]
        }
      },
      "id": "kw-1-schedule",
      "name": "Schedule Trigger (00:30 AM)",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "operation": "read",
        "sheetId": {
          "value": "={{ $env.SHEET_ID }}"
        },
        "range": "A:H",
        "options": {
          "filters": {
            "column": "\uac80\uc0c9\ubcfc\ub968",
            "value": ""
          }
        }
      },
      "id": "kw-2-sheets-read",
      "name": "Sheets Read (\uac80\uc0c9\ubcfc\ub968 \ube44\uc5b4\uc788\ub294 \ud589)",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        460,
        300
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// batch_keywords.js \u2014 \ud0a4\uc6cc\ub4dc 10\uac1c\uc529 \ubc30\uce58 \ubd84\ud560\n// n8n/code_nodes/batch_keywords.js \ub0b4\uc6a9\uc744 \uc5ec\uae30\uc5d0 \ubcf5\uc0ac"
      },
      "id": "kw-3-batch",
      "name": "Batch Keywords (10\uac1c\uc529)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        680,
        300
      ]
    },
    {
      "parameters": {
        "batchSize": 1,
        "options": {}
      },
      "id": "kw-4-loop",
      "name": "Loop Over Batches",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        900,
        300
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// get_access_token.js \u2014 Service Account JWT \u2192 Access Token \uad50\ud658\n// n8n/code_nodes/get_access_token.js \ub0b4\uc6a9\uc744 \uc5ec\uae30\uc5d0 \ubcf5\uc0ac"
      },
      "id": "kw-5-auth",
      "name": "Get Access Token (JWT)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1120,
        300
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://googleads.googleapis.com/v18/customers/{{ $env.GOOGLE_ADS_CUSTOMER_ID }}:generateKeywordIdeas",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $json.accessToken }}"
            },
            {
              "name": "developer-token",
              "value": "={{ $env.GOOGLE_ADS_DEVELOPER_TOKEN }}"
            },
            {
              "name": "login-customer-id",
              "value": "={{ $env.GOOGLE_ADS_LOGIN_CUSTOMER_ID }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"language\": \"languageConstants/1023\",\n  \"geoTargetConstants\": [\"geoTargetConstants/2410\"],\n  \"keywordPlanNetwork\": \"GOOGLE_SEARCH\",\n  \"keywordSeed\": {\n    \"keywords\": {{ JSON.stringify($json.keywords) }}\n  }\n}",
        "options": {
          "response": {
            "response": {
              "fullResponse": true
            }
          },
          "timeout": 30000
        }
      },
      "id": "kw-6-google-ads",
      "name": "Google Ads generateKeywordIdeas",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1340,
        300
      ],
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// parse_keyword_metrics.js \u2014 Google Ads \uc751\ub2f5 \u2192 \ubcfc\ub968/CPC/\ub09c\uc774\ub3c4/\uc6b0\uc120\uc21c\uc704 \ubcc0\ud658\n// n8n/code_nodes/parse_keyword_metrics.js \ub0b4\uc6a9\uc744 \uc5ec\uae30\uc5d0 \ubcf5\uc0ac"
      },
      "id": "kw-7-parse",
      "name": "Parse Keyword Metrics",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1560,
        300
      ]
    },
    {
      "parameters": {
        "operation": "update",
        "sheetId": {
          "value": "={{ $env.SHEET_ID }}"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "\uac80\uc0c9\ubcfc\ub968": "={{ $json.\uac80\uc0c9\ubcfc\ub968 }}",
            "\uc608\uc0c1CPC": "={{ $json.\uc608\uc0c1CPC }}",
            "\ub09c\uc774\ub3c4": "={{ $json.\ub09c\uc774\ub3c4 }}",
            "\uc6b0\uc120\uc21c\uc704": "={{ $json.\uc6b0\uc120\uc21c\uc704 }}"
          },
          "matchingColumns": [
            "\ud0a4\uc6cc\ub4dc"
          ]
        }
      },
      "id": "kw-8-sheets-update",
      "name": "Sheets Update (\uba54\ud2b8\ub9ad \uae30\ub85d)",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1780,
        300
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "amount": 1.5,
        "unit": "seconds"
      },
      "id": "kw-9-wait",
      "name": "Wait 1.5s (Rate Limit)",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        2000,
        300
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Error Handler \u2014 API \uc5d0\ub7ec \uc2dc \uae30\ubcf8\uac12\uc73c\ub85c graceful degradation\nconst batchData = $('Loop Over Batches').item.json;\nconst keywords = batchData.keywords || [];\nconst rowNumbers = batchData.rowNumbers || [];\nconst output = [];\n\nfor (let i = 0; i < keywords.length; i++) {\n  output.push({\n    json: {\n      \ud0a4\uc6cc\ub4dc: keywords[i],\n      rowNumber: rowNumbers[i] || '',\n      \uac80\uc0c9\ubcfc\ub968: 0,\n      \uc608\uc0c1CPC: 0,\n      \ub09c\uc774\ub3c4: 20,\n      \uc6b0\uc120\uc21c\uc704: 'C',\n    }\n  });\n}\n\nreturn output;"
      },
      "id": "kw-err-handler",
      "name": "Error Handler (Graceful Degradation)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1560,
        500
      ]
    },
    {
      "parameters": {
        "operation": "update",
        "sheetId": {
          "value": "={{ $env.SHEET_ID }}"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "\uac80\uc0c9\ubcfc\ub968": "={{ $json.\uac80\uc0c9\ubcfc\ub968 }}",
            "\uc608\uc0c1CPC": "={{ $json.\uc608\uc0c1CPC }}",
            "\ub09c\uc774\ub3c4": "={{ $json.\ub09c\uc774\ub3c4 }}",
            "\uc6b0\uc120\uc21c\uc704": "={{ $json.\uc6b0\uc120\uc21c\uc704 }}"
          },
          "matchingColumns": [
            "\ud0a4\uc6cc\ub4dc"
          ]
        }
      },
      "id": "kw-err-sheets-update",
      "name": "Sheets Update (\uc5d0\ub7ec \uae30\ubcf8\uac12)",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1780,
        500
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Schedule Trigger (00:30 AM)": {
      "main": [
        [
          {
            "node": "Sheets Read (\uac80\uc0c9\ubcfc\ub968 \ube44\uc5b4\uc788\ub294 \ud589)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sheets Read (\uac80\uc0c9\ubcfc\ub968 \ube44\uc5b4\uc788\ub294 \ud589)": {
      "main": [
        [
          {
            "node": "Batch Keywords (10\uac1c\uc529)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Batch Keywords (10\uac1c\uc529)": {
      "main": [
        [
          {
            "node": "Loop Over Batches",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Batches": {
      "main": [
        [
          {
            "node": "Get Access Token (JWT)",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Get Access Token (JWT)": {
      "main": [
        [
          {
            "node": "Google Ads generateKeywordIdeas",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Ads generateKeywordIdeas": {
      "main": [
        [
          {
            "node": "Parse Keyword Metrics",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Error Handler (Graceful Degradation)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Keyword Metrics": {
      "main": [
        [
          {
            "node": "Sheets Update (\uba54\ud2b8\ub9ad \uae30\ub85d)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sheets Update (\uba54\ud2b8\ub9ad \uae30\ub85d)": {
      "main": [
        [
          {
            "node": "Wait 1.5s (Rate Limit)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 1.5s (Rate Limit)": {
      "main": [
        [
          {
            "node": "Loop Over Batches",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Error Handler (Graceful Degradation)": {
      "main": [
        [
          {
            "node": "Sheets Update (\uc5d0\ub7ec \uae30\ubcf8\uac12)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sheets Update (\uc5d0\ub7ec \uae30\ubcf8\uac12)": {
      "main": [
        [
          {
            "node": "Wait 1.5s (Rate Limit)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "timezone": "Asia/Seoul",
    "saveManualExecutions": true
  },
  "meta": {
    "templateId": "",
    "notes": "Pipeline A \ubcf4\uc870 \uc6cc\ud06c\ud50c\ub85c\uc6b0: Google Ads Keyword Planner API\ub85c \ud0a4\uc6cc\ub4dc \uba54\ud2b8\ub9ad \uc0ac\uc804 \uc218\uc9d1.\n\ub9e4\uc77c 00:30\uc5d0 \uc2e4\ud589, Pipeline A(01:00)\ubcf4\ub2e4 30\ubd84 \uba3c\uc800.\n\uac80\uc0c9\ubcfc\ub968\uc774 \ube44\uc5b4\uc788\ub294 \ud589\ub9cc \ub300\uc0c1.\n\uc5d0\ub7ec \uc2dc \uae30\ubcf8\uac12 \uc720\uc9c0 (graceful degradation)."
  }
}