This workflow corresponds to n8n.io template #12248 — we link there as the canonical source.
This workflow follows the Google Sheets → Telegram 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 →
{
"nodes": [
{
"id": "16da12c1-464c-4cde-ab5f-2f4c25601cf0",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-1008,
2000
],
"parameters": {
"rule": {
"interval": [
{}
]
}
},
"typeVersion": 1.2
},
{
"id": "7b74a669-ea51-4c33-98fa-4673845a5237",
"name": "Create a session",
"type": "n8n-nodes-base.airtop",
"position": [
-784,
2000
],
"parameters": {
"profileName": "udemy",
"timeoutMinutes": 30,
"additionalFields": {}
},
"typeVersion": 1
},
{
"id": "50dcf645-8983-4c08-88d3-0fe81850016b",
"name": "Create a window",
"type": "n8n-nodes-base.airtop",
"position": [
-560,
2000
],
"parameters": {
"url": "https://www.udemy.com/courses/search/?q=n8n&src=sac",
"resource": "window",
"getLiveView": true,
"additionalFields": {}
},
"typeVersion": 1
},
{
"id": "698d4048-0288-4929-81d6-a0f1b29691df",
"name": "Loop Over Items",
"type": "n8n-nodes-base.splitInBatches",
"position": [
336,
1904
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "34766cef-6108-4506-aee3-149c606de4b9",
"name": "Close a window",
"type": "n8n-nodes-base.airtop",
"position": [
560,
1600
],
"parameters": {
"resource": "window",
"windowId": "={{ $('Create a window').item.json.data.windowId }}",
"operation": "close",
"sessionId": "={{ $('Create a session').item.json.sessionId }}"
},
"typeVersion": 1
},
{
"id": "9d73187e-c02f-4db3-a3e3-00e52b62aff4",
"name": "Terminate a session",
"type": "n8n-nodes-base.airtop",
"position": [
768,
1600
],
"parameters": {
"operation": "terminate",
"sessionId": "={{ $('Create a session').item.json.sessionId }}"
},
"typeVersion": 1
},
{
"id": "7881e7e1-e5ea-457b-b077-10cbfa0be6a3",
"name": "Split course data",
"type": "n8n-nodes-base.splitOut",
"position": [
-112,
2000
],
"parameters": {
"options": {},
"fieldToSplitOut": "output.products"
},
"typeVersion": 1
},
{
"id": "ce3b5661-4f56-492d-86cd-ccad8d2417ca",
"name": "Scrape course",
"type": "n8n-nodes-base.airtop",
"position": [
-336,
2000
],
"parameters": {
"prompt": "title, Current price, Original price, url, instructors name, rating, offerLeftTime(provided offerLeftTime is e.g,20 hours left at this price!)\n\nOriginal price is not available to \"\" blank output provide",
"resource": "extraction",
"operation": "query",
"additionalFields": {
"outputSchema": "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"properties\": {\n \"products\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"title\": { \"type\": \"string\" },\n \"current_price\": { \"type\": \"string\" },\n \"original_price\": { \"type\": \"string\" },\n \"url\": { \"type\": \"string\" },\n \"instructors_name\": { \"type\": \"string\" },\n \"rating\": { \"type\": \"string\" },\n \"offerLeftTime\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\"title\", \"current_price\",\"original_price\", \"url\", \"instructors_name\", \"rating\",\"offerLeftTime\"]\n }\n }\n },\n \"required\": [\"products\"]\n}",
"parseJsonOutput": true
}
},
"typeVersion": 1
},
{
"id": "48caad7c-34e6-4403-83b3-1c980174d131",
"name": "Check offer available or not",
"type": "n8n-nodes-base.if",
"position": [
112,
2000
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "d9b964ec-cb67-4c1f-aba5-0e8e4c5d241c",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $json.original_price }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "8fe3ce14-965d-4377-b68f-b224c300e6b6",
"name": "Find discount",
"type": "n8n-nodes-base.code",
"position": [
560,
1920
],
"parameters": {
"jsCode": "// Get values from the input item\nconst currentPriceStr = $input.first().json.current_price;\nconst originalPriceStr = $input.first().json.original_price;\n\n// Detect currency symbol (assumes same for both)\nconst currencySymbol = currentPriceStr.trim().charAt(0);\n\n// Clean currency symbols and commas, then parse to float\nconst currentPrice = parseFloat(currentPriceStr.replace(/[^0-9.]/g, '').replace(/,/g, ''));\nconst originalPrice = parseFloat(originalPriceStr.replace(/[^0-9.]/g, '').replace(/,/g, ''));\n\n// Calculate discount\nconst discount = ((originalPrice - currentPrice) / originalPrice) * 100;\nconst discountPercentage = discount.toFixed(2);\n\n// Return output with original currency symbol\nreturn [\n {\n json: {\n current_price: `${currencySymbol}${currentPrice}`,\n original_price: `${currencySymbol}${originalPrice}`,\n discount_percentage: `${discountPercentage}`\n }\n }\n];\n"
},
"typeVersion": 2
},
{
"id": "23b12285-eaf3-425a-9976-461aaa2b0c18",
"name": "Append 50% up disc data",
"type": "n8n-nodes-base.googleSheets",
"position": [
1008,
1904
],
"parameters": {
"columns": {
"value": {
"Url": "={{ $('Loop Over Items').item.json.url }}",
"Title": "={{ $('Loop Over Items').item.json.title }}",
"Rating": "={{ $('Loop Over Items').item.json.rating }}",
"Discount": "={{ $json.discount_percentage }}% OFF",
"Offer left": "={{ $('Loop Over Items').item.json.offerLeftTime }}",
"Current price": "={{ $json.current_price }}",
"Original price": "={{ $json.original_price }}",
"Instructor Name": "={{ $('Loop Over Items').item.json.instructors_name }}"
},
"schema": [
{
"id": "Instructor Name",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Instructor Name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Title",
"type": "string",
"display": true,
"required": false,
"displayName": "Title",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Url",
"type": "string",
"display": true,
"required": false,
"displayName": "Url",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Original price",
"type": "string",
"display": true,
"required": false,
"displayName": "Original price",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Discount",
"type": "string",
"display": true,
"required": false,
"displayName": "Discount",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Current price",
"type": "string",
"display": true,
"required": false,
"displayName": "Current price",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Offer left",
"type": "string",
"display": true,
"required": false,
"displayName": "Offer left",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Rating",
"type": "string",
"display": true,
"required": false,
"displayName": "Rating",
"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/1JQE5aW11UriY-jtybq9LTXqu-Un3khZuNp7SzSjXnVU/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1JQE5aW11UriY-jtybq9LTXqu-Un3khZuNp7SzSjXnVU",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1JQE5aW11UriY-jtybq9LTXqu-Un3khZuNp7SzSjXnVU/edit?usp=drivesdk",
"cachedResultName": "Online Course Price Tracker with Coupon Detection"
}
},
"typeVersion": 4.6
},
{
"id": "8d578a9c-6950-467f-8bf8-2c7a48804dc4",
"name": "Send notify course deal",
"type": "n8n-nodes-base.telegram",
"position": [
1216,
1904
],
"parameters": {
"text": "=\ud83e\udde0 Top Udemy Course Deal\n\n\ud83d\udc68\u200d\ud83c\udfeb InstructorName: {{ $json[\"Instructor Name\"] }}\n\ud83c\udf93 Course: {{ $json.Title }} \n\ud83d\udcb8 OriginalPrice: {{ $('Loop Over Items').item.json.original_price }} | {{ $json.Discount }} | CurrentPrice: {{ $json[\"Current price\"] }} \n\ud83c\udf10 {{ $json.Url }}\n\u2b50 Rating: {{ $json.Rating }}\n\u23f0 {{ $json[\"Offer left\"] }}",
"chatId": "chat_id",
"additionalFields": {
"appendAttribution": false
}
},
"typeVersion": 1.2
},
{
"id": "508d795b-2cf2-4abf-a91e-16cc2a30bc5f",
"name": "Check discount percentage 50%>",
"type": "n8n-nodes-base.if",
"position": [
784,
1920
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "ff84d4e1-b9ac-4896-be90-2e5903d8d8b9",
"operator": {
"type": "number",
"operation": "gte"
},
"leftValue": "={{ $json.discount_percentage }}",
"rightValue": 50
}
]
},
"looseTypeValidation": true
},
"typeVersion": 2.2
},
{
"id": "abd3cac0-21af-43f9-808c-3bbb64c5439d",
"name": "No Operation, do nothing",
"type": "n8n-nodes-base.noOp",
"position": [
336,
2208
],
"parameters": {},
"typeVersion": 1
},
{
"id": "fb1da1a4-adde-4bfb-8edb-62b1f994023b",
"name": "Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1584,
1776
],
"parameters": {
"width": 528,
"height": 528,
"content": "## How it works\nThis workflow monitors Udemy courses for deep discounts (50%+) and alerts you via Telegram.\n\nEvery day, it launches a browser session using Airtop, scrapes course listings from Udemy search results, and extracts pricing data. Each course is checked for active offers\u2014if the original price exists, the workflow calculates the discount percentage. Courses with 50%+ discounts are logged to Google Sheets and sent as formatted Telegram notifications with instructor, rating, and time-limited offer details.\n\n## Setup steps\n1. Connect your Airtop account (for browser automation)\n2. Update the search URL in \"Create a window\" to match your course interests\n3. Connect Google Sheets and select your tracking spreadsheet\n4. Add your Telegram bot token and chat ID\n5. Adjust the schedule trigger frequency (default: daily at midnight)\n\n\n### Sample sheet:\nhttps://docs.google.com/spreadsheets/d/1JQE5aW11UriY-jtybq9LTXqu-Un3khZuNp7SzSjXnVU/edit?gid=0#gid=0\n"
},
"typeVersion": 1
},
{
"id": "28836b1d-6303-4496-8367-ecb5979bfe0e",
"name": "Browser Automation",
"type": "n8n-nodes-base.stickyNote",
"position": [
-848,
1888
],
"parameters": {
"color": 7,
"width": 680,
"height": 308,
"content": "## Browser Automation\nLaunches Airtop session, opens Udemy search page, and scrapes course data using AI extraction."
},
"typeVersion": 1
},
{
"id": "b99e0060-9983-40ed-b3a7-8f438e226596",
"name": "Data Processing",
"type": "n8n-nodes-base.stickyNote",
"position": [
-160,
1792
],
"parameters": {
"color": 7,
"width": 840,
"height": 400,
"content": "## Data Processing\nSplits scraped courses into individual items and filters for those with active discount offers."
},
"typeVersion": 1
},
{
"id": "7140fc2b-4231-47fb-813a-404cfec2f299",
"name": "Discount Detection",
"type": "n8n-nodes-base.stickyNote",
"position": [
688,
1792
],
"parameters": {
"color": 7,
"width": 720,
"height": 404,
"content": "## Discount Detection\nCalculates discount percentage and saves courses with 50%+ discounts to Google Sheets, then sends Telegram alerts."
},
"typeVersion": 1
},
{
"id": "f7da6aca-6ff1-41d2-8cae-ec3ddeff1ead",
"name": "Cleanup",
"type": "n8n-nodes-base.stickyNote",
"position": [
400,
1504
],
"parameters": {
"color": 7,
"width": 620,
"height": 272,
"content": "## Cleanup\nCloses browser window and terminates Airtop session after processing all courses."
},
"typeVersion": 1
}
],
"connections": {
"Find discount": {
"main": [
[
{
"node": "Check discount percentage 50%>",
"type": "main",
"index": 0
}
]
]
},
"Scrape course": {
"main": [
[
{
"node": "Split course data",
"type": "main",
"index": 0
}
]
]
},
"Close a window": {
"main": [
[
{
"node": "Terminate a session",
"type": "main",
"index": 0
}
]
]
},
"Create a window": {
"main": [
[
{
"node": "Scrape course",
"type": "main",
"index": 0
}
]
]
},
"Loop Over Items": {
"main": [
[
{
"node": "Close a window",
"type": "main",
"index": 0
}
],
[
{
"node": "Find discount",
"type": "main",
"index": 0
}
]
]
},
"Create a session": {
"main": [
[
{
"node": "Create a window",
"type": "main",
"index": 0
}
]
]
},
"Schedule Trigger": {
"main": [
[
{
"node": "Create a session",
"type": "main",
"index": 0
}
]
]
},
"Split course data": {
"main": [
[
{
"node": "Check offer available or not",
"type": "main",
"index": 0
}
]
]
},
"Append 50% up disc data": {
"main": [
[
{
"node": "Send notify course deal",
"type": "main",
"index": 0
}
]
]
},
"Send notify course deal": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Check offer available or not": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
],
[
{
"node": "No Operation, do nothing",
"type": "main",
"index": 0
}
]
]
},
"Check discount percentage 50%>": {
"main": [
[
{
"node": "Append 50% up disc data",
"type": "main",
"index": 0
}
],
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Online course prices—especially on platforms like Udemy—change frequently and often include time-limited discounts. Manually checking prices, coupon availability, and offer expiration is tedious and unreliable.
Source: https://n8n.io/workflows/12248/ — 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 workflow continuously monitors the TikTok Ads Library for new creatives from specific advertisers or keyword searches, scrapes them via Apify, logs them into Google Sheets, and sends concise noti
This workflow automates plant care reminders and records using Google Sheets, Telegram, and OpenWeather API.
Apollo Data Enrichment Using Company Id to automatically finds contacts for companies listed in your Google Sheet, enriches each person with emails and phone numbers via Apollo’s API, and writes verif
++Download the google sheet here++ and replace this with the googles sheet node: Google sheet , upload to google sheets and replace in the google sheets node. Scheduled trigger: Runs once a day at 8 A
YT AI News Playlist Creator/AI News Form Updater. Uses googleSheets, httpRequest, splitOut, stickyNote. Scheduled trigger; 23 nodes.