AutomationFlowsData & Sheets › Automate Hotel Price Comparison with Multi-platform Scraping and Email Reporting

Automate Hotel Price Comparison with Multi-platform Scraping and Email Reporting

ByOneclick AI Squad @oneclick-ai on n8n.io

This is a production-ready, end-to-end workflow that automatically compares hotel prices across multiple booking platforms and delivers beautiful email reports to users. Unlike basic building blocks, this workflow is a complete solution ready to deploy. Input: Natural language…

Webhook trigger★★★★☆ complexity19 nodesHTTP RequestEmail SendGoogle Sheets
Data & Sheets Trigger: Webhook Nodes: 19 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #9777 — we link there as the canonical source.

This workflow follows the Emailsend → 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
{
  "id": "m3rGdoXJdGFTSnT6",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Automate Hotel Price Comparison with Multi-Platform Scraping and Email Reporting",
  "tags": [],
  "nodes": [
    {
      "id": "fa0570b5-623e-4c19-b6de-f47b5e5fd9ac",
      "name": "Webhook - Receive Request",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -944,
        -128
      ],
      "parameters": {
        "path": "hotel-price-check",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 1.1
    },
    {
      "id": "cdac9db5-4b10-45e6-940f-4a98ecbe8c4a",
      "name": "Parse & Validate Request",
      "type": "n8n-nodes-base.code",
      "position": [
        -720,
        -128
      ],
      "parameters": {
        "jsCode": "// Enhanced NLP Parser for Hotel Requests\nconst input = $input.first().json.body;\nconst query = input.message || input.query || input.text || '';\nconst userEmail = input.email || input.user_email || '';\nconst userName = input.name || input.user_name || 'Guest';\n\n// Greeting handler\nconst greetings = ['hi', 'hello', 'hey', 'start', 'help'];\nif (greetings.some(g => query.toLowerCase().includes(g)) && query.split(' ').length < 5) {\n  return [{\n    json: {\n      status: 'greeting',\n      response: `Hi ${userName}! \ud83d\udc4b I'm your Hotel Price Assistant.\\n\\nJust tell me:\\n\u2705 Hotel name\\n\u2705 City\\n\u2705 Check-in & check-out dates\\n\\nExample: \"Hilton Hotel in Singapore from 15th March to 18th March\"`,\n      userEmail,\n      userName\n    }\n  }];\n}\n\n// Date parsing with multiple formats\nfunction parseDate(text) {\n  const monthMap = {\n    jan: 0, january: 0, feb: 1, february: 1, mar: 2, march: 2,\n    apr: 3, april: 3, may: 4, jun: 5, june: 5,\n    jul: 6, july: 6, aug: 7, august: 7, sep: 8, september: 8,\n    oct: 9, october: 9, nov: 10, november: 10, dec: 11, december: 11\n  };\n\n  // Format: \"15th March\" or \"March 15\"\n  const dateRegex = /(\\d{1,2})(st|nd|rd|th)?\\s+(jan|january|feb|february|mar|march|apr|april|may|jun|june|jul|july|aug|august|sep|september|oct|october|nov|november|dec|december)|(jan|january|feb|february|mar|march|apr|april|may|jun|june|jul|july|aug|august|sep|september|oct|october|nov|november|dec|december)\\s+(\\d{1,2})(st|nd|rd|th)?/gi;\n  \n  const matches = [...text.matchAll(dateRegex)];\n  const dates = [];\n  const currentYear = new Date().getFullYear();\n  \n  matches.forEach(match => {\n    const day = parseInt(match[1] || match[6]);\n    const monthStr = (match[3] || match[4]).toLowerCase();\n    const month = monthMap[monthStr];\n    \n    if (day && month !== undefined) {\n      dates.push(new Date(currentYear, month, day));\n    }\n  });\n  \n  return dates.sort((a, b) => a - b);\n}\n\nconst dates = parseDate(query);\nlet checkInDate = null;\nlet checkOutDate = null;\n\nif (dates.length >= 2) {\n  checkInDate = dates[0];\n  checkOutDate = dates[1];\n} else if (dates.length === 1) {\n  checkInDate = dates[0];\n  // Default 3-night stay\n  checkOutDate = new Date(checkInDate);\n  checkOutDate.setDate(checkOutDate.getDate() + 3);\n}\n\n// Extract city\nconst cityPattern = /\\b(in|at|near)\\s+([a-z\\s]{3,20})(?=\\s+from|\\s+check|,|$)/i;\nconst cityMatch = query.match(cityPattern);\nlet city = cityMatch ? cityMatch[2].trim() : '';\n\n// Common cities fallback\nconst commonCities = ['singapore', 'mumbai', 'delhi', 'bangalore', 'goa', 'london', 'paris', 'dubai', 'new york', 'tokyo', 'bangkok'];\ncity = city || commonCities.find(c => query.toLowerCase().includes(c)) || '';\n\n// Extract hotel name\nlet hotelName = query\n  .replace(/from\\s+\\d/gi, '')\n  .replace(/to\\s+\\d/gi, '')\n  .replace(/check[- ]?in/gi, '')\n  .replace(/check[- ]?out/gi, '')\n  .replace(/\\d{1,2}(st|nd|rd|th)?\\s+\\w+/gi, '')\n  .replace(new RegExp(`\\\\b(in|at|near)\\\\s+${city}\\\\b`, 'gi'), '')\n  .replace(/hotel|room|price|want|need|know|please|for|dates?/gi, '')\n  .trim();\n\n// Clean hotel name\nhotelName = hotelName.split(/[,.]/).filter(p => p.trim().length > 2)[0]?.trim() || '';\n\n// Validation\nconst errors = [];\nif (!hotelName || hotelName.length < 3) errors.push('hotel name');\nif (!city) errors.push('city');\nif (!checkInDate) errors.push('check-in date');\nif (!checkOutDate) errors.push('check-out date');\n\nif (errors.length > 0) {\n  return [{\n    json: {\n      status: 'missing_info',\n      response: `I need more information. Please provide: ${errors.join(', ')}.\\n\\nExample: \"Marriott Hotel in Dubai from 20th December to 23rd December\"`,\n      userEmail,\n      userName\n    }\n  }];\n}\n\n// Format dates\nconst formatDate = (date) => {\n  const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];\n  return `${date.getDate()} ${months[date.getMonth()]} ${date.getFullYear()}`;\n};\n\nconst nights = Math.ceil((checkOutDate - checkInDate) / (1000 * 60 * 60 * 24));\n\nreturn [{\n  json: {\n    status: 'ready',\n    hotelName: hotelName.split(' ').map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(' '),\n    city: city.split(' ').map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(' '),\n    checkIn: formatDate(checkInDate),\n    checkOut: formatDate(checkOutDate),\n    checkInISO: checkInDate.toISOString().split('T')[0],\n    checkOutISO: checkOutDate.toISOString().split('T')[0],\n    nights,\n    userEmail,\n    userName,\n    originalQuery: query\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "e6bc8693-f361-4e71-bc5b-3fee26331176",
      "name": "Check If Ready",
      "type": "n8n-nodes-base.if",
      "position": [
        -496,
        -128
      ],
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{$json.status}}",
              "value2": "ready"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "3bfffcf2-29fc-4a34-9aef-d88bebc1291f",
      "name": "Scrape Booking.com",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -272,
        -416
      ],
      "parameters": {
        "url": "https://api.example.com/scrape/booking",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "hotel",
              "value": "={{$json.hotelName}}"
            },
            {
              "name": "city",
              "value": "={{$json.city}}"
            },
            {
              "name": "checkIn",
              "value": "={{$json.checkInISO}}"
            },
            {
              "name": "checkOut",
              "value": "={{$json.checkOutISO}}"
            },
            {
              "name": "platform",
              "value": "booking"
            }
          ]
        }
      },
      "typeVersion": 4.1,
      "continueOnFail": true
    },
    {
      "id": "1fbf0962-d1e2-4b18-9b5f-8ddbbbec6060",
      "name": "Scrape Agoda",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -272,
        -224
      ],
      "parameters": {
        "url": "https://api.example.com/scrape/agoda",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "hotel",
              "value": "={{$json.hotelName}}"
            },
            {
              "name": "city",
              "value": "={{$json.city}}"
            },
            {
              "name": "checkIn",
              "value": "={{$json.checkInISO}}"
            },
            {
              "name": "checkOut",
              "value": "={{$json.checkOutISO}}"
            },
            {
              "name": "platform",
              "value": "agoda"
            }
          ]
        }
      },
      "typeVersion": 4.1,
      "continueOnFail": true
    },
    {
      "id": "9044675b-7dbd-43b1-9dd7-2adc3f176d48",
      "name": "Scrape Expedia",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -272,
        -32
      ],
      "parameters": {
        "url": "https://api.example.com/scrape/expedia",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "hotel",
              "value": "={{$json.hotelName}}"
            },
            {
              "name": "city",
              "value": "={{$json.city}}"
            },
            {
              "name": "checkIn",
              "value": "={{$json.checkInISO}}"
            },
            {
              "name": "checkOut",
              "value": "={{$json.checkOutISO}}"
            },
            {
              "name": "platform",
              "value": "expedia"
            }
          ]
        }
      },
      "typeVersion": 4.1,
      "continueOnFail": true
    },
    {
      "id": "401b0e56-d028-4345-8b4a-0920e6a678f5",
      "name": "Aggregate & Compare",
      "type": "n8n-nodes-base.code",
      "position": [
        -48,
        -224
      ],
      "parameters": {
        "jsCode": "// Aggregate and compare prices from all platforms\nconst items = $input.all();\nconst searchData = items[0].json;\n\nconst prices = [];\nconst errors = [];\n\n// Process each platform result\nitems.slice(1).forEach((item, index) => {\n  const platforms = ['Booking.com', 'Agoda', 'Expedia'];\n  const platform = platforms[index];\n  \n  if (item.json.error) {\n    errors.push(`${platform}: Unable to fetch`);\n  } else if (item.json.price) {\n    prices.push({\n      platform,\n      price: parseFloat(item.json.price),\n      currency: item.json.currency || 'USD',\n      roomType: item.json.roomType || 'Standard Room',\n      url: item.json.url || '#',\n      availability: item.json.availability !== false\n    });\n  }\n});\n\n// Sort by price\nprices.sort((a, b) => a.price - b.price);\n\nif (prices.length === 0) {\n  return [{\n    json: {\n      status: 'no_results',\n      message: 'Unable to find prices. The hotel might not be available for these dates.',\n      errors,\n      ...searchData\n    }\n  }];\n}\n\nconst bestDeal = prices[0];\nconst avgPrice = prices.reduce((sum, p) => sum + p.price, 0) / prices.length;\nconst savings = prices.length > 1 ? prices[prices.length - 1].price - bestDeal.price : 0;\n\nreturn [{\n  json: {\n    status: 'success',\n    ...searchData,\n    results: prices,\n    bestDeal,\n    avgPrice: Math.round(avgPrice),\n    savings: Math.round(savings),\n    totalResults: prices.length,\n    errors\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "0e569c2e-fd37-4bf8-a80e-385dd6580b10",
      "name": "Format Email Report",
      "type": "n8n-nodes-base.code",
      "position": [
        176,
        -224
      ],
      "parameters": {
        "jsCode": "// Format beautiful email with comparison\nconst data = $input.first().json;\n\nif (data.status === 'no_results') {\n  return [{\n    json: {\n      subject: `\u274c No Results - ${data.hotelName}, ${data.city}`,\n      html: `\n        <div style=\"font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;\">\n          <h2>\u274c Unable to Find Prices</h2>\n          <p>We couldn't find available prices for:</p>\n          <div style=\"background: #f5f5f5; padding: 15px; border-radius: 8px;\">\n            <strong>${data.hotelName}</strong><br>\n            \ud83d\udccd ${data.city}<br>\n            \ud83d\udcc5 ${data.checkIn} to ${data.checkOut} (${data.nights} nights)\n          </div>\n          <p style=\"margin-top: 20px;\">Possible reasons:</p>\n          <ul>\n            <li>Hotel not available on these dates</li>\n            <li>Hotel name might be different on booking platforms</li>\n            <li>No rooms available</li>\n          </ul>\n          <p>Please try:</p>\n          <ul>\n            <li>Different dates</li>\n            <li>Checking the exact hotel name</li>\n            <li>Another hotel in ${data.city}</li>\n          </ul>\n        </div>\n      `,\n      ...data\n    }\n  }];\n}\n\nconst { hotelName, city, checkIn, checkOut, nights, results, bestDeal, avgPrice, savings } = data;\n\nconst resultsHtml = results.map((r, i) => `\n  <tr style=\"${i === 0 ? 'background: #e8f5e9;' : ''}\">\n    <td style=\"padding: 12px; border-bottom: 1px solid #ddd;\">\n      ${i === 0 ? '\ud83c\udfc6 ' : ''}<strong>${r.platform}</strong>\n    </td>\n    <td style=\"padding: 12px; border-bottom: 1px solid #ddd;\">${r.roomType}</td>\n    <td style=\"padding: 12px; border-bottom: 1px solid #ddd; text-align: right;\">\n      <strong style=\"color: ${i === 0 ? '#2e7d32' : '#333'}; font-size: 18px;\">\n        ${r.currency} ${r.price.toLocaleString()}\n      </strong>\n    </td>\n    <td style=\"padding: 12px; border-bottom: 1px solid #ddd; text-align: center;\">\n      <a href=\"${r.url}\" style=\"background: #1976d2; color: white; padding: 8px 16px; text-decoration: none; border-radius: 4px; display: inline-block;\">View</a>\n    </td>\n  </tr>\n`).join('');\n\nconst html = `\n<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n</head>\n<body style=\"margin: 0; padding: 20px; background: #f5f5f5; font-family: Arial, sans-serif;\">\n  <div style=\"max-width: 650px; margin: 0 auto; background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1);\">\n    \n    <!-- Header -->\n    <div style=\"background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; text-align: center;\">\n      <h1 style=\"margin: 0; font-size: 28px;\">\ud83c\udfe8 Hotel Price Comparison</h1>\n      <p style=\"margin: 10px 0 0 0; opacity: 0.9;\">Best deals for your stay</p>\n    </div>\n\n    <!-- Hotel Info -->\n    <div style=\"padding: 25px; background: #f8f9fa;\">\n      <h2 style=\"margin: 0 0 15px 0; color: #333;\">${hotelName}</h2>\n      <div style=\"color: #666; line-height: 1.8;\">\n        <div>\ud83d\udccd <strong>Location:</strong> ${city}</div>\n        <div>\ud83d\udcc5 <strong>Check-in:</strong> ${checkIn}</div>\n        <div>\ud83d\udcc5 <strong>Check-out:</strong> ${checkOut}</div>\n        <div>\ud83c\udf19 <strong>Duration:</strong> ${nights} night${nights > 1 ? 's' : ''}</div>\n      </div>\n    </div>\n\n    <!-- Best Deal Highlight -->\n    <div style=\"margin: 20px; padding: 20px; background: linear-gradient(135deg, #667eea15 0%, #764ba215 100%); border-radius: 8px; border-left: 4px solid #667eea;\">\n      <div style=\"font-size: 14px; color: #667eea; font-weight: bold; margin-bottom: 8px;\">\ud83c\udfc6 BEST DEAL FOUND</div>\n      <div style=\"font-size: 24px; font-weight: bold; color: #333; margin-bottom: 5px;\">\n        ${bestDeal.currency} ${bestDeal.price.toLocaleString()}\n      </div>\n      <div style=\"color: #666;\">on ${bestDeal.platform} \u2022 ${bestDeal.roomType}</div>\n      ${savings > 0 ? `<div style=\"margin-top: 10px; color: #2e7d32; font-weight: bold;\">\ud83d\udcb0 Save ${bestDeal.currency} ${savings.toLocaleString()} compared to highest price!</div>` : ''}\n    </div>\n\n    <!-- Price Comparison Table -->\n    <div style=\"padding: 0 20px 20px 20px;\">\n      <h3 style=\"color: #333; margin-bottom: 15px;\">\ud83d\udcca Price Comparison</h3>\n      <table style=\"width: 100%; border-collapse: collapse; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,0.1);\">\n        <thead>\n          <tr style=\"background: #f5f5f5;\">\n            <th style=\"padding: 12px; text-align: left; color: #666; font-weight: 600;\">Platform</th>\n            <th style=\"padding: 12px; text-align: left; color: #666; font-weight: 600;\">Room Type</th>\n            <th style=\"padding: 12px; text-align: right; color: #666; font-weight: 600;\">Price</th>\n            <th style=\"padding: 12px; text-align: center; color: #666; font-weight: 600;\">Action</th>\n          </tr>\n        </thead>\n        <tbody>\n          ${resultsHtml}\n        </tbody>\n      </table>\n      \n      <div style=\"margin-top: 15px; padding: 15px; background: #f8f9fa; border-radius: 8px; text-align: center; color: #666;\">\n        <strong>Average Price:</strong> ${bestDeal.currency} ${avgPrice.toLocaleString()}\n      </div>\n    </div>\n\n    <!-- Footer -->\n    <div style=\"padding: 20px; background: #f8f9fa; border-top: 1px solid #e0e0e0; text-align: center; color: #666; font-size: 13px;\">\n      <p style=\"margin: 0 0 10px 0;\">\u2728 Prices are subject to availability and may change.</p>\n      <p style=\"margin: 0;\">Happy travels! \ud83c\udf0d</p>\n    </div>\n\n  </div>\n</body>\n</html>\n`;\n\nreturn [{\n  json: {\n    subject: `\ud83c\udfe8 ${hotelName} - Best Price: ${bestDeal.currency} ${bestDeal.price.toLocaleString()}`,\n    html,\n    ...data\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "f1d4a0a7-0441-4609-8e35-0db86378de93",
      "name": "Send Email Report",
      "type": "n8n-nodes-base.emailSend",
      "position": [
        400,
        -320
      ],
      "parameters": {
        "options": {},
        "subject": "={{$json.subject}}",
        "toEmail": "={{$json.userEmail}}",
        "fromEmail": "user@example.com"
      },
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "3a3a94aa-23f4-4758-9fb7-bac33b35ecca",
      "name": "Webhook Response (Success)",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        624,
        -320
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ {\\n  \"success\": true,\\n  \"message\": \"Price comparison sent to your email!\",\\n  \"hotel\": $json.hotelName,\\n  \"bestPrice\": $json.bestDeal.price,\\n  \"platform\": $json.bestDeal.platform\\n} }}"
      },
      "typeVersion": 1
    },
    {
      "id": "1a4fd9f4-b08e-4c7e-9ab5-cc9c0e9cdf7f",
      "name": "Webhook Response (Info)",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        848,
        48
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ {\\n  \"success\": false,\\n  \"message\": $json.response,\\n  \"status\": $json.status\\n} }}"
      },
      "typeVersion": 1
    },
    {
      "id": "460b6b8d-679e-4876-9f45-b0b6aa432f8e",
      "name": "Log Analytics",
      "type": "n8n-nodes-base.set",
      "position": [
        400,
        -32
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "={{ {\\n  \"timestamp\": $now,\\n  \"query\": $json.originalQuery,\\n  \"hotel\": $json.hotelName,\\n  \"city\": $json.city,\\n  \"checkIn\": $json.checkIn,\\n  \"checkOut\": $json.checkOut,\\n  \"bestPrice\": $json.bestDeal?.price,\\n  \"platform\": $json.bestDeal?.platform,\\n  \"totalResults\": $json.totalResults,\\n  \"userEmail\": $json.userEmail\\n} }}"
      },
      "typeVersion": 3.2
    },
    {
      "id": "c7984244-c4d7-4343-88d6-422e1f54a06f",
      "name": "Save to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        624,
        -32
      ],
      "parameters": {
        "columns": {
          "mappingMode": "autoMapInputData"
        },
        "options": {},
        "operation": "append",
        "sheetName": "Analytics",
        "documentId": "your-google-sheet-id",
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "0de90b41-12aa-4997-96f9-ee90d6b59ebe",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -960,
        -304
      ],
      "parameters": {
        "width": 150,
        "height": 352,
        "content": "User sends natural language query"
      },
      "typeVersion": 1
    },
    {
      "id": "4cda970d-9585-43e2-b188-f7662e9ecee2",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -768,
        -304
      ],
      "parameters": {
        "color": 4,
        "width": 192,
        "height": 352,
        "content": "Extract hotel, city, dates"
      },
      "typeVersion": 1
    },
    {
      "id": "6ca495c6-d8bd-483d-a79a-8c4b4d8e58ef",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -528,
        -304
      ],
      "parameters": {
        "color": 3,
        "width": 150,
        "height": 352,
        "content": "Validate all required info"
      },
      "typeVersion": 1
    },
    {
      "id": "b120b69f-566c-4c91-af14-8ee7171d0288",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -64,
        -400
      ],
      "parameters": {
        "color": 5,
        "width": 150,
        "height": 352,
        "content": "Compare prices, find best deal"
      },
      "typeVersion": 1
    },
    {
      "id": "6835fed2-3bed-4591-85b3-7e0ea31c3ec0",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        144,
        -400
      ],
      "parameters": {
        "color": 3,
        "width": 150,
        "height": 352,
        "content": "Create beautiful HTML report"
      },
      "typeVersion": 1
    },
    {
      "id": "c661889d-6ab1-48ed-8044-f7616aa53999",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        368,
        -464
      ],
      "parameters": {
        "width": 662,
        "height": 656,
        "content": "Send response"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "7e3f322c-14e9-4659-a3eb-8265b957e295",
  "connections": {
    "Scrape Agoda": {
      "main": [
        [
          {
            "node": "Aggregate & Compare",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Analytics": {
      "main": [
        [
          {
            "node": "Save to Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check If Ready": {
      "main": [
        [
          {
            "node": "Scrape Booking.com",
            "type": "main",
            "index": 0
          },
          {
            "node": "Scrape Agoda",
            "type": "main",
            "index": 0
          },
          {
            "node": "Scrape Expedia",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Webhook Response (Info)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape Expedia": {
      "main": [
        [
          {
            "node": "Aggregate & Compare",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Email Report": {
      "main": [
        [
          {
            "node": "Webhook Response (Success)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape Booking.com": {
      "main": [
        [
          {
            "node": "Aggregate & Compare",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate & Compare": {
      "main": [
        [
          {
            "node": "Format Email Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Email Report": {
      "main": [
        [
          {
            "node": "Send Email Report",
            "type": "main",
            "index": 0
          },
          {
            "node": "Log Analytics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save to Google Sheets": {
      "main": [
        [
          {
            "node": "Webhook Response (Info)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse & Validate Request": {
      "main": [
        [
          {
            "node": "Check If Ready",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook - Receive Request": {
      "main": [
        [
          {
            "node": "Parse & Validate Request",
            "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.

Pro

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

About this workflow

This is a production-ready, end-to-end workflow that automatically compares hotel prices across multiple booking platforms and delivers beautiful email reports to users. Unlike basic building blocks, this workflow is a complete solution ready to deploy. Input: Natural language…

Source: https://n8n.io/workflows/9777/ — original creator credit. Request a take-down →

More Data & Sheets workflows → · Browse all categories →

Related workflows

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

Data & Sheets

Convalidaciones Académicas - Estructura Base. Uses googleSheets, emailSend, googleDrive, httpRequest. Webhook trigger; 35 nodes.

Google Sheets, Email Send, Google Drive +2
Data & Sheets

Are you tired of manually entering open house visitor information into your CRM? Losing hot leads because you didn't follow up fast enough? This powerful n8n workflow automatically syncs every SignSna

Email Send, Google Sheets, HubSpot +3
Data & Sheets

Messenger Responder (FB + IG). Uses httpRequest, googleSheets, emailSend. Webhook trigger; 15 nodes.

HTTP Request, Google Sheets, Email Send
Data & Sheets

WF_MAIN_orchestrator_v4. Uses httpRequest, googleSheets, emailSend. Webhook trigger; 12 nodes.

HTTP Request, Google Sheets, Email Send
Data & Sheets

This workflow automates invoice generation from form submissions, ensuring unique order IDs, creating PDF invoices, storing files, emailing customers, and logging invoice data — all seamlessly integra

Google Sheets, HTTP Request, Google Drive +1