{
  "id": "kO0kNE2AKKzesj0F",
  "name": "Store_Best_Deals_on_Airbnb",
  "tags": [],
  "nodes": [
    {
      "id": "3be17a72-1d6a-4bea-a36c-708125eef7ef",
      "name": "Run Daily (9 AM)",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        20,
        1020
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "d47e5caa-a861-4dad-9f48-52e816fc7a71",
      "name": "Set Search Criteria",
      "type": "n8n-nodes-base.set",
      "position": [
        240,
        1020
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "9426515e-7232-483f-ae94-09cf500c8dc4",
              "name": "Location",
              "type": "string",
              "value": "Los-Angeles"
            },
            {
              "id": "61bc5a40-687c-4f49-b9dc-ee0f24511b6f",
              "name": "checkin",
              "type": "string",
              "value": "2025-09-15"
            },
            {
              "id": "5bc0153f-90cc-43cc-92b0-64c87dcc4e59",
              "name": "checkout",
              "type": "string",
              "value": "2025-09-20"
            },
            {
              "id": "e6b268f2-c05a-44ed-9303-c0def3abf4fe",
              "name": "adults",
              "type": "number",
              "value": 2
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "80e8b765-0840-47d3-b591-060f82bb97c2",
      "name": "Scrape Airbnb via Bright Data",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        500,
        1020
      ],
      "parameters": {
        "url": "https://api.brightdata.com/request",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "zone",
              "value": "n8n_unblocker"
            },
            {
              "name": "url",
              "value": "=https://www.airbnb.com/s/{{ $json.Location }}--CA--United-States/homes?checkin={{ $json.checkin }}&checkout={{ $json.checkout }}&adults={{ $json.adults }}"
            },
            {
              "name": "country",
              "value": "us"
            },
            {
              "name": "format",
              "value": "raw"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer YOUR_TOKEN_HERE KEY"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "59da6712-9264-4622-a2ca-9791a892d336",
      "name": "Extract Price & Title",
      "type": "n8n-nodes-base.code",
      "position": [
        720,
        1020
      ],
      "parameters": {
        "jsCode": "// Get the HTML content from the previous node.\n// Assuming your HTTP Request node's output is named 'data'.\nconst html = items[0].json.data;\n\n// This regex finds the specific <script> tag containing the search results.\nconst regex = /<script id=\"data-deferred-state-0\"[^>]*>([\\s\\S]*?)<\\/script>/;\nconst match = html.match(regex);\n\nif (!match || !match[1]) {\n  console.log(\"Could not find the data script tag.\");\n  return [{\n    error: \"Data script tag not found. The page structure might have changed.\"\n  }];\n}\n\ntry {\n  // Parse the JSON string found inside the script tag.\n  const jsonData = JSON.parse(match[1]);\n\n  // Navigate through the complex object to find the search results array.\n  // Using optional chaining (?.) prevents errors if a key doesn't exist.\n  const searchResults = jsonData?.niobeMinimalClientData?.[0]?.[1]?.data?.presentation?.staysSearch?.results?.searchResults;\n\n  if (!searchResults || !Array.isArray(searchResults)) {\n    console.log(\"Could not find search results in the JSON data.\");\n    return [{\n      error: \"Search results not found in the JSON data. The data structure may have changed.\"\n    }];\n  }\n\n  // Map over the results to extract the title and price for each listing.\n  const listings = searchResults.map(item => {\n    const title = item.title;\n    \n    // The price can be in 'discountedPrice' or 'price', so we check for both.\n    const primaryLine = item.structuredDisplayPrice?.primaryLine;\n    let price = \"N/A\";\n    if (primaryLine) {\n      const priceValue = primaryLine.discountedPrice || primaryLine.price;\n      const qualifier = primaryLine.qualifier || '';\n      price = `${priceValue} ${qualifier}`.trim();\n    }\n\n    return {\n      title,\n      price,\n    };\n  });\n\n  // n8n expects an array of objects to be returned.\n  // Each object will become a separate item for the next node.\n  return listings;\n\n} catch (e) {\n  console.error(\"Error parsing JSON data:\", e);\n  return [{\n    error: \"Failed to parse JSON data from the script tag.\"\n  }];\n}"
      },
      "typeVersion": 2
    },
    {
      "id": "4710e13c-3ce5-4980-bfbf-727b4a425ae3",
      "name": "Save to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1000,
        1020
      ],
      "parameters": {
        "columns": {
          "value": {
            "Price": "={{ $json.price }}",
            "Title": "={{ $json.title }}",
            "Adults": "={{ $('Set Search Criteria').item.json.adults }}",
            "CheckIn": "={{ $('Set Search Criteria').item.json.checkin }}",
            "CheckOut": "={{ $('Set Search Criteria').item.json.checkout }}",
            "Location": "={{ $('Set Search Criteria').item.json.Location }}"
          },
          "schema": [
            {
              "id": "Location",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Location",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "CheckIn",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "CheckIn",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "CheckOut",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "CheckOut",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Adults",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Adults",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Price",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Price",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Title",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nwNkfh8n1qr6ft5mF3PhhDTTaDXb1K_4H3gd3D-nADc/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1nwNkfh8n1qr6ft5mF3PhhDTTaDXb1K_4H3gd3D-nADc",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nwNkfh8n1qr6ft5mF3PhhDTTaDXb1K_4H3gd3D-nADc/edit?usp=drivesdk",
          "cachedResultName": "Rooms on Airbnb"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "7e0664d1-40ce-4498-b412-6ebc60fc9a7f",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        0
      ],
      "parameters": {
        "color": 6,
        "width": 380,
        "height": 1180,
        "content": "## \ud83d\udfe2 **Section 1: \ud83e\udded Define & Trigger the Search**\n\n### \ud83e\udde9 Nodes:\n\n1. \u23f0 **Run Daily**\n2. \ud83d\udee0 **Set Search Criteria**\n\n---\n\n### \u2728 What's Happening Here:\n\n#### \u23f0 **Run Daily**\n\n* This node is your **alarm clock** \u23f0.\n* It automatically **starts the workflow** based on a schedule (e.g., daily at 8:00 AM).\n* You never need to click \"Execute Workflow\" manually again \u2014 it's 100% hands-free.\n\n> *\u201cImagine this as your personal assistant waking up every day to go find vacation deals for you.\u201d*\n\n#### \ud83d\udee0 **Set Search Criteria**\n\n* This is where you **set your filters**: destination, travel dates, number of guests.\n* For example:\n\n  ```json\n  {\n    \"location\": \"Los Angeles\",\n    \"checkin\": \"2025-09-15\",\n    \"checkout\": \"2025-09-20\",\n    \"adults\": 2\n  }\n  ```\n* This data will be **used in the scraping URL** sent to Airbnb through Bright Data.\n\n> *\u201cIt\u2019s like telling your assistant: \u2018Look for 2-person stays in LA from Sept 15\u201320.\u2019\u201d*\n\n---\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "b3904951-145f-44dd-947d-d99194798798",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        420,
        60
      ],
      "parameters": {
        "color": 3,
        "width": 440,
        "height": 1120,
        "content": "## \ud83c\udf10 **Section 2: \ud83d\ude80 Scrape & Extract Airbnb Data**\n\n### \ud83e\udde9 Nodes:\n\n3. \ud83c\udf10 **Scrape Airbnb via Bright Data**\n4. \ud83e\udde0 **Extract Price & Title**\n\n---\n\n### \u2728 What's Happening Here:\n\n#### \ud83c\udf10 **Scrape Airbnb via Bright Data**\n\n* This node **sends a request** to Airbnb using **Bright Data Web Unlocker**.\n* Bright Data acts like a **stealth browser** \ud83d\udd75\ufe0f that avoids blocks and loads the real webpage for you.\n* It fetches all the page data (even if Airbnb tries to hide it from bots).\n\n> *\u201cImagine Bright Data as your super-sneaky browser that can go where others can't.\u201d*\n\n#### \ud83e\udde0 **Extract Price & Title**\n\n* The raw response from Airbnb (HTML or JSON) is **parsed** in this Code node.\n* The code looks for:\n\n  * \ud83c\udff7\ufe0f **Listing Title**\n  * \ud83d\udcb0 **Price per night**\n* These are pulled out from the messy page code and transformed into clean data items.\n\n> *\u201cIt\u2019s like using a fine-tooth comb to extract the useful info from a messy travel brochure.\u201d*\n\n---\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "c41d599a-6211-4d0b-a392-212d2f948612",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        900,
        280
      ],
      "parameters": {
        "color": 5,
        "width": 320,
        "height": 900,
        "content": "## \ud83d\udcc4 **Section 3: \ud83e\uddfe Store the Results**\n\n### \ud83e\udde9 Node:\n\n5. \ud83d\udcc4 **Save to Google Sheets**\n\n---\n\n### \u2728 What's Happening Here:\n\n#### \ud83d\udcc4 **Save to Google Sheets**\n\n* All the extracted data (titles and prices) are **stored row-by-row** in a Google Sheet.\n* Each day adds a new set of listings, creating a full **price history** over time.\n* You can use this sheet to:\n\n  * Track **price changes**\n  * Find **off-peak bargains**\n  * Compare cities or time periods\n\n> *\u201cThis is your digital notebook where every deal is neatly logged for review, comparison, or alerts.\u201d*\n\n---\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "6d833041-f1a3-40e4-a192-78aa903cb9e7",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1880,
        20
      ],
      "parameters": {
        "color": 4,
        "width": 1300,
        "height": 320,
        "content": "=======================================\n            WORKFLOW ASSISTANCE\n=======================================\nFor any questions or support, please contact:\n    Yaron@nofluff.online\n\nExplore more tips and tutorials here:\n   - YouTube: https://www.youtube.com/@YaronBeen/videos\n   - LinkedIn: https://www.linkedin.com/in/yaronbeen/\n=======================================\n"
      },
      "typeVersion": 1
    },
    {
      "id": "1adeb274-de15-4f85-b198-5a235ae934a5",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1880,
        360
      ],
      "parameters": {
        "color": 4,
        "width": 1289,
        "height": 2738,
        "content": "# \ud83c\udf1f Automation Overview: \u201cFind Best Airbnb Deals (Daily Tracker)\u201d\n\nThis n8n workflow automatically scrapes Airbnb listings every day and records the **title and price** of each rental into **Google Sheets**, helping you track the best travel deals over time \u2014 perfect for deal hunters, travel bloggers, or market researchers!\n\n---\n\n## \ud83d\udfe2 **Section 1: \ud83e\udded Define & Trigger the Search**\n\n### \ud83e\udde9 Nodes:\n\n1. \u23f0 **Run Daily**\n2. \ud83d\udee0 **Set Search Criteria**\n\n---\n\n### \u2728 What's Happening Here:\n\n#### \u23f0 **Run Daily**\n\n* This node is your **alarm clock** \u23f0.\n* It automatically **starts the workflow** based on a schedule (e.g., daily at 8:00 AM).\n* You never need to click \"Execute Workflow\" manually again \u2014 it's 100% hands-free.\n\n> *\u201cImagine this as your personal assistant waking up every day to go find vacation deals for you.\u201d*\n\n#### \ud83d\udee0 **Set Search Criteria**\n\n* This is where you **set your filters**: destination, travel dates, number of guests.\n* For example:\n\n  ```json\n  {\n    \"location\": \"Los Angeles\",\n    \"checkin\": \"2025-09-15\",\n    \"checkout\": \"2025-09-20\",\n    \"adults\": 2\n  }\n  ```\n* This data will be **used in the scraping URL** sent to Airbnb through Bright Data.\n\n> *\u201cIt\u2019s like telling your assistant: \u2018Look for 2-person stays in LA from Sept 15\u201320.\u2019\u201d*\n\n---\n\n## \ud83c\udf10 **Section 2: \ud83d\ude80 Scrape & Extract Airbnb Data**\n\n### \ud83e\udde9 Nodes:\n\n3. \ud83c\udf10 **Scrape Airbnb via Bright Data**\n4. \ud83e\udde0 **Extract Price & Title**\n\n---\n\n### \u2728 What's Happening Here:\n\n#### \ud83c\udf10 **Scrape Airbnb via Bright Data**\n\n* This node **sends a request** to Airbnb using **Bright Data Web Unlocker**.\n* Bright Data acts like a **stealth browser** \ud83d\udd75\ufe0f that avoids blocks and loads the real webpage for you.\n* It fetches all the page data (even if Airbnb tries to hide it from bots).\n\n> *\u201cImagine Bright Data as your super-sneaky browser that can go where others can't.\u201d*\n\n#### \ud83e\udde0 **Extract Price & Title**\n\n* The raw response from Airbnb (HTML or JSON) is **parsed** in this Code node.\n* The code looks for:\n\n  * \ud83c\udff7\ufe0f **Listing Title**\n  * \ud83d\udcb0 **Price per night**\n* These are pulled out from the messy page code and transformed into clean data items.\n\n> *\u201cIt\u2019s like using a fine-tooth comb to extract the useful info from a messy travel brochure.\u201d*\n\n---\n\n## \ud83d\udcc4 **Section 3: \ud83e\uddfe Store the Results**\n\n### \ud83e\udde9 Node:\n\n5. \ud83d\udcc4 **Save to Google Sheets**\n\n---\n\n### \u2728 What's Happening Here:\n\n#### \ud83d\udcc4 **Save to Google Sheets**\n\n* All the extracted data (titles and prices) are **stored row-by-row** in a Google Sheet.\n* Each day adds a new set of listings, creating a full **price history** over time.\n* You can use this sheet to:\n\n  * Track **price changes**\n  * Find **off-peak bargains**\n  * Compare cities or time periods\n\n> *\u201cThis is your digital notebook where every deal is neatly logged for review, comparison, or alerts.\u201d*\n\n---\n\n## \ud83c\udfaf Why This Workflow is Powerful (Even for Beginners):\n\n| Benefit \ud83d\udca1                   | Description                                                               |\n| ---------------------------- | ------------------------------------------------------------------------- |\n| \ud83d\udcc6 **Fully Automated**       | No manual work \u2014 set it once and let it run every day.                    |\n| \ud83d\udcca **Easy Tracking**         | Automatically builds a database of deals over time.                       |\n| \ud83d\udcbb **Low-Code & Expandable** | You can easily add filters, alerts (e.g., Telegram, email), or analytics. |\n| \ud83e\uddf0 **Bright Data-Powered**   | Avoids bot detection and ensures clean, reliable data.                    |\n\n---\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "d309ee77-594c-40ad-b943-2ea268b0b47f",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1300,
        280
      ],
      "parameters": {
        "color": 7,
        "width": 380,
        "height": 240,
        "content": "## I\u2019ll receive a tiny commission if you join Bright Data through this link\u2014thanks for fueling more free content!\n\n### https://get.brightdata.com/1tndi4600b25"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "4a456006-c946-40d6-8cd8-55db7f2518a6",
  "connections": {
    "Run Daily (9 AM)": {
      "main": [
        [
          {
            "node": "Set Search Criteria",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Search Criteria": {
      "main": [
        [
          {
            "node": "Scrape Airbnb via Bright Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Price & Title": {
      "main": [
        [
          {
            "node": "Save to Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape Airbnb via Bright Data": {
      "main": [
        [
          {
            "node": "Extract Price & Title",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}