This workflow corresponds to n8n.io template #15947 — we link there as the canonical source.
This workflow follows the HTTP Request → 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 →
{
"id": "aYee16LC3aPei4Yv",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "West Texas Intermediate Brent Prices Tracker",
"tags": [],
"nodes": [
{
"id": "3fc5dadf-8ca9-4fae-a47b-dd79fcab56c1",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
0,
0
],
"parameters": {
"width": 480,
"height": 608,
"content": "## WTI Brent Crude Commodity Prices Tracker for Traders and Commodity Managers\n\n### How it works\n\n1. The workflow is triggered on a schedule.\n2. It performs an HTTP request to fetch the WTI Brent price.\n3. JavaScript code processes the data dynamically.\n4. The results are aggregated.\n5. An output message is sent via Telegram.\n\n### Setup steps\n\n- [ ] Configure the schedule in 'Schedule Trigger'.\n- [ ] Set the target URL in 'WTI Brent Price'.\n- [ ] Equip necessary credentials for Telegram output in 'Output Message'.\n\n### Customization\n\nThe JavaScript code node can be customized to manipulate or analyze the data in different ways."
},
"typeVersion": 1
},
{
"id": "e18f3760-fb2d-4d09-bca7-d56237e6e814",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
560,
0
],
"parameters": {
"color": 7,
"width": 384,
"height": 304,
"content": "## Trigger and fetch data\n\nInitiates the workflow on schedule and fetches WTI Brent price."
},
"typeVersion": 1
},
{
"id": "d6f283e9-4396-4206-9810-bab4852ecb25",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
976,
0
],
"parameters": {
"color": 7,
"width": 400,
"height": 304,
"content": "## Process and aggregate data\n\nProcesses data using JavaScript and aggregates results."
},
"typeVersion": 1
},
{
"id": "aa8448cd-9617-4a7a-8d28-5ccf29b994a0",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1408,
0
],
"parameters": {
"color": 7,
"height": 304,
"content": "## Send output message\n\nDelivers the aggregated data via a Telegram message."
},
"typeVersion": 1
},
{
"id": "9d056cdc-1924-47b1-8968-b37d671bd1ff",
"name": "Extract WTI Futures Data",
"type": "n8n-nodes-base.code",
"onError": "continueRegularOutput",
"position": [
1024,
128
],
"parameters": {
"jsCode": "// Extracts WTI Crude Oil Futures data dynamically\nconst htmlContent = $input.item.json.data || \"\";\n\nif (!htmlContent || typeof htmlContent !== 'string') {\n return [{ json: { error: \"No HTML content found from the HTTP Request.\" } }];\n}\n\nconst now = new Date();\nconst scrapeDate = now.toISOString().split('T')[0];\nconst scrapeTime = now.toTimeString().split(' ')[0];\n\nconst results = [];\nconst foundSymbols = new Set();\n\n// WTI contracts use the \"CL\" prefix followed by a month code and 2-digit year (e.g., CLN26)\nconst symbolRegex = /CL([FGHJKMNQUVXZ])([0-9]{2})/g;\nlet match;\n\nconst monthMap = {\n 'F': 'Jan', 'G': 'Feb', 'H': 'Mar', 'J': 'Apr', 'K': 'May', 'M': 'Jun',\n 'N': 'Jul', 'Q': 'Aug', 'U': 'Sep', 'V': 'Oct', 'X': 'Nov', 'Z': 'Dec'\n};\n\nwhile ((match = symbolRegex.exec(htmlContent)) !== null) {\n const symbol = match[0];\n \n // Prevent duplicates if the symbol appears multiple times on the page\n if (foundSymbols.has(symbol)) continue;\n foundSymbols.add(symbol);\n\n const monthCode = match[1];\n const yearSuffix = match[2];\n const month = monthMap[monthCode];\n const year = \"20\" + yearSuffix;\n\n const idx = match.index;\n // Get context AFTER the symbol to extract its row data\n const ctx = htmlContent.substring(idx, Math.min(htmlContent.length, idx + 400));\n\n // Extract price: Match the first valid decimal price (e.g., 91.74) following the symbol\n const priceRegex = new RegExp(`${symbol}[\\\\s\\\\S]{0,150}?(?:<td[^>]*>|<div[^>]*>|\\\\s)([0-9]{2,3}\\\\.[0-9]{2})(?:<|\\\\s)`);\n const priceMatch = ctx.match(priceRegex) || ctx.match(/([0-9]{2,3}\\.[0-9]{2})/);\n\n // Extract change: Match the first number with an explicit + or - sign\n const changeMatch = ctx.match(/([+\\-][0-9]+\\.[0-9]{2})/);\n\n // Extract time or date (e.g., 18:05 or 22/05)\n const timeMatch = ctx.match(/([0-2][0-9]:[0-5][0-9]|[0-3][0-9]\\/[0-1][0-9])/);\n\n if (priceMatch) {\n results.push({\n json: {\n symbol: symbol,\n month: month,\n year: year,\n contract: `${month} ${year}`,\n price_usd: parseFloat(priceMatch[1]),\n change: changeMatch ? parseFloat(changeMatch[1]) : 0,\n website_update_time: timeMatch ? timeMatch[1] : \"\",\n scrape_date: scrapeDate,\n scrape_time: scrapeTime,\n source: \"https://oilprice.com/futures/wti/\",\n commodity: \"WTI Crude Oil\"\n }\n });\n }\n}\n\n// Sort chronologically by year, then month\nconst monthOrder = { 'Jan':1, 'Feb':2, 'Mar':3, 'Apr':4, 'May':5, 'Jun':6, 'Jul':7, 'Aug':8, 'Sep':9, 'Oct':10, 'Nov':11, 'Dec':12 };\nresults.sort((a, b) => {\n if (a.json.year !== b.json.year) return parseInt(a.json.year) - parseInt(b.json.year);\n return monthOrder[a.json.month] - monthOrder[b.json.month];\n});\n\n// Limit to the next 15 contracts to ensure the Telegram message fits well on mobile screens\nconst finalResults = results.slice(0, 15);\n\nif (finalResults.length === 0) {\n return [{ json: { error: \"No contracts extracted\", scrape_date: scrapeDate } }];\n}\n\nreturn finalResults;"
},
"typeVersion": 2,
"alwaysOutputData": true
},
{
"id": "d7c9c504-8ee8-42f4-a907-cd34eeacb8c2",
"name": "Aggregate Crude Data",
"type": "n8n-nodes-base.aggregate",
"position": [
1232,
128
],
"parameters": {
"options": {},
"aggregate": "aggregateAllItemData"
},
"typeVersion": 1
},
{
"id": "a22c09b0-7b98-40bf-bcb0-b5af2e17109d",
"name": "When Market Hour Starts",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
608,
128
],
"parameters": {
"rule": {
"interval": [
{
"field": "weeks",
"triggerAtDay": [
1,
2,
3,
4,
5
],
"triggerAtHour": 9
},
{
"field": "weeks",
"triggerAtDay": [
1,
2,
3,
4,
5
],
"triggerAtHour": 11,
"triggerAtMinute": 11
},
{
"field": "weeks",
"triggerAtDay": [
1,
2,
3,
4,
5
],
"triggerAtHour": 13,
"triggerAtMinute": 30
},
{
"field": "weeks",
"triggerAtDay": [
1,
2,
3,
4,
5
],
"triggerAtHour": 16
},
{
"field": "weeks",
"triggerAtDay": [
1,
2,
3,
4,
5
],
"triggerAtHour": 19,
"triggerAtMinute": 15
}
]
}
},
"typeVersion": 1.3
},
{
"id": "1fb11c47-0ebd-42a6-9f3d-edda675f6005",
"name": "Send Telegram Alert",
"type": "n8n-nodes-base.telegram",
"position": [
1456,
128
],
"parameters": {
"text": "==\ud83c\uddfa\ud83c\uddf8 <b>WTI Crude Oil Futures</b>\n\ud83d\udcc5 {{ $json.data[0].scrape_date }} \u2022 {{ $json.data[0].scrape_time }}\n\n{{ $json.data.every(item => item.change >= 0) ? '\u2705 ALL UP' : $json.data.every(item => item.change < 0) ? '\u26a0\ufe0f ALL DOWN' : '\ud83d\udcca MIXED MARKET' }}\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n<pre>\nSymb Period Price Change\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n{{ $json.data.map(item => {\n const emoji = item.change > 0 ? '\ud83d\udfe2' : item.change < 0 ? '\ud83d\udd34' : '\u26aa';\n const symbol = item.symbol.padEnd(6);\n const period = `${item.month}${String(item.year).slice(-2)}`.padEnd(7);\n const price = `$${item.price_usd.toFixed(2)}`.padEnd(9);\n const change = `${item.change >= 0 ? '+' : ''}${item.change.toFixed(2)}`;\n return `${emoji} ${symbol} ${period} ${price} ${change}`;\n}).join('\\n') }}\n</pre>\n\n\ud83d\udcca <b>Range:</b> ${{ Math.min(...$json.data.map(d => d.price_usd)).toFixed(2) }} - ${{ Math.max(...$json.data.map(d => d.price_usd)).toFixed(2) }}\n\ud83d\udcc8 <b>Avg Change:</b> {{ ($json.data.reduce((sum, d) => sum + d.change, 0) / $json.data.length).toFixed(2) >= 0 ? '+' : '' }}${{ ($json.data.reduce((sum, d) => sum + d.change, 0) / $json.data.length).toFixed(2) }}\n\n\ud83d\udd17 <a href=\"{{ $json.data[0].source }}\">Live WTI Source</a>",
"chatId": "123456789",
"additionalFields": {
"parse_mode": "HTML",
"appendAttribution": false
}
},
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.2
},
{
"id": "761e94e5-a17e-4c63-8392-2438f7b749c8",
"name": "Fetch WTI Crude Prices",
"type": "n8n-nodes-base.httpRequest",
"position": [
800,
128
],
"parameters": {
"url": "https://oilprice.com/futures/wti/",
"options": {}
},
"typeVersion": 4.3
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "2195a6b4-1d4b-483e-9615-5b7a26e9adc2",
"connections": {
"Aggregate Crude Data": {
"main": [
[
{
"node": "Send Telegram Alert",
"type": "main",
"index": 0
}
]
]
},
"Fetch WTI Crude Prices": {
"main": [
[
{
"node": "Extract WTI Futures Data",
"type": "main",
"index": 0
}
]
]
},
"When Market Hour Starts": {
"main": [
[
{
"node": "Fetch WTI Crude Prices",
"type": "main",
"index": 0
}
]
]
},
"Extract WTI Futures Data": {
"main": [
[
{
"node": "Aggregate Crude Data",
"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.
telegramApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow runs on a weekday schedule, scrapes WTI crude oil futures prices from Reliable Sources, extracts and formats key contract data with JavaScript, and sends a compact market snapshot to a Telegram chat. Runs on a schedule at specified times on weekdays. Fetches the…
Source: https://n8n.io/workflows/15947/ — 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.
Auto Hunt. Uses httpRequest, googleSheets, rssFeedRead, telegram. Scheduled trigger; 78 nodes.
Auto-Hunt. Uses httpRequest, googleSheets, rssFeedRead, telegram. Scheduled trigger; 78 nodes.
This workflow runs daily at 9 AM, uses Perplexity to compile vaping industry news, optionally captures article screenshots via Browserless, and generates a HeyGen avatar video from a template. It then
. Uses googleSheets, telegram, httpRequest, wise. Scheduled trigger; 36 nodes.
GNCA AI News Pipeline. Uses rssFeedRead, httpRequest, telegram, errorTrigger. Scheduled trigger; 31 nodes.