テンプレートHP - コンテンツスケジュール

上級

これはContent Creation, Multimodal AI分野の自動化ワークフローで、24個のノードを含みます。主にSet, Code, Wait, Merge, HttpRequestなどのノードを使用。 GPT-4、Apify、Google Sheetsを使ってInstagramコンテンツの自動スケジュール生成

前提条件
  • ターゲットAPIの認証情報が必要な場合あり
  • Google Sheets API認証情報
ワークフロープレビュー
ノード接続関係を可視化、ズームとパンをサポート
ワークフローをエクスポート
以下のJSON設定をn8nにインポートして、このワークフローを使用できます
{
  "id": "78RdoKSIQITTYT8u",
  "meta": {
    "instanceId": "a9966e7d53853abbaaeed78ba2b9971c959f5792b2cccdff75eb461951503a7f",
    "templateCredsSetupCompleted": true
  },
  "name": "template HP - Content Schedule",
  "tags": [
    {
      "id": "qGxXgaGLRNM9h4LO",
      "name": "template",
      "createdAt": "2025-08-04T16:37:34.891Z",
      "updatedAt": "2025-08-04T16:37:34.891Z"
    }
  ],
  "nodes": [
    {
      "id": "99e295bb-f026-4641-a96a-098461e70cf7",
      "name": "「Test workflow」クリック時",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -1960,
        40
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "b220a61b-3bce-49db-a5cc-0b56e0480f39",
      "name": "AI Agent - コンテンツスケジュール",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        720,
        -20
      ],
      "parameters": {
        "text": "=Write an Instagram post based on the inputs below.\n\nPillar: {{ $json.Pillar }}\nFormat: {{ $json.Format }}\nHoliday: {{ $json.Holiday }}\nSeason: {{ $json.SeasonStage }}\nBlog Title and URL (if applicable): {{ $json.Examples }}\nBlog Description (if any): {{ $json.Description }}\n\n\n🧠 Instructions:\n1. If the Holiday is not \"None\"\nWeave the occasion into the caption and visual description with elegant emotional framing and thematic imagery.\nE.g., a father and child for Father’s Day, subtle red-white-blue palette for July 4th.\nMatch tone to the holiday — nostalgic, celebratory, or heartfelt.\n\n2. If the Pillar is “Posts based on blogs”\nUse the blog title and description to inspire a visually or emotionally powerful idea.\n\nDo not summarize the blog.\n\nYou may end with a soft call to action — but do NOT use “See more in our latest journal.”\n\nInclude a CTA if the blog content or description clearly invites it.\n\n3. If the Pillar is \"testimonials”\n\nOnly begin a caption in the testimonial pillar with [add personalized sentence], not any other pillar. \n\nDo not use quotation marks unless a real quote is supplied.\n\nFocus on the tone of the testimonial (e.g., appreciation, transformation, confidence), not the exact wording.\n \n\n3. Use SeasonStage only to shape tone and atmosphere — not always as direct mention.\nLet the stage of the season influence the emotional feel, not dominate the caption.\nAvoid starting every post with “In summer…” or “As spring begins…”.\nInstead, use:\n\nEnvironmental cues (e.g., light, textures, locations, routines)\n\nEmotional themes (e.g., renewal, warmth, intimacy)\n\nTime-sensitive urgency sparingly (e.g., “final weeks for fall bookings” in mid-October)\n\n💡 You do not need to include the season name unless it adds meaningful context or urgency.\n\n4. Use a tone that is elegant, sincere, and emotionally grounded — aligned with Hartshorn Portraiture’s brand: a boutique photography studio creating framed, luxury portrait artwork for affluent families. \n\nPrioritize clarity and connection over poetic language. Avoid metaphors, clichés, or overly descriptive scenes. Write how a thoughtful human would speak. Let the subject speak for itself — your words should complement, not compete.\n\nKeep it natural, succinct (2–4 sentences), and tailored to the post’s format and intent. Vary your message — make sure it feels fresh, specific, and distinctive from other captions.\n\n5. Visual Description\nDescribe the layout, emotion, or structure of the visual clearly.\nUse variety and specificity (e.g., quote overlay on a candid moment, elegant close-up of frames, side-by-side client transformation).\n\n📦 Output Format:\nCaption\n[Your caption here — 2–4 sentences, emotionally grounded, with clarity and variety]\n\nVisual Description\n[1 sentence describing the image or structure. Be specific.]\n\nHashtags\nInclude ALL brand hashtags:\n#hartshornportraiture #jcmoms #hobokenmommies #njfamilyphotographer #hobokenfamilyphotographer #hobokennj #hobokenmoms #jcmommies #jcfamilyphotographer #jerseycityphotography #hobokenphotographer\nAdd 2 additional relevant local or theme-based hashtags (e.g., #hobokenweekend, #NJbeachportraits)",
        "options": {
          "systemMessage": "=You are a luxury brand copywriter for Hartshorn Portraiture, a boutique photography studio in Hoboken, NJ that creates wall-ready portrait artwork for affluent families who value connection, tradition, and timeless beauty. Your voice is elegant, warm, sincere, and human — never overly poetic, sentimental, or abstract. Captions should sound like something a real person would write, with subtle luxury cues and emotional intelligence.\n\nDo not use flowery or exaggerated phrases.  Avoid repeating sentence structures or relying on vague themes without a clear visual or client connection. \n\nYour job is to generate:\n\nA caption (Instagram-ready, 2–4 sentences)\n\nA visual description (1–2 clear lines describing the image or post structure)\n\n📌 Caption Guidelines\nWrite 2–4 emotionally powerful sentences\n\nUse varied tone across captions: poetic, editorial, warm, reflective, celebratory\n\nNever repeat exact sentence patterns. Avoid empty adjectives. Favor simple, well-crafted language that speaks directly to the client’s values: family, permanence, beauty, and a home filled with meaningful art.\nUse no more than 1 subtle seasonal detail or metaphor per caption, and only if it adds meaning\n\nDo not use quote marks (\"...\") unless an actual quote is provided.\n\n🔁 Content Variation\n\nDon’t default to “family” as the emotional core in every caption. Instead, rotate across:\n\n✨ Artistry & Craftsmanship – Mac’s eye, framing, archival prints, quality\n\n🏡 Interior Impact – how artwork transforms space\n\n🌱 Milestones – maternity, personal portraits, growth\n\n💬 Client Testimonials – If the Pillar is testimonial-based:\n- Begin the caption with this exact placeholder (do not replace it): [add personalized sentence]\n- Do not use quotation marks unless quoting a real client\n- Reflect the tone of the testimonial (e.g., appreciation, confidence, transformation), not the literal language\n- Focus on the client’s emotional journey or experience, not specific wording\n\n🕰️ Timelessness & Legacy – but not always via parenthood/family framing\n\nWhen family is the focus, vary your emotional angle (joy, strength, nostalgia, etc.) and avoid repetitive tones.\n\n🗓️ Seasonal Strategy (SeasonStage = {{ $json.SeasonStage }})\nUse SeasonStage to guide tone, emotion, and visual mood — not to mention the season name in every caption. Let seasonal cues emerge through imagery and energy, not explicit labels.\n\n✅ Use SeasonStage to:\n\nAdjust urgency and emotional pace (e.g., “final weeks to book”)\n\nShape imagery and mood (e.g., “golden evening light” vs. “sun-drenched beach”)\n\n❌ Do not:\n\nMention the season name (“summer,” “spring,” etc.) in every caption\n\nRepeat seasonal clichés (“As summer begins…” “This spring…”)\n\n⚠️ Limit explicit seasonal mentions to once every 4–5 captions unless the post is directly seasonal (e.g., Mother’s Day, holiday gifting)\n\n🧠 Emotional & Visual Hooks (optional ways to begin a post)\nYou may open a caption with:\n\nA vivid moment of interaction (“The way he reached for her hand mid-laugh…”)\n\nA home setting or design cue (“This foyer now holds three generations of smiles.”)\n\n\n🖼️ Visual Description Guidelines\n\nYou may describe the post’s format if relevant — e.g., “quote overlay,” “side-by-side transformation,” or “carousel of three candid moments.”\n\nAvoid values/emotions like “precious,” “timeless,” “beautiful connection”\n\nDescribe exactly what’s visible:\n\n“Client reviewing prints in the studio”\n\n“Framed black-and-white portrait above the fireplace”\n\n“Photo collage with a quote overlay”\n\nKeep descriptions brief, neutral in tone, and focused on layout or subject matter.\n\n🏷️ Hashtag Rules\nEnd every post with 13–15 hashtags:\n\nAlways include ALL brand hashtags:\n#hartshornportraiture #jcmoms #hobokenmommies #njfamilyphotographer #hobokenfamilyphotographer #hobokennj #hobokenmoms #jcmommies #jcfamilyphotographer #jerseycityphotography #hobokenphotographer\n\nAdd 2–3 custom/local hashtags based on content or location:\n\ne.g., #NJbeachportraits, #hobokenweekend, #hobokenstudio"
        },
        "promptType": "define"
      },
      "typeVersion": 1.8
    },
    {
      "id": "cc5be169-0f5f-4ff1-8cea-11bb1325970a",
      "name": "アイテムをループ処理",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        140,
        -40
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "8d8bb837-d650-488c-bb03-ce865fba2a6e",
      "name": "No Operation, 何もしない",
      "type": "n8n-nodes-base.noOp",
      "position": [
        460,
        -260
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "ae777421-894a-4013-8278-c6ff1c150e10",
      "name": "待機",
      "type": "n8n-nodes-base.wait",
      "position": [
        1620,
        -20
      ],
      "webhookId": "b5167d12-ee8b-49a0-836b-944b40e1a32a",
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "8f2da55a-4f97-4c80-beaa-2f6877d5ad36",
      "name": "マージ",
      "type": "n8n-nodes-base.merge",
      "position": [
        -440,
        -40
      ],
      "parameters": {},
      "typeVersion": 3.1
    },
    {
      "id": "e2f55596-4f66-437e-8509-4f3714285b65",
      "name": "参照",
      "type": "@n8n/n8n-nodes-langchain.toolCode",
      "position": [
        940,
        200
      ],
      "parameters": {
        "name": "Description_Reference",
        "jsCode": "const references = [\n  {\n    title: \"Framed Portrait in Home\",\n    description: \"Framed portrait hanging in a cozy home interior, placed above a mantel or sofa.\",\n    themes: [\"Framing\", \"Interior\", \"Wall Art\"]\n  },\n  {\n    title: \"Framing Overlay\",\n    description: \"A blurred frame photo with a text overlay that says 'custom framing.'\",\n    themes: [\"Framing\", \"Product\", \"Behind the Scenes\"]\n  },\n  {\n    title: \"Father’s Day Collage\",\n    description: \"A collage of 2–3 photos with 'Happy Father’s Day' text on top.\",\n    themes: [\"Father’s Day\", \"Holiday\"]\n  },\n  {\n    title: \"Testimonial Quote\",\n    description: \"Blurred image with a client review placed in elegant text overlay.\",\n    themes: [\"Testimonial\", \"Client Quote\"]\n  },\n  {\n    title: \"Mac Behind the Scenes\",\n    description: \"Photo of Mac working during a shoot — camera in hand, lights or backdrops visible.\",\n    themes: [\"Artist Insight\", \"Behind the Scenes\"]\n  },\n  {\n    title: \"Beach Portrait Styling\",\n    description: \"Carousel of beach portraits with text like 'what to wear' or 'color palette tips.'\",\n    themes: [\"Beach\", \"Style Guide\", \"Educational\"]\n  },\n  {\n    title: \"Studio Portrait\",\n    description: \"Neutral-tone studio portrait with soft lighting and minimal background.\",\n    themes: [\"Studio\", \"Interior\"]\n  },\n  {\n    title: \"Summer Promo\",\n    description: \"Ad-style post with text like 'Celebrate Summer' or 'Now Booking Beach Sessions.'\",\n    themes: [\"Seasonal\", \"Summer\", \"Ad\"]\n  },\n  {\n    title: \"Holiday Message\",\n    description: \"One image with a decorative border and greeting for July 4th, New Year, etc.\",\n    themes: [\"Holiday\", \"Seasonal\", \"Card\"]\n  },\n  {\n    title: \"Consultation Snapshot\",\n    description: \"A client and photographer reviewing prints or digital proofs in the studio.\",\n    themes: [\"Client Experience\", \"Behind the Scenes\", \"Studio\"]\n  },\n  {\n    title: \"Archival Printing\",\n    description: \"Close-up of the printer or archival paper roll in use, highlighting quality.\",\n    themes: [\"Printing\", \"Product\", \"Behind the Scenes\"]\n  },\n  {\n    title: \"Custom Albums\",\n    description: \"Photo of a hands flipping through a custom photo album on a wood table.\",\n    themes: [\"Albums\", \"Products\", \"Details\"]\n  },\n  {\n    title: \"Wall Display Mockup\",\n    description: \"Mockup of a wall gallery with 2–4 portrait prints in a clean, staged room.\",\n    themes: [\"Interior\", \"Framing\", \"Gallery\"]\n  },\n  {\n    title: \"Tools of the Trade\",\n    description: \"Flatlay of cameras, lenses, and accessories on a studio desk.\",\n    themes: [\"Artist Insight\", \"Tools\", \"Studio\"]\n  },\n  {\n    title: \"Gift Card Promo\",\n    description: \"Photo of a wrapped gift with an HP card insert, styled like a holiday present.\",\n    themes: [\"Gifts\", \"Promo\", \"Product\"]\n  }\n];\n\n// Matching logic\nconst input = query.toLowerCase();\n\nlet match = references.find(ref =>\n  ref.themes.some(theme => input.includes(theme.toLowerCase()))\n);\n\nif (!match) {\n  match = references[Math.floor(Math.random() * references.length)];\n}\n\nreturn match.description;\n",
        "description": "Help the AI Agent create a visual description of the post. Have it create one with a similar structure, clarity and simplicity. "
      },
      "typeVersion": 1.1
    },
    {
      "id": "b96217b2-3298-4f1e-bb8f-601728ed5986",
      "name": "タイトル、説明、URLを抽出",
      "type": "n8n-nodes-base.code",
      "position": [
        -1440,
        180
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst output = [];\n\nfor (const item of items) {\n  const metadata = item.json.metadata || {};\n  const url = metadata.canonicalUrl || item.json.url || '';\n  const title = metadata.title || '';\n  const description = metadata.description || '';\n  \n  // Optional: Filter out any posts without a title\n  if (title && url) {\n    output.push({\n      json: {\n        URL: url,\n        Title: title,\n        Description: description\n      }\n    });\n  }\n}\n\nreturn output;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "2d435a34-ac65-4d26-8c09-fe87afcc6e71",
      "name": "希望のブログ月を入力",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -1240,
        180
      ],
      "parameters": {
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "Fujtlc6eM7k9s5AV",
          "name": "Google Sheets account 3"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "1390ac63-087a-49b0-b0ee-de7679160e36",
      "name": "ブログ記事をスクレイピング",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1640,
        180
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "c1bce033-228d-44f0-a60e-26dc4068e696",
      "name": "リクエスト制限回避のための待機",
      "type": "n8n-nodes-base.wait",
      "position": [
        -1020,
        180
      ],
      "webhookId": "a57b14a3-b7a8-4359-b3d7-aa69f72d1123",
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "52b5306c-b81a-43b2-a778-63f86af543b0",
      "name": "希望のブログ月を読み取り",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -820,
        180
      ],
      "parameters": {
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "Fujtlc6eM7k9s5AV",
          "name": "Google Sheets account 3"
        }
      },
      "executeOnce": true,
      "typeVersion": 4.5
    },
    {
      "id": "e78d082b-711b-442a-aad3-b72b1a8b1744",
      "name": "データを読み取り",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -1240,
        -200
      ],
      "parameters": {
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "Fujtlc6eM7k9s5AV",
          "name": "Google Sheets account 3"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "ed563c5f-0c00-4c5e-9ed8-fed7d329cce9",
      "name": "情報を抽出",
      "type": "n8n-nodes-base.code",
      "position": [
        -200,
        -40
      ],
      "parameters": {
        "jsCode": "const startDate = new Date(\"2025-07-07\");\nconst endDate = new Date(\"2026-01-30\");\n\nconst daysOfWeek = [1, 3, 5]; // Monday, Wednesday, Friday\nconst fallbackPillars = [\"Family Images\", \"Behind-the-Scenes\"];\nconst serviceSubThemes = [\n  \"Beach\", \"Park\", \"Family\", \"Maternity\", \"Newborns\",\n  \"Headshots\", \"Studio\", \"Beauty\", \"Frames\",\n  \"Photo albums\", \"Cards\", \"Birthdays\", \"Anniversaries\"\n];\nconst processSubThemes = [\n  \"Images of studio layout/exterior of studio\",\n  \"Printer + archival inks\",\n  \"Frame options/process\",\n  \"Photo of camera/lighting equipment used to prove quality\",\n  \"Photo presentation and selection\",\n  \"Photo of consultation (blurred client + focus on HP employee)\"\n];\n\nconst items = $input.all();\nconst contentRows = items.filter(r => r.json.Pillar);\nconst blogPosts = items.filter(r => r.json.URL);\n\nconst output = [];\nconst lastUsedDates = {};\nconst serviceUsedMonths = {};\nconst processUsedMonths = {};\nlet serviceSubThemeIndex = 0;\nlet processSubThemeIndex = 0;\nlet lastFallbackUsed = null;\n\nfunction getSeasonStage(date) {\n  const month = date.getMonth() + 1; // JavaScript months are 0-based\n\n  if (month === 12 || month === 1 || month === 2) {\n    if (month === 12) return \"Early Winter\";\n    if (month === 1) return \"Mid Winter\";\n    return \"Late Winter\"; // February\n  }\n  if (month === 3 || month === 4 || month === 5) {\n    if (month === 3) return \"Early Spring\";\n    if (month === 4) return \"Mid Spring\";\n    return \"Late Spring\"; // May\n  }\n  if (month === 6 || month === 7 || month === 8) {\n    if (month === 6) return \"Early Summer\";\n    if (month === 7) return \"Mid Summer\";\n    return \"Late Summer\"; // August\n  }\n  if (month === 9 || month === 10 || month === 11) {\n    if (month === 9) return \"Early Fall\";\n    if (month === 10) return \"Mid Fall\";\n    return \"Late Fall\"; // November\n  }\n}\n\n\nfunction getMinDays(freq) {\n  const f = (freq || '').toLowerCase();\n  if (f.includes('bi-weekly')) return 14;\n  if (f.includes('weekly')) return 7;\n  if (f.includes('daily')) return 1;\n  if (f.includes('every 2 months')) return 60;\n  if (f.includes('monthly') || f.includes('every 4 weeks')) return 30;\n  return 14;\n}\n\nfunction daysBetween(d1, d2) {\n  return Math.floor((d2 - d1) / (1000 * 60 * 60 * 24));\n}\n\nfunction isScheduledDay(date) {\n  return daysOfWeek.includes(date.getDay());\n}\n\nfor (let d = new Date(startDate); d <= endDate; d.setDate(d.getDate() + 1)) {\n  if (!isScheduledDay(d)) continue;\n\n  const date = new Date(d);\n  const dateStr = date.toISOString().split('T')[0];\n  const dayName = date.toLocaleDateString('en-US', { weekday: 'long' });\n  const seasonStage = getSeasonStage(date);\n  const monthKey = `${date.getFullYear()}-${date.getMonth()}`;\n  const currentMonthName = date.toLocaleString('default', { month: 'long' }).toLowerCase();\n  let scheduled = false;\n\n  // Blog logic\n  const blog = blogPosts.find(b => {\n    const preferredMonth = (b.json[\"Preferred Month\"] || '').toLowerCase();\n    return preferredMonth === currentMonthName && !b.json._used;\n  });\n\n  if (blog) {\n    blog.json._used = true;\n    output.push({\n      json: {\n        Date: dateStr,\n        Day: dayName,\n        Pillar: \"Posts based on blogs\",\n        Format: \"Images\",\n        Description: blog.json.Description || '',\n        Examples: `${blog.json.Title} — ${blog.json.URL}`,\n        SeasonStage: seasonStage\n      }\n    });\n    continue;\n  }\n\n  // Regular content\n  for (const row of contentRows) {\n    const basePillar = row.json.Pillar.trim();\n    const freq = row.json.Frequency;\n    const minGap = getMinDays(freq);\n    const lastUsed = lastUsedDates[basePillar];\n    const diff = lastUsed ? daysBetween(lastUsed, date) : Infinity;\n\n    if (diff < minGap) continue;\n    if (output.length > 0 && output[output.length - 1].json.Pillar === basePillar) continue;\n\n    const post = JSON.parse(JSON.stringify(row.json)); // Deep copy\n    let finalPillar = basePillar;\n\n    if (basePillar.toLowerCase() === \"types of services offered\") {\n      if (serviceUsedMonths[monthKey]) continue;\n      const sub = serviceSubThemes[serviceSubThemeIndex % serviceSubThemes.length];\n      finalPillar = `Types of services offered – ${sub}`;\n      serviceUsedMonths[monthKey] = true;\n      serviceSubThemeIndex++;\n    }\n\n    if (basePillar.toLowerCase() === \"explanation of processes (ex: presentation, printing, installation)\") {\n      if (processUsedMonths[monthKey]) continue;\n      const sub = processSubThemes[processSubThemeIndex % processSubThemes.length];\n      finalPillar = `Explanation of processes – ${sub}`;\n      processUsedMonths[monthKey] = true;\n      processSubThemeIndex++;\n    }\n\n    output.push({\n      json: {\n        Date: dateStr,\n        Day: dayName,\n        Pillar: finalPillar,\n        Format: post[\"Content Form\"] || post[\"Format\"] || '',\n        Description: post[\"Structure\"] || '',\n        Examples: post[\"Examples\"] || '',\n        SeasonStage: seasonStage\n      }\n    });\n    lastUsedDates[basePillar] = date;\n    scheduled = true;\n    break;\n  }\n\n  // Fallback logic\n  if (!scheduled) {\n    const options = fallbackPillars.filter(p => p !== lastFallbackUsed);\n    const fallback = options[0] || fallbackPillars[0];\n    output.push({\n      json: {\n        Date: dateStr,\n        Day: dayName,\n        Pillar: fallback,\n        Format: '',\n        Description: '',\n        Examples: '',\n        SeasonStage: seasonStage\n      }\n    });\n    lastFallbackUsed = fallback;\n  }\n}\n\nreturn output;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "4fd2f21e-636a-4d97-afde-76a735d76bbc",
      "name": "祝日を特定",
      "type": "n8n-nodes-base.code",
      "position": [
        440,
        -20
      ],
      "parameters": {
        "jsCode": "const post = $json;\nconst date = new Date(post.Date);\nconst year = date.getFullYear();\nconst month = date.getMonth(); // 0 = Jan, 11 = Dec\nconst dateNum = date.getDate();\nconst day = date.getDay(); // 0 = Sun, 1 = Mon, ..., 6 = Sat\n\nconst formattedDate = date.toISOString().split(\"T\")[0];\n\nfunction isNthWeekday(month, weekday, n) {\n  let count = 0;\n  for (let i = 1; i <= 31; i++) {\n    const d = new Date(year, month, i);\n    if (d.getMonth() !== month) break;\n    if (d.getDay() === weekday) count++;\n    if (count === n) return i;\n  }\n  return -1;\n}\n\nfunction isLastWeekday(month, weekday) {\n  let last = -1;\n  for (let i = 1; i <= 31; i++) {\n    const d = new Date(year, month, i);\n    if (d.getMonth() !== month) break;\n    if (d.getDay() === weekday) last = i;\n  }\n  return last;\n}\n\n// Easter (hardcoded for accuracy)\nconst easterDates = {\n  2025: \"2025-04-20\",\n  2026: \"2026-04-05\",\n  2027: \"2027-03-28\"\n};\n\nlet holiday = \"None\";\n\n// Fixed-date holidays\nif (month === 0 && dateNum === 1) holiday = \"New Year’s Day\";\nelse if (month === 1 && dateNum === 14) holiday = \"Valentine’s Day\";\nelse if (month === 2 && dateNum === 17) holiday = \"St. Patrick’s Day\";\nelse if (formattedDate === easterDates[year]) holiday = \"Easter\";\nelse if (month === 5 && dateNum === 19) holiday = \"Juneteenth\";\nelse if (month === 6 && dateNum === 4) holiday = \"Independence Day\";\nelse if (month === 9 && dateNum === 11) holiday = \"Veterans Day\";\nelse if (month === 9 && dateNum === 31) holiday = \"Halloween\"; // rare edge case\nelse if (month === 11 && dateNum === 25) holiday = \"Christmas Day\";\n\n// Floating holidays\nelse if (month === 0 && dateNum === isNthWeekday(0, 1, 3)) holiday = \"Martin Luther King Jr. Day\";\nelse if (month === 1 && dateNum === isNthWeekday(1, 1, 3)) holiday = \"Presidents’ Day\";\nelse if (month === 4 && dateNum === isNthWeekday(4, 0, 2)) holiday = \"Mother’s Day\";\nelse if (month === 4 && dateNum === isLastWeekday(4, 1)) holiday = \"Memorial Day\";\nelse if (month === 5 && dateNum === isNthWeekday(5, 0, 3)) holiday = \"Father’s Day\";\nelse if (month === 8 && dateNum === isNthWeekday(8, 1, 1)) holiday = \"Labor Day\";\nelse if (month === 9 && dateNum === isNthWeekday(9, 1, 2)) holiday = \"Columbus Day\";\nelse if (month === 10 && dateNum === isNthWeekday(10, 4, 4)) holiday = \"Thanksgiving\";\n\npost.Holiday = holiday;\nreturn { json: post };\n"
      },
      "typeVersion": 2
    },
    {
      "id": "a097c38c-d64e-41e9-92db-206304a7b62d",
      "name": "キャプション、説明、ハッシュタグ",
      "type": "n8n-nodes-base.code",
      "position": [
        1160,
        -20
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const fullText = $json.output || '';\nconst lines = fullText.split('\\n');\n\nlet caption = '';\nlet visualDescription = '';\nlet hashtags = '';\n\nlet currentSection = '';\n\nfor (const line of lines) {\n  const trimmed = line.trim();\n\n  // Normalize section header detection (handles : — or none)\n  if (/^\\*?\\*?\\s*Caption\\b/i.test(trimmed)) {\n    currentSection = 'caption';\n    continue;\n  } else if (/^\\*?\\*?\\s*Visual Description\\b/i.test(trimmed)) {\n    currentSection = 'visualDescription';\n    continue;\n  } else if (/^\\*?\\*?\\s*Hashtags\\b/i.test(trimmed)) {\n    currentSection = 'hashtags';\n    continue;\n  }\n\n  if (currentSection === 'caption') caption += trimmed + ' ';\n  else if (currentSection === 'visualDescription') visualDescription += trimmed + ' ';\n  else if (currentSection === 'hashtags') hashtags += trimmed + ' ';\n}\n\nreturn {\n  json: {\n    Caption: caption.trim(),\n    Description: visualDescription.trim(),\n    Hashtags: hashtags.trim()\n  }\n};\n"
      },
      "typeVersion": 2
    },
    {
      "id": "90e6800b-0abe-4389-9368-41ce6b1bec3e",
      "name": "フィールド名を変更",
      "type": "n8n-nodes-base.set",
      "position": [
        1380,
        -20
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "cc70084f-cad3-45d8-a680-09a59fa6c87e",
              "name": "Caption",
              "type": "string",
              "value": "={{ $json.Caption }}"
            },
            {
              "id": "4e265422-f477-4a90-8043-ca6ede55d7e6",
              "name": "Hashtags",
              "type": "string",
              "value": "={{ $json.Hashtags }}"
            },
            {
              "id": "d16a69df-4b8e-464e-a19d-e8566c12897d",
              "name": "Date",
              "type": "string",
              "value": "={{ $('Loop Over Items').item.json.Date }}"
            },
            {
              "id": "7e52cc33-9c30-4f22-9260-d04d6f2d365d",
              "name": "Day",
              "type": "string",
              "value": "={{ $('Loop Over Items').item.json.Day }}"
            },
            {
              "id": "8b1956b4-e34a-46c0-9e9e-fe21a8b49ffc",
              "name": "Pillar",
              "type": "string",
              "value": "={{ $('Loop Over Items').item.json.Pillar }}"
            },
            {
              "id": "8a7356bc-3292-41e7-8f04-bc156e8090c0",
              "name": "Format",
              "type": "string",
              "value": "={{ $('Loop Over Items').item.json.Format }}"
            },
            {
              "id": "8bc6ddeb-71c2-419c-8be4-522cb7ad96e2",
              "name": "Description",
              "type": "string",
              "value": "={{ $json.Description }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "04be4907-f32c-4551-a4c5-b8de83c1b511",
      "name": "データをエクスポート",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1860,
        -20
      ],
      "parameters": {
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "Fujtlc6eM7k9s5AV",
          "name": "Google Sheets account 3"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "1277c53a-64bc-4dd6-a100-497a37ad22d7",
      "name": "付箋",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3180,
        -960
      ],
      "parameters": {
        "width": 1080,
        "height": 2060,
        "content": "## Try It Out!\n\n### This n8n template creates a fully automated Instagram content schedule using AI and Google Sheets. It is perfect for content creators, marketing teams, or local businesses looking to organize and scale their social media posting.\n\n### How it works\nThe workflow starts by reading two sets of inputs from a Google Sheet:\n- Your content strategy inputs (Pillar, Objective, Frequency, Format, Structure, Examples).\n- A list of scraped blog posts with title, URL, and description (fetched from your website).\n- Blog posts are scraped using Apify and parsed to extract key fields, which are stored in a tab labeled \"Input (blog month)\".\n- You can assign a preferred posting month for each blog (e.g. fall blog posts get tagged for September).\n- The workflow then merges both inputs and extracts the relevant information for further information added by ChatGPT.\n\n### AI Scheduling & Personalization\nOnce merged, the workflow loops through each content item and:\n- Identifies if the scheduled post falls on or near a holiday (like Mother’s Day) and adjusts the content accordingly.\n- A reference tool is attached to guide structure and tone, based on a library of post examples.\n- Sends the content to an AI Agent (using GPT-4, but customizable) that generates:\n1. A compelling Instagram caption\n2. A visual description\n3. Hashtags\n4. Suggested post date, day, content pillar, and format (carousel, reel, image, etc.)\n\n### Output\n- All generated content including captions, structure, dates, hashtags, and pillar is exported into a tab titled Output in your Google Sheet.\n- The final schedule is ready for manual review, editing, or publishing to social media.\n\n### How to use\n- The workflow uses a manual trigger to start, but you can replace it with a Webhook, cron job, or form submission.\n- Add/edit your content strategy in Google Sheets.\n\n### How to Set-Up\n### Initial Input Tab\n- Define your content pillars and structure\n- Create a tab named \"Input\" or \"Strategy\" \nInclude these columns:\n- Pillar: e.g., Family images\n- Objective: e.g., Showcase images\n- Frequency: e.g., Bi-weekly\n- Content Form: e.g., Images, Reels\n- Structure: brief description of expected layout (e.g., carousel Q&A, singular photo)\n- Examples: prompts or questions to guide AI (e.g., Why do you think families should do a session?)\n\n### Input (blog month) Tab – Store scraped blog content\nInclude these columns:\n- URL: direct link to blog post\n- Title: blog post title\n- Description: short summary of the post\n- Preferred Month: month you want it posted (e.g., August, September)\n- This sheet is partially auto-filled by the workflow (except for Preferred Month)\n\n### Output Tab – Final scheduled content\nInclude these columns:\n- Date: scheduled posting date (YYYY-MM-DD)\n- Day: day of the week\n- Pillar: content category assigned\n- Format: e.g., Images, Reels, Carousel\n- Description: visual summary\n- Caption: Instagram-ready caption\n- Hashtags: complete hashtag block\n\n### To use the Apify HTTP Request node:\n1. Drag in an HTTP Request node into your n8n workflow.\n2. Set the Method and URL based on how you're using Apify:\n- Use POST if you want to run an actor live with dynamic input (e.g. scrape blog posts in real time).\n- Use GET if you want to retrieve results from a completed or static dataset run (faster and cheaper if you're reusing previous data).\n3. Configure query or body parameters:\n4. Include your Apify API token for authentication (e.g. token=YOUR_API_KEY)\n- For POST: include an input object with any required actor settings (e.g., blog URL to scrape).\n- For GET: specify the dataset ID in the URL \n5. Test the node to ensure you're retrieving the blog titles, descriptions, and URLs as expected.\n\n### Requirements\n- Apify account for scraping blog posts\n- OpenAI key (e.g. GPT-4) or another model of your choice\n- Google Sheets Credentials \n\n### Example Use Cases\n- A photographer repurposing blogs into Instagram carousels\n- A nonprofit automatically generating seasonal posts \n- A small team managing multi-pillar content across weeks or months\n\n### Need Help?\nJoin the n8n Discord or ask in the n8n Forum!\nHappy Content Making ! 📅✨\n"
      },
      "typeVersion": 1
    },
    {
      "id": "fac1861d-edea-4450-bae2-d2a4cc4a3ef5",
      "name": "付箋6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2020,
        -480
      ],
      "parameters": {
        "color": 7,
        "width": 1960,
        "height": 980,
        "content": "# 1. Two Sets of Input\n\nSet #1 (top): Reads the initial input data that is manually inputted of the type of pillar(s) you want to focus on in your content, it's objective, their frequency, form of content, structure, and examples. \n\n\nSet #2 (bottom): The Apify actor scrapes websites for their blog post to repurpose as social media content. Next, the code node will extract the title, description and URL. You can manually input the preferred month you want the post posted and the workflow will take that into account when creating the schedule. \n\nThe information from these two sets of input will merge and eventually, all the relevant information will be extracted for further analysis. "
      },
      "typeVersion": 1
    },
    {
      "id": "c0cd04bd-7a71-4250-abe8-369e547956bf",
      "name": "付箋7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        60,
        -480
      ],
      "parameters": {
        "color": 7,
        "width": 1960,
        "height": 980,
        "content": "# 2. Use GPT-4 to Create Captions, Hashtags, and Visual Descriptions\n\nUsing the Code Node \"Identify Holiday\", the AI will tailor a post accordingly if it falls on the day of a holiday. For example, if a post falls on Mother's Day, it may use that to set the scene for the rest of the post. The OpenAI Agent will create the visual description, hashtag, caption, and date and day it should be posted. The AI takes into account the examples listed earlier in the Google Sheets so if you want to tweak the results, that would be a good start.\n\nLastly, after the AI Agent has run, all of the data is exported into a google sheets tab labelled \"Output\". "
      },
      "typeVersion": 1
    },
    {
      "id": "18c9f341-d123-47d8-a049-e0a41c6ff8d8",
      "name": "付箋1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2020,
        -1320
      ],
      "parameters": {
        "width": 1880,
        "height": 800,
        "content": "## Input #1: \"Initial Input\" \n\n![Workflow Screenshot](https://i.imgur.com/UTa0VCH.png)\n"
      },
      "typeVersion": 1
    },
    {
      "id": "447abe53-d03d-4905-b1f8-e69991210d11",
      "name": "付箋2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2020,
        560
      ],
      "parameters": {
        "width": 1880,
        "height": 440,
        "content": "## Input #2: \"Input (blog month)\" \n\n![Google Sheets Strategy Tab](https://i.imgur.com/g1GYuU7.png)\n"
      },
      "typeVersion": 1
    },
    {
      "id": "eb712852-4cb7-4618-9437-edc2aa203017",
      "name": "付箋3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2080,
        -460
      ],
      "parameters": {
        "width": 1860,
        "height": 580,
        "content": "## Output \n![Blog Input Tab](https://i.imgur.com/onDxG3U.png)\n\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "cf50b336-182f-4027-9c8c-611c2d4e599c",
  "connections": {
    "ae777421-894a-4013-8278-c6ff1c150e10": {
      "main": [
        [
          {
            "node": "04be4907-f32c-4551-a4c5-b8de83c1b511",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "8f2da55a-4f97-4c80-beaa-2f6877d5ad36": {
      "main": [
        [
          {
            "node": "ed563c5f-0c00-4c5e-9ed8-fed7d329cce9",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "e78d082b-711b-442a-aad3-b72b1a8b1744": {
      "main": [
        [
          {
            "node": "8f2da55a-4f97-4c80-beaa-2f6877d5ad36",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "e2f55596-4f66-437e-8509-4f3714285b65": {
      "ai_tool": [
        [
          {
            "node": "b220a61b-3bce-49db-a5cc-0b56e0480f39",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "04be4907-f32c-4551-a4c5-b8de83c1b511": {
      "main": [
        [
          {
            "node": "cc5be169-0f5f-4ff1-8cea-11bb1325970a",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "90e6800b-0abe-4389-9368-41ce6b1bec3e": {
      "main": [
        [
          {
            "node": "ae777421-894a-4013-8278-c6ff1c150e10",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "cc5be169-0f5f-4ff1-8cea-11bb1325970a": {
      "main": [
        [
          {
            "node": "8d8bb837-d650-488c-bb03-ce865fba2a6e",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "4fd2f21e-636a-4d97-afde-76a735d76bbc",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "4fd2f21e-636a-4d97-afde-76a735d76bbc": {
      "main": [
        [
          {
            "node": "b220a61b-3bce-49db-a5cc-0b56e0480f39",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1390ac63-087a-49b0-b0ee-de7679160e36": {
      "main": [
        [
          {
            "node": "b96217b2-3298-4f1e-bb8f-601728ed5986",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "ed563c5f-0c00-4c5e-9ed8-fed7d329cce9": {
      "main": [
        [
          {
            "node": "cc5be169-0f5f-4ff1-8cea-11bb1325970a",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "8d8bb837-d650-488c-bb03-ce865fba2a6e": {
      "main": [
        []
      ]
    },
    "52b5306c-b81a-43b2-a778-63f86af543b0": {
      "main": [
        [
          {
            "node": "8f2da55a-4f97-4c80-beaa-2f6877d5ad36",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "2d435a34-ac65-4d26-8c09-fe87afcc6e71": {
      "main": [
        [
          {
            "node": "c1bce033-228d-44f0-a60e-26dc4068e696",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "b220a61b-3bce-49db-a5cc-0b56e0480f39": {
      "main": [
        [
          {
            "node": "a097c38c-d64e-41e9-92db-206304a7b62d",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "c1bce033-228d-44f0-a60e-26dc4068e696": {
      "main": [
        [
          {
            "node": "52b5306c-b81a-43b2-a778-63f86af543b0",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "a097c38c-d64e-41e9-92db-206304a7b62d": {
      "main": [
        [
          {
            "node": "90e6800b-0abe-4389-9368-41ce6b1bec3e",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "b96217b2-3298-4f1e-bb8f-601728ed5986": {
      "main": [
        [
          {
            "node": "2d435a34-ac65-4d26-8c09-fe87afcc6e71",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "99e295bb-f026-4641-a96a-098461e70cf7": {
      "main": [
        [
          {
            "node": "e78d082b-711b-442a-aad3-b72b1a8b1744",
            "type": "main",
            "index": 0
          },
          {
            "node": "1390ac63-087a-49b0-b0ee-de7679160e36",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
よくある質問

このワークフローの使い方は?

上記のJSON設定コードをコピーし、n8nインスタンスで新しいワークフローを作成して「JSONからインポート」を選択、設定を貼り付けて認証情報を必要に応じて変更してください。

このワークフローはどんな場面に適していますか?

上級 - コンテンツ作成, マルチモーダルAI

有料ですか?

このワークフローは完全無料です。ただし、ワークフローで使用するサードパーティサービス(OpenAI APIなど)は別途料金が発生する場合があります。

関連ワークフロー

テンプレート - ブログ生成
Googleの自動補完とGPT-4を使ってSEO最適化されたブログ記事を作成する
Set
Code
Wait
+
Set
Code
Wait
21 ノードkeisha kalra
コンテンツ作成
OpenAI、RunwayML、ElevenLabsを使って無顔の短い動画を自動化
OpenAI、RunwayML、ElevenLabs を使ってアニメ顔の短い動画を自動化:スクリプトからソーシャルメディアへ
Set
Code
Wait
+
Set
Code
Wait
56 ノードLeeWei
コンテンツ作成
OpenAI、ElevenLabs、Fal.ai を使用した動画・パ odcast・ASM R向けのウイルス性コンテンツ自動作成
OpenAI、ElevenLabs、そして Fal.ai を使って動画、ポッドキャスト、ASMR に向けたウイルスのコンテンツ作成を自動化
Set
Code
Wait
+
Set
Code
Wait
97 ノードAdam Crafts
コンテンツ作成
バッチSEOコンテンツ生成とAI画像付きWebflowドラフト作成(テンプレート)
GPT、Gemini画像、Webflowデラフトで行う大量SEOコンテンツ生成
If
Set
Code
+
If
Set
Code
54 ノードDahiana
コンテンツ作成
AIとFreepikを使用してRedditのビジネス課題をウイルスのLinkedInコンテンツに変換
AIとFreepikを使用してRedditのビジネス課題をウイルスのなLinkedInコンテンツに変換
If
Set
Code
+
If
Set
Code
48 ノードDaniel Lianes
コンテンツ作成
💥 NanoBanana、Seedream 4、ChatGPT Image、Veo 3 を使って動画広告を自動化 - VIDEO
AI(NanoBanana、Seedream、GPT-4o、Veo 3)を使って動画広告キャンペーンを自動化し公開
Set
Code
Wait
+
Set
Code
Wait
63 ノードDr. Firas
コンテンツ作成
ワークフロー情報
難易度
上級
ノード数24
カテゴリー2
ノードタイプ12
難易度説明

上級者向け、16ノード以上の複雑なワークフロー

外部リンク
n8n.ioで表示

このワークフローを共有

カテゴリー

カテゴリー: 34