This workflow corresponds to n8n.io template #17908 — 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 →
{
"name": "Automate image copyright checks on Google Lens into Google Sheets",
"nodes": [
{
"id": "b3e6f75a-0001-4c01-d001-000000000001",
"name": "Every Monday morning",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
0,
520
],
"parameters": {
"rule": {
"interval": [
{
"field": "weeks",
"triggerAtDay": [
1
],
"triggerAtHour": 8
}
]
}
},
"typeVersion": 1.2
},
{
"id": "b3e6f75a-0002-4c02-d002-000000000002",
"name": "Set the images to watch",
"type": "n8n-nodes-base.set",
"position": [
260,
520
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "b3e6f75a-0011-4c11-d011-+1234567890",
"name": "imageUrls",
"type": "array",
"value": "[\"https://images-assets.nasa.gov/image/PIA12348/PIA12348~medium.jpg\"]"
},
{
"id": "b3e6f75a-0012-4c12-d012-+1234567890",
"name": "maxMatchesPerImage",
"type": "number",
"value": "25"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "b3e6f75a-0003-4c03-d003-000000000003",
"name": "One lookup per image",
"type": "n8n-nodes-base.splitOut",
"position": [
520,
520
],
"parameters": {
"include": "allOtherFields",
"options": {},
"fieldToSplitOut": "imageUrls"
},
"typeVersion": 1
},
{
"id": "b3e6f75a-0004-4c04-d004-000000000004",
"name": "Find pages using the image",
"type": "@apify/n8n-nodes-apify.apify",
"position": [
780,
520
],
"parameters": {
"memory": 1024,
"actorId": "johnvc~google-lens-api",
"resource": "Actors",
"operation": "Run actor and get dataset",
"customBody": "={{ JSON.stringify({ image_url: $json.imageUrls, search_type: 'exact_matches', max_results: $json.maxMatchesPerImage }) }}"
},
"typeVersion": 1
},
{
"id": "b3e6f75a-0005-4c05-d005-000000000005",
"name": "Keep rows that are real matches",
"type": "n8n-nodes-base.filter",
"position": [
1040,
520
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "b3e6f75a-0041-4c41-d041-+1234567890",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.resultType }}",
"rightValue": "match"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "b3e6f75a-0006-4c06-d006-000000000006",
"name": "One row per page",
"type": "n8n-nodes-base.code",
"position": [
1300,
520
],
"parameters": {
"jsCode": "// One row per page carrying the image, with the source image kept on every\n// row so a sheet covering a whole portfolio stays sortable.\nconst weekOf = $now.toFormat('yyyy-MM-dd');\n\nconst clip = (s, n) => {\n const v = String(s || '').replace(/\\s+/g, ' ').trim();\n return v.length > n ? v.slice(0, n - 3) + '...' : v;\n};\n\nreturn $input.all().map((item) => {\n const m = item.json;\n return {\n json: {\n 'Week of': weekOf,\n 'Your image': m.queryImage || '',\n Rank: m.position ?? '',\n 'Site using it': m.source || '',\n 'Page title': clip(m.title, 180),\n 'Page URL': m.url || '',\n Width: typeof m.imageWidth === 'number' ? m.imageWidth : '',\n Height: typeof m.imageHeight === 'number' ? m.imageHeight : '',\n },\n };\n});\n"
},
"typeVersion": 2
},
{
"id": "b3e6f75a-0007-4c07-d007-000000000007",
"name": "Append image uses to sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
1560,
520
],
"parameters": {
"columns": {
"value": {},
"mappingMode": "autoMapInputData",
"matchingColumns": []
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": ""
},
"documentId": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"typeVersion": 4.5
},
{
"id": "b3e6f75a-0090-4c90-d090-000000000090",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-620,
40
],
"parameters": {
"width": 520,
"height": 1240,
"content": "## Automate image copyright checks on Google Lens into Google Sheets\n\nNo stock agency monitoring service and no per-image fee. Every week this looks up each image you list and appends one row per page carrying that exact image: the site using it, the page title, the page URL, and the dimensions of the copy they are serving.\n\nPoint it at your own photography, product shots or illustrations and the sheet becomes a usage log. Because it appends a dated snapshot, a site that appears in this week's rows and not last week's is a new use.\n\n**Who's it for**\nPhotographers and illustrators checking where their work ended up, brands watching for product shots on marketplaces they never authorised, and anyone who needs a list of URLs before they can send a single email.\n\n**How it works**\n1. A schedule trigger fires once a week\n2. One Set node holds the image URLs you want to watch\n3. The Apify node runs an exact-match lookup for each image\n4. A Code node turns each match into a row with the page and the site\n\n**Setup**\n1. Create a free Apify account and add your API token to the Apify credential\n2. Connect your Google account in the Sheets node and pick a spreadsheet\n3. Replace the example image URLs with links to your own images, each one publicly reachable without a login\n\n**Good to know**\nAbout a third of a cent per match, so an image with 30 uses costs about a cent to check.\nThis finds pages using your image. It does not decide who owns it or whether a use is licensed, so treat the sheet as a list of leads to review rather than a verdict.\nNot every image is in the index. A brand new upload, or one behind a login, will return nothing. Test one image manually before trusting a whole portfolio to a schedule.\nActor: https://apify.com/johnvc/google-lens-api?fpr=9n7kx3&fp_sid=n8n"
},
"typeVersion": 1
},
{
"id": "b3e6f75a-0091-4c91-d091-000000000091",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-40,
60
],
"parameters": {
"color": 7,
"width": 240,
"height": 240,
"content": "### 1. Schedule\nRuns every Monday morning. Change the cadence, or swap in a manual trigger while you test."
},
"typeVersion": 1
},
{
"id": "b3e6f75a-0092-4c92-d092-000000000092",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
220,
60
],
"parameters": {
"color": 7,
"width": 240,
"height": 240,
"content": "### 2. Your input\nThe image URLs to watch, and how many matches to return for each. This is the only node you edit."
},
"typeVersion": 1
},
{
"id": "b3e6f75a-0093-4c93-d093-000000000093",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
480,
60
],
"parameters": {
"color": 7,
"width": 240,
"height": 240,
"content": "### 3. Fan out\nTurns your image list into one Lens lookup each."
},
"typeVersion": 1
},
{
"id": "b3e6f75a-0094-4c94-d094-000000000094",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
740,
60
],
"parameters": {
"color": 7,
"width": 240,
"height": 240,
"content": "### 4. Look up\nRuns the Google Lens API actor in exact-match mode. Add your Apify credential here."
},
"typeVersion": 1
},
{
"id": "b3e6f75a-0095-4c95-d095-000000000095",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
1000,
60
],
"parameters": {
"color": 7,
"width": 240,
"height": 240,
"content": "### 5. Clean\nKeeps only real match rows, so an upstream error never reaches the sheet."
},
"typeVersion": 1
},
{
"id": "b3e6f75a-0096-4c96-d096-000000000096",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
1260,
60
],
"parameters": {
"color": 7,
"width": 240,
"height": 240,
"content": "### 6. Shape\nOne row per page using the image, with the source image kept on the row."
},
"typeVersion": 1
},
{
"id": "b3e6f75a-0097-4c97-d097-000000000097",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
1520,
60
],
"parameters": {
"color": 7,
"width": 240,
"height": 240,
"content": "### 7. Deliver\nPick your spreadsheet and tab. Columns map automatically."
},
"typeVersion": 1
},
{
"id": "b3e6f75a-0080-4c80-d080-000000000080",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
220,
860
],
"parameters": {
"color": 7,
"width": 400,
"height": 240,
"content": "### Images must be publicly reachable\nThe lookup fetches the URL you give it, so a link behind a login, a signed URL, or a local path returns nothing. Some hosts are simply not in the index either, so a zero-row result means unknown rather than unused."
},
"typeVersion": 1
},
{
"id": "b3e6f75a-0081-4c81-d081-000000000081",
"name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote",
"position": [
660,
860
],
"parameters": {
"color": 7,
"width": 400,
"height": 240,
"content": "### It finds uses, not infringements\nA row means a page is serving your image. It might be licensed, credited, or fair use. Read the sheet as a shortlist to review, not a list of offenders."
},
"typeVersion": 1
}
],
"settings": {
"executionOrder": "v1"
},
"connections": {
"One row per page": {
"main": [
[
{
"node": "Append image uses to sheet",
"type": "main",
"index": 0
}
]
]
},
"Every Monday morning": {
"main": [
[
{
"node": "Set the images to watch",
"type": "main",
"index": 0
}
]
]
},
"One lookup per image": {
"main": [
[
{
"node": "Find pages using the image",
"type": "main",
"index": 0
}
]
]
},
"Set the images to watch": {
"main": [
[
{
"node": "One lookup per image",
"type": "main",
"index": 0
}
]
]
},
"Find pages using the image": {
"main": [
[
{
"node": "Keep rows that are real matches",
"type": "main",
"index": 0
}
]
]
},
"Keep rows that are real matches": {
"main": [
[
{
"node": "One row per page",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
No stock agency monitoring service and no per-image fee. Every week this looks up the images you list and appends one row per page carrying that exact image to Google Sheets: the site, the page title, the page URL and the dimensions of their copy. Runs every Monday morning on a…
Source: https://n8n.io/workflows/17908/ — 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.
No rank tracker subscription and no SERP API key. This template checks where your business ranks in Google's local results for the keywords you choose, every week, and appends one row per listing to G
No Firecrawl API key and no OpenAI key. This template checks every week whether any member of Congress disclosed a trade in the stocks you follow, and appends one row per transaction to Google Sheets:
No Google Business Profile connection and no SerpApi or Bright Data account. This template runs weekly review tracking across the whole Google local pack for your search term, appending one row per bu
No IP watch service and no per-seat subscription. Every week this searches Google Patents for the technologies you follow and appends one row per patent to Google Sheets: title, assignee, inventor, pu
This workflow runs every Monday morning, searches Google Shopping for a list of products using Apify, summarizes credible weekly pricing metrics per product, and appends the results to a Google Sheets