AutomationFlowsSlack & Telegram › Track Wti Crude Futures From Oilprice.com and Send Telegram Alerts

Track Wti Crude Futures From Oilprice.com and Send Telegram Alerts

ByRahul Shah @rahulshah111 on n8n.io

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…

Cron / scheduled trigger★★★★☆ complexity9 nodesTelegramHTTP Request
Slack & Telegram Trigger: Cron / scheduled Nodes: 9 Complexity: ★★★★☆ Added:

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 →

Download .json
{
  "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.

Pro

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 →

More Slack & Telegram workflows → · Browse all categories →

Related workflows

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

Slack & Telegram

. Uses googleSheets, telegram, httpRequest, wise. Scheduled trigger; 36 nodes.

Google Sheets, Telegram, HTTP Request +2
Slack & Telegram

GNCA AI News Pipeline. Uses rssFeedRead, httpRequest, telegram, errorTrigger. Scheduled trigger; 31 nodes.

RSS Feed Read, HTTP Request, Telegram +1
Slack & Telegram

GNCA AI News Pipeline. Uses rssFeedRead, httpRequest, telegram, errorTrigger. Scheduled trigger; 29 nodes.

RSS Feed Read, HTTP Request, Telegram +1
Slack & Telegram

This workflow automates plant care reminders and records using Google Sheets, Telegram, and OpenWeather API.

Google Sheets, HTTP Request, Telegram
Slack & Telegram

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

Google Sheets, HTTP Request, Error Trigger +1