This workflow corresponds to n8n.io template #15172 — 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 →
{
"meta": {
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "3973284d-8c68-42c3-81ee-9de605e47f22",
"name": "Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
-80,
-112
],
"parameters": {
"color": 7,
"width": 400,
"height": 550,
"content": "## Brent Crude Futures Tracker\n\nSilently scrapes live Brent Crude futures prices for the next 10 months, formats them into a clean table, and delivers updates to Telegram.\n\n### How it works\n1. Triggers multiple times a day on weekdays\n2. Scrapes oilprice.com Brent Crude page\n3. Parses 10 forward contracts (prices and changes)\n4. Aggregates data into a single array\n5. Formats and sends an HTML table to Telegram\n\n### Setup\n- [ ] Add your Telegram Bot credentials\n- [ ] Enter your Telegram Chat ID in the `Send Telegram Table` node\n- [ ] Adjust the `Market Hours Trigger` node times if you are not in IST timezone\n\n### How to Use\nSimply wait for the scheduled times, or click 'Test Workflow' to receive a live snapshot of the forward curve instantly."
},
"typeVersion": 1
},
{
"id": "125f8d81-c5c9-4152-a8d9-5b51f8a85445",
"name": "Sec1",
"type": "n8n-nodes-base.stickyNote",
"position": [
384,
48
],
"parameters": {
"width": 412,
"height": 244,
"content": "## 1. Schedule & Fetch\nRuns on schedule and gets raw HTML."
},
"typeVersion": 1
},
{
"id": "6afbdff3-ec6a-4b20-8371-3d0e6e5cb3cc",
"name": "Sec2",
"type": "n8n-nodes-base.stickyNote",
"position": [
816,
48
],
"parameters": {
"width": 420,
"height": 244,
"content": "## 2. Parse & Combine\nExtracts 10 contracts and bundles them."
},
"typeVersion": 1
},
{
"id": "72e21ada-fd5a-44e0-be5f-dac28813893a",
"name": "Sec3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1280,
48
],
"parameters": {
"width": 250,
"height": 244,
"content": "## 3. Notify\nSends HTML formatted table to Telegram."
},
"typeVersion": 1
},
{
"id": "8959692d-f633-40c8-b2f8-799570b5b165",
"name": "Market Hours Trigger1",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
448,
144
],
"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": "9b4bd061-ca85-4279-9163-f165ad1ed7e5",
"name": "Fetch Brent Page1",
"type": "n8n-nodes-base.httpRequest",
"position": [
640,
144
],
"parameters": {
"url": "https://oilprice.com/futures/brent/",
"options": {}
},
"typeVersion": 4.3
},
{
"id": "8a60254d-4ad8-4fa3-8d90-cd4df1b68b94",
"name": "Parse 10 Contracts1",
"type": "n8n-nodes-base.code",
"onError": "continueRegularOutput",
"position": [
896,
144
],
"parameters": {
"jsCode": "// CORRECTED Code - Extracts correct \"Last\" price column\nconst htmlContent = $input.item.json.data || \"\";\n\nif (!htmlContent || typeof htmlContent !== 'string') {\n return [{ json: { error: \"No HTML content\" } }];\n}\n\nconst now = new Date();\nconst scrapeDate = now.toISOString().split('T')[0];\nconst scrapeTime = now.toTimeString().split(' ')[0];\n\nconst symbolsData = [\n { symbol: 'CBH26', month: 'Mar' },\n { symbol: 'CBJ26', month: 'Apr' },\n { symbol: 'CBK26', month: 'May' },\n { symbol: 'CBM26', month: 'Jun' },\n { symbol: 'CBN26', month: 'Jul' },\n { symbol: 'CBQ26', month: 'Aug' },\n { symbol: 'CBU26', month: 'Sep' },\n { symbol: 'CBV26', month: 'Oct' },\n { symbol: 'CBX26', month: 'Nov' },\n { symbol: 'CBZ26', month: 'Dec' }\n];\n\nconst results = [];\n\nfor (const { symbol, month } of symbolsData) {\n const idx = htmlContent.indexOf(symbol);\n \n if (idx > -1) {\n // Get context AFTER the symbol (not before)\n // This ensures we get data from the same row\n const ctx = htmlContent.substring(idx, Math.min(htmlContent.length, idx + 800));\n \n // Look for the \"Last\" column value specifically\n // Pattern: symbol, then month/year, then the FIRST number is Last price\n const priceMatch = ctx.match(/CBH26[\\s\\S]{0,200}?<\\/a>[\\s\\S]{0,200}?<div[^>]*>\\s*([0-9]{2}\\.[0-9]{2})\\s*</);\n \n // Alternative: Use more specific pattern for \"last\" column\n const lastPriceMatch = ctx.match(/<div[^>]*class=\"[^\"]*last[^\"]*\"[^>]*>\\s*([0-9]{2}\\.[0-9]{2})/i);\n \n // Try another pattern - look for first price after contract name\n const firstPriceMatch = ctx.match(/2026[^0-9]*([0-9]{2}\\.[0-9]{2})/);\n \n const price = lastPriceMatch ? lastPriceMatch[1] : \n (priceMatch ? priceMatch[1] : \n (firstPriceMatch ? firstPriceMatch[1] : null));\n \n if (price) {\n // Find change value\n const changeMatch = ctx.match(/([+\\-][0-9]\\.[0-9]{2})/);\n \n // Find time\n const timeMatch = ctx.match(/([0-9]{2}:[0-9]{2})/);\n \n results.push({\n json: {\n symbol: symbol,\n month: month,\n year: \"2026\",\n contract: `${month} 2026`,\n price_usd: parseFloat(price),\n change: changeMatch ? parseFloat(changeMatch[1]) : null,\n website_update_time: timeMatch ? timeMatch[1] : \"\",\n scrape_date: scrapeDate,\n scrape_time: scrapeTime,\n source: \"https://oilprice.com/futures/brent/\",\n commodity: \"Brent Crude Oil Futures\"\n }\n });\n }\n }\n}\n\nif (results.length === 0) {\n return [{\n json: {\n error: \"No contracts extracted\",\n scrape_date: scrapeDate\n }\n }];\n}\n\nreturn results;\n"
},
"typeVersion": 2,
"alwaysOutputData": true
},
{
"id": "2f39141f-73cf-4163-9295-6d7522ed30f5",
"name": "Aggregate Items1",
"type": "n8n-nodes-base.aggregate",
"position": [
1088,
144
],
"parameters": {
"options": {},
"aggregate": "aggregateAllItemData"
},
"typeVersion": 1
},
{
"id": "8467a8ed-7938-46d7-b890-738ac6288fc0",
"name": "Send Telegram Table1",
"type": "n8n-nodes-base.telegram",
"position": [
1360,
144
],
"parameters": {
"text": "=\ud83d\udee2\ufe0f <b>Brent 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' }}\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\n<pre>\nContract 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\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(9);\n const period = `${item.month} '${String(item.year).slice(-2)}`.padEnd(9);\n const price = `$${item.price_usd}`.padEnd(7);\n const change = `${item.change >= 0 ? '+' : ''}$${item.change}`;\n return `${emoji} ${symbol} ${period} ${price} ${change}`;\n}).join('\\n') }}\n</pre>\n\n\ud83d\udcca Range: ${{ Math.min(...$json.data.map(d => d.price_usd)) }} - ${{ Math.max(...$json.data.map(d => d.price_usd)) }}\n\ud83d\udcc8 Avg: {{ ($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 }}\">Source</a>",
"chatId": "123456789",
"additionalFields": {
"parse_mode": "HTML",
"appendAttribution": false
}
},
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.2
}
],
"connections": {
"Aggregate Items1": {
"main": [
[
{
"node": "Send Telegram Table1",
"type": "main",
"index": 0
}
]
]
},
"Fetch Brent Page1": {
"main": [
[
{
"node": "Parse 10 Contracts1",
"type": "main",
"index": 0
}
]
]
},
"Parse 10 Contracts1": {
"main": [
[
{
"node": "Aggregate Items1",
"type": "main",
"index": 0
}
]
]
},
"Market Hours Trigger1": {
"main": [
[
{
"node": "Fetch Brent Page1",
"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
Oil traders, energy analysts, commodity research desks, shipping operations teams, refinery planners, equity investors in oil stocks, macro researchers, and anyone whose day starts with "where is Brent trading?" If you track the international oil benchmark and want hands-free…
Source: https://n8n.io/workflows/15172/ — 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.