AutomationFlowsGeneral › AI-Powered Stock Image SEO Workflow

AI-Powered Stock Image SEO Workflow

Original n8n title: Passive Income with Stock Images

Passive Income with Stock Images. Uses googleDrive, openAi, googleSheets. Event-driven trigger; 15 nodes.

Event trigger★★★★☆ complexityAI-powered15 nodesGoogle DriveOpenAIGoogle Sheets
General Trigger: Event Nodes: 15 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow follows the Google Drive → 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 →

Download .json
{
  "name": "Passive Income with Stock Images",
  "nodes": [
    {
      "parameters": {},
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -100,
        0
      ],
      "id": "90e24dc0-6df4-4aee-b3fe-77a06c88a7d7",
      "name": "When clicking \u2018Test workflow\u2019"
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        700,
        0
      ],
      "id": "9cb0b7f7-43d4-4e56-ae06-45d159e9c82c",
      "name": "Loop Over Items"
    },
    {
      "parameters": {
        "jsCode": "// Extract the JSON response from the previous step\nconst inputData = $input.first().json.content;\n\n// Strip out possible markdown wrappers like ```json\nconst sanitizedData = inputData.replace(/```json\\n\\n/g, '');\n\ntry {\n  // Attempt to parse the JSON string\n  const dataObjects = JSON.parse(sanitizedData);\n\n  // Format each object: convert keywords array into a comma-separated string\n  const result = dataObjects.map(entry => ({\n    json: {\n      Title: entry.Title,\n      Description: entry.Description,\n      Keywords: entry.Keywords.join(', ')\n    }\n  }));\n\n  return result;\n} catch (err) {\n  // Throw an error if the JSON is invalid\n  throw new Error(\"JSON parsing failed: \" + err.message);\n}"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1200,
        180
      ],
      "id": "ad580c69-397d-4d63-a8a5-6db26ecd5e10",
      "name": "Array to String"
    },
    {
      "parameters": {
        "resource": "fileFolder",
        "returnAll": true,
        "filter": {
          "folderId": {
            "__rl": true,
            "value": "1k9qStpXZAa19TEOGxpxzTWLaZxv0EJUl",
            "mode": "list",
            "cachedResultName": "Upload Data Here | Adobe Stock",
            "cachedResultUrl": "https://drive.google.com/drive/folders/1k9qStpXZAa19TEOGxpxzTWLaZxv0EJUl"
          }
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        200,
        0
      ],
      "id": "072f17f0-d6a5-4f52-96f7-49ee77e588bb",
      "name": "Search for Images",
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "download",
        "fileId": {
          "__rl": true,
          "value": "={{ $json.id }}",
          "mode": "id"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        400,
        0
      ],
      "id": "421839e2-e0f8-4ce8-a5a4-36c3bb8a0506",
      "name": "Download Images",
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "resource": "image",
        "operation": "analyze",
        "modelId": {
          "__rl": true,
          "value": "gpt-4o-mini",
          "mode": "list",
          "cachedResultName": "GPT-4O-MINI"
        },
        "text": "=** Task **\nCreate metadata for a single image in valid JSON format. The output must be a JSON array containing one or more objects, where each object includes:\n\n\n\"Title\" \u2013 A descriptive name for the image. Between 50 - 80 characters\n\n\"Keywords\" \u2013 A list of 55 keywords, each relevant to the image content, with a max length of 30 characters each\n\n\n** Format Requirements **\n\n- Return a valid JSON array (no markdown or extra text)\n\n- The Keywords field must always include exactly 55 strings\n\n- Each keyword string must be under or equal to 30 characters\n\n- Follow strict JSON syntax\n\n** Example Output **\n\n[\n  {\n    \"Title\": \"Sunset Over the Ocean\",\n    \"Keywords\": [\"sunset\", \"ocean\", \"sea\", \"water\", \"golden hour\", \"nature\", \"scenic\", \"landscape\", \"travel\", \"peaceful\", \"sun\", \"sky\", \"evening\", \"coast\", \"waves\", \"reflection\", \"tranquil\", \"serene\", \"beach\", \"clouds\", \"light\", \"summer\", \"dusk\", \"horizon\", \"natural beauty\"]\n  }\n]\n\n** Important **\nOnly return the JSON array. Do not include explanations, formatting syntax, or headers.",
        "inputType": "base64",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 1.8,
      "position": [
        1000,
        180
      ],
      "id": "c008cfcb-8d7d-4bf3-8ede-5baca0b47f94",
      "name": "Create Title & Keywords",
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "1HCNfC7h_qk8goQh5DOTw0pMxcu0Qe3U7fVzldop7qzg",
          "mode": "list",
          "cachedResultName": "Adobe Stock",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1HCNfC7h_qk8goQh5DOTw0pMxcu0Qe3U7fVzldop7qzg/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "Sheet1",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1HCNfC7h_qk8goQh5DOTw0pMxcu0Qe3U7fVzldop7qzg/edit#gid=0"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Title": "={{ $json.Title }}",
            "Keywords": "={{ $json.Keywords }}",
            "Filename": "={{ $('Loop Over Items').item.json.name }}",
            "Category": "1"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "Filename",
              "displayName": "Filename",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Title",
              "displayName": "Title",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Keywords",
              "displayName": "Keywords",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Category",
              "displayName": "Category",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Releases",
              "displayName": "Releases",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1400,
        180
      ],
      "id": "73f2930b-7400-44ff-82a4-df974a067147",
      "name": "Google Sheets",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "deleteFile",
        "fileId": {
          "__rl": true,
          "value": "={{ $('Search for Images').item.json.id }}",
          "mode": "id"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        1700,
        180
      ],
      "id": "82f206a9-5202-4945-9310-0e7b5224d5d1",
      "name": "Google Drive",
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "amount": 3,
        "unit": "minutes"
      },
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        1000,
        -100
      ],
      "id": "e9f5c583-cade-4546-98cf-831aee1e0047",
      "name": "Wait for 3 Minutes"
    },
    {
      "parameters": {
        "operation": "clear",
        "documentId": {
          "__rl": true,
          "value": "1HCNfC7h_qk8goQh5DOTw0pMxcu0Qe3U7fVzldop7qzg",
          "mode": "list",
          "cachedResultName": "Adobe Stock",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1HCNfC7h_qk8goQh5DOTw0pMxcu0Qe3U7fVzldop7qzg/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "Sheet1",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1HCNfC7h_qk8goQh5DOTw0pMxcu0Qe3U7fVzldop7qzg/edit#gid=0"
        },
        "keepFirstRow": true
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1200,
        -100
      ],
      "id": "88659757-237f-46d4-98cc-cc5272ed5a2f",
      "name": "Clear Sheet",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "content": "## Get Images",
        "height": 240,
        "width": 420
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        140,
        -60
      ],
      "id": "9130f967-37f6-4c12-90f8-9d5b1ba621d7",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "content": "## Create Titles & Keywords",
        "height": 260,
        "width": 620,
        "color": 5
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        940,
        80
      ],
      "id": "a6439d50-b95f-464e-b356-87031bce88f0",
      "name": "Sticky Note1"
    },
    {
      "parameters": {
        "content": "## Delete Images",
        "height": 260,
        "width": 320,
        "color": 4
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1580,
        80
      ],
      "id": "ded6408f-e46c-4907-af64-e4fcbaf59880",
      "name": "Sticky Note2"
    },
    {
      "parameters": {
        "content": "## Delete Data & Prepare for Next Content",
        "height": 260,
        "width": 620,
        "color": 3
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        940,
        -200
      ],
      "id": "5c95e0fb-f2be-4d19-8d01-35854573e256",
      "name": "Sticky Note3"
    },
    {
      "parameters": {
        "content": "## Repeat for Each Image",
        "height": 240,
        "width": 340,
        "color": 6
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        580,
        -60
      ],
      "id": "ac24fab1-1f5b-498d-becc-812da2cabe22",
      "name": "Sticky Note4"
    }
  ],
  "connections": {
    "When clicking \u2018Test workflow\u2019": {
      "main": [
        [
          {
            "node": "Search for Images",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [
          {
            "node": "Wait for 3 Minutes",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Create Title & Keywords",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Array to String": {
      "main": [
        [
          {
            "node": "Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search for Images": {
      "main": [
        [
          {
            "node": "Download Images",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Images": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Title & Keywords": {
      "main": [
        [
          {
            "node": "Array to String",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets": {
      "main": [
        [
          {
            "node": "Google Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Drive": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait for 3 Minutes": {
      "main": [
        [
          {
            "node": "Clear Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "db2c197d-18cc-435a-8c8a-eab51d4b2b93",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "id": "L05uXdIRJvWEMTIh",
  "tags": []
}

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.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

How this works

Generate passive income by transforming your stock images into sellable assets with AI-powered metadata, saving hours of manual tagging and boosting discoverability on platforms like Shutterstock. This workflow suits photographers and creators with Google Drive folders full of untagged images who want to automate content preparation for marketplaces. It starts by looping through your images, downloading them from Google Drive, then leverages OpenAI to craft compelling titles and keywords before logging everything in Google Sheets for easy tracking and export.

Use this when you have batches of 50-200 stock photos ready for upload but lack time for SEO optimisation, especially for recurring monthly uploads. Avoid it for high-volume libraries over 1,000 images, as processing could hit API limits; instead, run in smaller segments. Common variations include swapping OpenAI for a custom prompt to target specific niches like travel or food photography, or adding a node to directly upload optimised files to an FTP for stock sites.

About this workflow

Passive Income with Stock Images. Uses googleDrive, openAi, googleSheets. Event-driven trigger; 15 nodes.

Source: https://github.com/Khuzaima-AI-2112/n8n-automation-templates/blob/master/02_Visuals-&-Social-Media/05_Make-passive-income-with-this-automation/Passive_Income_with_Stock_Images.json — original creator credit. Request a take-down →

More General workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

General

SEO key word analysis and filter. Uses manualTrigger, lmChatOpenAi, googleDrive, extractFromFile. Event-driven trigger; 26 nodes.

OpenAI Chat, Google Drive, Text Classifier +1
General

AI Story Generator. Uses chatTrigger, httpRequest, openAi, googleSheets. Chat trigger; 14 nodes.

Chat Trigger, HTTP Request, OpenAI +2
General

Fetch the Most Recent Document from Google Drive. Uses googleDocs, toolWikipedia, toolCalculator, googleSheets. Event-driven trigger; 12 nodes.

Google Docs, Tool Wikipedia, Tool Calculator +3
General

Create and publish Instagram carousels using OpenAI gpt-image-1 and AI caption. Uses chainLlm, outputParserItemList, lmChatOpenAi, splitInBatches. Scheduled trigger; 32 nodes.

Chain Llm, Output Parser Item List, OpenAI Chat +3
General

PPC Thievery. Uses manualTrigger, googleDrive, stickyNote, httpRequest. Event-driven trigger; 28 nodes.

Google Drive, HTTP Request, OpenAI +1