This workflow corresponds to n8n.io template #15673 — we link there as the canonical source.
This workflow follows the Google Sheets → HTTP Request 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 →
{
"id": "BZ8KkG0nsep2UnDz",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Delivery Route Optimizer for Carriers",
"tags": [],
"nodes": [
{
"id": "5ba957ea-dff9-456c-8750-86e878e81622",
"name": "When clicking \u2018Execute workflow\u2019",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-560,
176
],
"parameters": {},
"typeVersion": 1
},
{
"id": "fda47def-1eb1-47de-a0ce-cff6687a0097",
"name": "Loop Over Items",
"type": "n8n-nodes-base.splitInBatches",
"position": [
224,
-176
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "74d54af3-5d0a-45aa-a0c7-dfe6490d16d9",
"name": "Wait",
"type": "n8n-nodes-base.wait",
"position": [
1104,
-160
],
"parameters": {
"amount": 10
},
"typeVersion": 1.1
},
{
"id": "ddea4835-1c3c-4b9e-88b9-600033607747",
"name": "Loop Over Items1",
"type": "n8n-nodes-base.splitInBatches",
"position": [
1088,
432
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "d530451b-a2dc-4112-b80b-03cd68caac03",
"name": "Merge",
"type": "n8n-nodes-base.merge",
"position": [
832,
432
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineAll"
},
"typeVersion": 3.2
},
{
"id": "a5a6bb50-a751-443a-97d0-d87d3970b0c7",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-240,
-832
],
"parameters": {
"width": 1248,
"height": 496,
"content": "## Daily Delivery Route Optimizer for Carriers and Logistics companies Using Google Maps API\n\nhis workflow automates the process of converting delivery addresses into geographic coordinates and **optimizes delivery routes for each carrier** using Google Maps.\n\nFinally, the workflow outputs an optimized circular route that starts and ends at the base location.\n\nThis workflow is particularly useful for:\n\n* Logistics companies\n* Courier services\n* Delivery businesses\n* Field service operations\n* Fleet management teams\n\n[Enable these Google Maps API](https://console.developers.google.com/apis/api/mapstools.googleapis.com/overview?project=1063614113399)\n\n### **How it works:**\n\nThis workflow reads today\u2019s deliveries from Google Sheets, geocodes each address with Google Maps Places API, and updates the sheet with latitude, longitude, and processing status. It then groups completed deliveries by carrier and uses Google Routes API with nearest neighbor and 2-opt optimization to generate an efficient circular route starting and ending at the warehouse. The final output is structured JSON containing the optimized stop order for each carrier.\n\n### **Setup steps:**\n\nCreate a Google Sheet with delivery columns, configure Google Sheets OAuth2 credentials in n8n, and add a Google Maps API key with Places API and Routes API enabled. Update the workflow with your sheet `documentId`, correct sheet name, and warehouse start address. Run the workflow manually to geocode new deliveries, update the spreadsheet, and return optimized routes grouped by carrier.\n\n"
},
"typeVersion": 1
},
{
"id": "6abac159-fe75-44c3-8e31-dc6aed810e02",
"name": "Get date delivery",
"type": "n8n-nodes-base.googleSheets",
"position": [
-128,
-176
],
"parameters": {
"options": {},
"filtersUI": {
"values": [
{
"lookupValue": "={{$now.format('dd/LL/yyyy')}}",
"lookupColumn": "DATE DELIVERY"
},
{
"lookupColumn": "LONG"
},
{
"lookupColumn": "LONG"
}
]
},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1iQHlKJOfAI_9OeKaemW21n82IEbMVsvuaQqSLIKw2n8/edit#gid=0",
"cachedResultName": "Foglio1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1iQHlKJOfAI_9OeKaemW21n82IEbMVsvuaQqSLIKw2n8",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1iQHlKJOfAI_9OeKaemW21n82IEbMVsvuaQqSLIKw2n8/edit?usp=drivesdk",
"cachedResultName": "Carrier"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "bebf4178-141b-43d3-807b-258824290318",
"name": "Update Lat and Lng",
"type": "n8n-nodes-base.googleSheets",
"position": [
784,
-160
],
"parameters": {
"columns": {
"value": {
"DONE": "x",
"LANG": "={{ $json.results[0].geometry.location.lat }}",
"LONG": "={{ $json.results[0].geometry.location.lng }}",
"row_number": "={{ $('Loop Over Items').item.json.row_number }}"
},
"schema": [
{
"id": "NAME CARRIER",
"type": "string",
"display": true,
"required": false,
"displayName": "NAME CARRIER",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "DATE DELIVERY",
"type": "string",
"display": true,
"required": false,
"displayName": "DATE DELIVERY",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "ADDRESS",
"type": "string",
"display": true,
"required": false,
"displayName": "ADDRESS",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "LANG",
"type": "string",
"display": true,
"required": false,
"displayName": "LANG",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "LONG",
"type": "string",
"display": true,
"required": false,
"displayName": "LONG",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "DONE",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "DONE",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "row_number",
"type": "number",
"display": true,
"removed": false,
"readOnly": true,
"required": false,
"displayName": "row_number",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"row_number"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {
"cellFormat": "USER_ENTERED"
},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1iQHlKJOfAI_9OeKaemW21n82IEbMVsvuaQqSLIKw2n8/edit#gid=0",
"cachedResultName": "Foglio1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1iQHlKJOfAI_9OeKaemW21n82IEbMVsvuaQqSLIKw2n8",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1iQHlKJOfAI_9OeKaemW21n82IEbMVsvuaQqSLIKw2n8/edit?usp=drivesdk",
"cachedResultName": "Carrier"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "dd027f6a-69b6-4064-b740-46f211300576",
"name": "Get Lat and Lng",
"type": "n8n-nodes-base.httpRequest",
"position": [
496,
-160
],
"parameters": {
"url": "https://maps.googleapis.com/maps/api/place/textsearch/json",
"options": {},
"sendQuery": true,
"authentication": "genericCredentialType",
"genericAuthType": "httpQueryAuth",
"queryParameters": {
"parameters": [
{
"name": "query",
"value": "={{ $json.ADDRESS }}"
}
]
}
},
"credentials": {
"httpQueryAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "b4115495-b650-4fe5-961a-8be4ba970859",
"name": "Start address",
"type": "n8n-nodes-base.set",
"position": [
-128,
288
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "6bbe88bf-286a-48b8-94b7-67356f79952a",
"name": "START ADDRESS",
"type": "string",
"value": "via rotaldo 11 verona"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "9599fdcb-abe3-432d-9d9b-73d6aba502c1",
"name": "Get Lat and Lng of Start address",
"type": "n8n-nodes-base.httpRequest",
"position": [
224,
288
],
"parameters": {
"url": "https://maps.googleapis.com/maps/api/place/textsearch/json",
"options": {},
"sendQuery": true,
"authentication": "genericCredentialType",
"genericAuthType": "httpQueryAuth",
"queryParameters": {
"parameters": [
{
"name": "query",
"value": "={{ $json[\"START ADDRESS\"] }}"
}
]
}
},
"credentials": {
"httpQueryAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "e6b25111-cc04-4a19-84b7-e38999cd33b2",
"name": "Set vars",
"type": "n8n-nodes-base.set",
"position": [
480,
288
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "1af4ed4c-ab2c-42b2-87fb-63370d34bf66",
"name": "START ADDRESS",
"type": "string",
"value": "={{ $json.results[0].formatted_address }}"
},
{
"id": "c4ac0441-6569-4046-80d7-7675b71ab25d",
"name": "START LAT",
"type": "number",
"value": "={{ $json.results[0].geometry.location.lat }}"
},
{
"id": "19cbf770-9210-479a-8f2b-83de67b0e72d",
"name": "START LNG",
"type": "number",
"value": "={{ $json.results[0].geometry.location.lng }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "65806d26-9725-4ebc-9520-4ca36aa0aa11",
"name": "Get addresses",
"type": "n8n-nodes-base.googleSheets",
"position": [
224,
672
],
"parameters": {
"options": {},
"filtersUI": {
"values": [
{
"lookupValue": "={{$now.format('dd/LL/yyyy')}}",
"lookupColumn": "DATE DELIVERY"
},
{
"lookupValue": "x",
"lookupColumn": "DONE"
}
]
},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1iQHlKJOfAI_9OeKaemW21n82IEbMVsvuaQqSLIKw2n8/edit#gid=0",
"cachedResultName": "Foglio1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1iQHlKJOfAI_9OeKaemW21n82IEbMVsvuaQqSLIKw2n8",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1iQHlKJOfAI_9OeKaemW21n82IEbMVsvuaQqSLIKw2n8/edit?usp=drivesdk",
"cachedResultName": "Carrier"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "30f26882-3ca4-4a6f-8dda-4994ed76e0a8",
"name": "Delivery Algorithm",
"type": "n8n-nodes-base.code",
"position": [
1376,
448
],
"parameters": {
"jsCode": "\nconst GOOGLE_API_KEY = \"xxx\";\n\n\nconst distanceCache = {};\n\nasync function drivingDistance(lat1, lng1, lat2, lng2) {\n const key = `${lat1},${lng1}|${lat2},${lng2}`;\n if (distanceCache[key] !== undefined) return distanceCache[key];\n\n try {\n const result = await this.helpers.httpRequest({\n method: \"POST\",\n url: \"https://routes.googleapis.com/directions/v2:computeRoutes\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Goog-Api-Key\": GOOGLE_API_KEY,\n \"X-Goog-FieldMask\": \"routes.distanceMeters\"\n },\n body: {\n origin: {\n location: { latLng: { latitude: lat1, longitude: lng1 } }\n },\n destination: {\n location: { latLng: { latitude: lat2, longitude: lng2 } }\n },\n travelMode: \"DRIVE\",\n routingPreference: \"TRAFFIC_UNAWARE\"\n },\n json: true\n });\n\n if (!result.routes || result.routes.length === 0) throw new Error(\"No route\");\n distanceCache[key] = result.routes[0].distanceMeters;\n return distanceCache[key];\n\n } catch (e) {\n const dLat = lat2 - lat1;\n const dLng = lng2 - lng1;\n const d = Math.sqrt(dLat * dLat + dLng * dLng) * 111000;\n distanceCache[key] = d;\n return d;\n }\n}\n\n\nasync function totalCircularDistance(route) {\n let total = 0;\n for (let i = 0; i < route.length; i++) {\n const a = route[i];\n const b = route[(i + 1) % route.length]; // circolare: ultimo \u2192 primo\n total += await drivingDistance.call(this, a.LANG, a.LONG, b.LANG, b.LONG);\n }\n return total;\n}\n\nconst items = $input.all();\nconst data = items[0].json;\n\nconst start = {\n row_number: 0,\n \"NAME CARRIER\": data[\"NAME CARRIER\"],\n \"DATE DELIVERY\": data.deliveries[0][\"DATE DELIVERY\"],\n ADDRESS: data[\"START ADDRESS\"],\n LANG: data[\"START LAT\"],\n LONG: data[\"START LNG\"]\n};\n\nconst stops = data.deliveries.map(d => ({\n row_number: d.row_number,\n \"NAME CARRIER\": d[\"NAME CARRIER\"],\n \"DATE DELIVERY\": d[\"DATE DELIVERY\"],\n ADDRESS: d.ADDRESS,\n LANG: d.LANG,\n LONG: d.LONG\n}));\n\nlet remaining = [...stops];\nconst route = [];\nlet currentLat = start.LANG;\nlet currentLng = start.LONG;\n\nwhile (remaining.length > 0) {\n let bestIndex = 0;\n let bestCost = Infinity;\n\n const isLastStep = remaining.length === 1;\n\n const costs = await Promise.all(\n remaining.map(async (stop, i) => {\n // Distanza dal punto corrente al candidato\n const toCandidate = await drivingDistance.call(\n this, currentLat, currentLng, stop.LANG, stop.LONG\n );\n\n if (isLastStep) {\n // Ultimo step: considera anche il ritorno alla base\n const toBase = await drivingDistance.call(\n this, stop.LANG, stop.LONG, start.LANG, start.LONG\n );\n return toCandidate + toBase;\n }\n\n return toCandidate;\n })\n );\n\n for (let i = 0; i < costs.length; i++) {\n if (costs[i] < bestCost) {\n bestCost = costs[i];\n bestIndex = i;\n }\n }\n\n const nearest = remaining.splice(bestIndex, 1)[0];\n route.push(nearest);\n currentLat = nearest.LANG;\n currentLng = nearest.LONG;\n}\n\nlet improved = true;\nlet iterations = 0;\nconst MAX_ITERATIONS = 100;\n\nwhile (improved && iterations < MAX_ITERATIONS) {\n improved = false;\n iterations++;\n\n for (let i = 0; i < route.length - 1; i++) {\n for (let j = i + 1; j < route.length; j++) {\n\n const prevI = i === 0 ? start : route[i - 1];\n const nextJ = j === route.length - 1 ? start : route[j + 1];\n\n const currentCost =\n distanceCache[`${prevI.LANG},${prevI.LONG}|${route[i].LANG},${route[i].LONG}`] || 0 +\n distanceCache[`${route[j].LANG},${route[j].LONG}|${nextJ.LANG},${nextJ.LONG}`] || 0;\n\n // Dopo swap: ...\u2192 route[j] \u2192 route[j-1] \u2192...\u2192 route[i] \u2192 route[j+1|base] \u2192...\n const newCost =\n distanceCache[`${prevI.LANG},${prevI.LONG}|${route[j].LANG},${route[j].LONG}`] || 0 +\n distanceCache[`${route[i].LANG},${route[i].LONG}|${nextJ.LANG},${nextJ.LONG}`] || 0;\n\n if (newCost < currentCost) {\n // Inverti il segmento tra i e j\n const segment = route.slice(i, j + 1).reverse();\n route.splice(i, j - i + 1, ...segment);\n improved = true;\n }\n }\n }\n}\n\nconst lastRowNumber = route[route.length - 1].row_number;\n\nconst returnToBase = {\n row_number: lastRowNumber + 1,\n \"NAME CARRIER\": data[\"NAME CARRIER\"],\n \"DATE DELIVERY\": data.deliveries[0][\"DATE DELIVERY\"],\n ADDRESS: data[\"START ADDRESS\"],\n LANG: data[\"START LAT\"],\n LONG: data[\"START LNG\"]\n};\n\nconst ordered = [start, ...route, returnToBase];\n\nreturn ordered.map(stop => ({ json: stop }));"
},
"typeVersion": 2
},
{
"id": "2406c2a0-2557-461d-b15f-e69eb535993b",
"name": "Group delivery",
"type": "n8n-nodes-base.code",
"position": [
480,
672
],
"parameters": {
"jsCode": "const items = $input.all();\n\nconst grouped = {};\n\nfor (const item of items) {\n const carrier = item.json[\"NAME CARRIER\"];\n\n if (!grouped[carrier]) {\n grouped[carrier] = [];\n }\n\n grouped[carrier].push(item.json);\n}\n\n// trasformo in array finale\nconst result = Object.keys(grouped).map(carrier => {\n return {\n json: {\n \"NAME CARRIER\": carrier,\n deliveries: grouped[carrier]\n }\n };\n});\n\nreturn result;"
},
"typeVersion": 2
},
{
"id": "993d2b38-d8a7-4704-9cd4-57b0d2e866bc",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
992,
304
],
"parameters": {
"color": 7,
"width": 592,
"height": 384,
"content": "## STEP 6 - Algorithm routing\nSet GOOGLE_API_KEY and the algorithm Implements a nearest neighbor heuristic + 2-opt optimization"
},
"typeVersion": 1
},
{
"id": "d1040742-ca03-4eb2-bb2b-573a8d4082e9",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-240,
-288
],
"parameters": {
"color": 7,
"width": 336,
"height": 336,
"content": "## STEP 1 - Database\n[Clone this Sheet](https://docs.google.com/spreadsheets/d/1iQHlKJOfAI_9OeKaemW21n82IEbMVsvuaQqSLIKw2n8/edit?usp=sharing) and fill Name carrier, Date delivery and Address columns"
},
"typeVersion": 1
},
{
"id": "c442237c-171d-4639-a8fa-a5bed5165c83",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-240,
192
],
"parameters": {
"color": 7,
"width": 368,
"height": 304,
"content": "## STEP 3 - Start address\n\nSet the START_ADDRESS"
},
"typeVersion": 1
},
{
"id": "2e5cf06d-1f8d-4f8b-aede-dfc11599c1c9",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
128,
-288
],
"parameters": {
"color": 7,
"width": 1152,
"height": 336,
"content": "## STEP 2 - Latitude and Longitude\n\nCalls the Google Maps Places API (textsearch) to get latitude and longitude"
},
"typeVersion": 1
},
{
"id": "bee9462e-66ba-4810-ac17-f94653cc0dd4",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
160,
192
],
"parameters": {
"color": 7,
"width": 528,
"height": 304,
"content": "## STEP 4 - Latitude and Longitude\nCalls the Google Maps Places API (textsearch) to get latitude and longitude"
},
"typeVersion": 1
},
{
"id": "ccea0d37-06ef-4b1f-a43d-a7fb34b2cebc",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
160,
528
],
"parameters": {
"color": 7,
"width": 528,
"height": 320,
"content": "## STEP 5 - Group by Name Carrier\n\nThese are fully geocoded deliveries ready for routing and Groups all delivery rows by NAME CARRIER"
},
"typeVersion": 1
},
{
"id": "ff7fa13c-f969-4da9-b209-15136d1fbe03",
"name": "Group by Carrier",
"type": "n8n-nodes-base.code",
"position": [
1376,
128
],
"parameters": {
"jsCode": "const items = $input.all();\n\nconst grouped = {};\n\nfor (const item of items) {\n const carrier = item.json[\"NAME CARRIER\"];\n\n if (!grouped[carrier]) {\n grouped[carrier] = [];\n }\n\n grouped[carrier].push(item.json);\n}\n\nreturn Object.entries(grouped).map(([carrier, data]) => {\n return {\n json: {\n carrier,\n deliveries: data\n }\n };\n});"
},
"typeVersion": 2
},
{
"id": "c5a56f3f-e696-408e-8172-66d35f68695c",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-576,
-16
],
"parameters": {
"rule": {
"interval": [
{}
]
}
},
"typeVersion": 1.3
},
{
"id": "2882effe-afab-413c-86a3-4cbdb874f11f",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
1040,
-1200
],
"parameters": {
"width": 896,
"height": 864,
"content": "## Workflow output\nThe workflow returns the optimized delivery routes as structured JSON data, making the result highly flexible and easy to integrate into external systems. The JSON can be sent via webhook to a courier management system, logistics platform, ERP, CRM, or any custom application that needs delivery route data.\nBecause the output is fully structured, it can also be reused for analytics, automation, reporting, or real-time operational dashboards.\nFor example, I created a custom web application that accepts the generated JSON and automatically displays all optimized routes directly on Google Maps, allowing dispatchers and drivers to visualize delivery paths.\n\n"
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "0c941dbe-0f50-4783-96f3-4cf4bda6dcfc",
"connections": {
"Wait": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Merge": {
"main": [
[
{
"node": "Loop Over Items1",
"type": "main",
"index": 0
}
]
]
},
"Set vars": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"Get addresses": {
"main": [
[
{
"node": "Group delivery",
"type": "main",
"index": 0
}
]
]
},
"Start address": {
"main": [
[
{
"node": "Get addresses",
"type": "main",
"index": 0
},
{
"node": "Get Lat and Lng of Start address",
"type": "main",
"index": 0
}
]
]
},
"Group delivery": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
},
"Get Lat and Lng": {
"main": [
[
{
"node": "Update Lat and Lng",
"type": "main",
"index": 0
}
]
]
},
"Loop Over Items": {
"main": [
[],
[
{
"node": "Get Lat and Lng",
"type": "main",
"index": 0
}
]
]
},
"Group by Carrier": {
"main": [
[]
]
},
"Loop Over Items1": {
"main": [
[
{
"node": "Group by Carrier",
"type": "main",
"index": 0
}
],
[
{
"node": "Delivery Algorithm",
"type": "main",
"index": 0
}
]
]
},
"Schedule Trigger": {
"main": [
[
{
"node": "Get date delivery",
"type": "main",
"index": 0
},
{
"node": "Start address",
"type": "main",
"index": 0
}
]
]
},
"Get date delivery": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Delivery Algorithm": {
"main": [
[
{
"node": "Loop Over Items1",
"type": "main",
"index": 0
}
]
]
},
"Update Lat and Lng": {
"main": [
[
{
"node": "Wait",
"type": "main",
"index": 0
}
]
]
},
"Get Lat and Lng of Start address": {
"main": [
[
{
"node": "Set vars",
"type": "main",
"index": 0
}
]
]
},
"When clicking \u2018Execute workflow\u2019": {
"main": [
[
{
"node": "Start address",
"type": "main",
"index": 0
},
{
"node": "Get date delivery",
"type": "main",
"index": 0
}
]
]
}
}
}
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.
googleSheetsOAuth2ApihttpQueryAuth
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow automates the process of converting delivery addresses into geographic coordinates and optimizes delivery routes for each carrier using Google Maps.
Source: https://n8n.io/workflows/15673/ — 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.
This template is ideal for solo store owners, eCommerce marketers, automation beginners, or anyone using Shopify and Gmail who wants to recover lost revenue without coding.
PCN. Uses googleSheets, httpRequest, @n-octo-n/n8n-nodes-json-database, itemLists. Event-driven trigger; 60 nodes.
The workflow automates the process of gathering extensive keyword data for a "Main Keyword." It starts by reading initial parameters from a Google Sheets template, creates a new dedicated Google Sheet
🔥 March Sale – n8n Community Members Get ideoGener8r for Just $27! (Reg. $47) Use Coupon Code: (Valid until 3/31/2025 for n8n community members)
📄 Documentation: Notion Guide