AutomationFlowsAI & RAG › Promo Seeker: Auto-find Promo Codes with Serpapi, Gemini & Telegram

Promo Seeker: Auto-find Promo Codes with Serpapi, Gemini & Telegram

ByKhairul Muhtadin @khmuhtadin on n8n.io

Promo Seeker automatically finds, verifies, and delivers active promo codes to users via Telegram or email using SerpAPI + Gemini (OpenRouter). Saves hours of manual searching and deduplicates results into an n8n Data Table for fast reuse.

Cron / scheduled trigger★★★★☆ complexityAI-powered19 nodesTool Serp ApiAgentOpenRouter ChatTelegram TriggerOutput Parser StructuredData TableForm TriggerGmail
AI & RAG Trigger: Cron / scheduled Nodes: 19 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #9172 — we link there as the canonical source.

This workflow follows the Agent → Datatable 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
{
  "nodes": [
    {
      "id": "05250a10-aa6e-4455-b0e4-596932620af4",
      "name": "SerpAPI",
      "type": "@n8n/n8n-nodes-langchain.toolSerpApi",
      "position": [
        -1216,
        864
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "serpApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "4f6e4b5b-d504-4637-84c1-3bcebcffedd6",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -960,
        -368
      ],
      "parameters": {
        "color": 5,
        "width": 464,
        "height": 976,
        "content": "## How to Get Credentials\n\n### 1. SerpAPI Node\n- Go to [SerpAPI](https://serpapi.com/) and sign up\n- After logging in, find your **API Key** in your dashboard\n- In n8n: **Credentials** > **Create New** > **SerpAPI**\n- Paste your API Key and save\n\n### 2. OpenRouter (Gemini 2.5 Pro) Node\n- Go to [OpenRouter](https://openrouter.ai/) and create an account\n- Navigate to **Keys** section\n- Copy your **API Key**\n- In n8n: **Credentials** > **Create New** > **OpenRouter API**\n- Enter your API Key and save\n\n### 3. Telegram Nodes (Trigger & Send Message)\n- Open Telegram and search for [@BotFather](https://t.me/botfather)\n- Send `/newbot` and follow instructions to create your bot\n- Copy the **Bot Token** provided\n- In n8n: **Credentials** > **Create New** > **Telegram API**\n- Paste your Bot Token and save\n\n### 4. Gmail Node\n- In n8n: **Credentials** > **Create New** > **Gmail OAuth2**\n- Click **Connect my account**\n- Authorize with your Google account\n- Grant necessary permissions\n\n### 5. Data Table Nodes\n- **No credentials needed** - Data Tables are built into n8n\n- Create tables directly in your n8n instance\n\n**Required columns for this workflow:**\n- `platform` (string)\n- `promoCode` (string)\n- `value` (string)\n- `termsConditions` (string)\n- `validUntil` (dateTime)\n\n---\n**Security Note:** Keep all API keys secure and never share them publicly."
      },
      "typeVersion": 1
    },
    {
      "id": "9b6787e7-9f3c-4b00-8af4-ddaf68930cf4",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2208,
        432
      ],
      "parameters": {
        "width": 672,
        "height": 240,
        "content": "## How This Workflow Works (Sticky Note)\n\n- This workflow is designed to help you find valid and working promo codes and vouchers from across the internet.\n- When you send a message, the AI Agent uses search tools (SerpAPI) to look for the latest discounts and vouchers.\n- The AI Agent checks and filters results to show only new and active promos.\n- All results are displayed in the chat for easy access.\n- Just type what kind of promo you want and let the workflow do the rest!"
      },
      "typeVersion": 1
    },
    {
      "id": "baebe25f-d3d7-4f49-9785-a81180032773",
      "name": "Promo Seeker Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -1280,
        640
      ],
      "parameters": {
        "text": "=platform: {{ $('Platform').item.json.query }}",
        "options": {
          "systemMessage": "=# Voucher & Discount Finder Expert\n\nYou are a specialized assistant focused on finding valid, working vouchers and discount codes across the internet.\n\n## Your Primary Objectives:\n1. Search for current, active discount codes and vouchers\n2. Verify codes are recent and likely to be working (within last 30 days)\n3. Present results clearly with code and discount value upfront\n4. Focus on the most recent and reliable sources\n\n## Search Strategy:\n- Use web_search to find current voucher codes\n- Look for codes posted within the last 30 days from today's date\n- Prioritize reputable coupon sites and official brand sources\n- Cross-reference multiple sources when possible\n- Check for expiration dates\n\n## Response Format:\nWhen you find vouchers, present them as:\n- **CODE**: [the actual voucher code]\n- **VALUE**: [discount amount or percentage]\n- **EXPIRES**: [expiration date if available]\n- **SOURCE**: [where you found it]\n- **VERIFIED**: [date it was posted/verified]\n\n## Important Notes:\n- Current date: {{$now}}\n- Only search for codes from the last 30 days maximum\n- Be direct - provide codes immediately without lengthy explanations\n- If no valid codes are found, clearly state this and suggest alternatives"
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "37983435-354d-4c0c-b6c0-94d27160d563",
      "name": "Gemini 2.5Pro",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        -1344,
        864
      ],
      "parameters": {
        "model": "google/gemini-2.5-pro",
        "options": {}
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "78f91869-bc57-4ce9-9df6-b7b7e959cc69",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -2464,
        592
      ],
      "parameters": {
        "rule": {
          "interval": [
            {}
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "1169ebb4-b2f9-4786-8f97-7228f17420aa",
      "name": "Platform",
      "type": "n8n-nodes-base.set",
      "position": [
        -2016,
        240
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "e7641500-13e1-46ec-8240-0b32e4fec715",
              "name": "query",
              "type": "string",
              "value": "={{ $json.Platform || $json.message?.text || $json.body.platform || '' }}"
            },
            {
              "id": "2813a353-96d0-4a90-906e-8b6340b9d099",
              "name": "receiver",
              "type": "string",
              "value": "={{ $json.chatId || $json.email || $json.body.email || '' }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "28bd7fdd-5810-4742-b953-c068981da584",
      "name": "No Operation, do nothing",
      "type": "n8n-nodes-base.noOp",
      "position": [
        -2240,
        240
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "65c389a3-30fd-45d9-9677-194e2f46f2ac",
      "name": "Telegram Trigger",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        -2464,
        192
      ],
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "2422da6d-98c1-4f1b-92ce-9a6e39df0060",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        -1088,
        864
      ],
      "parameters": {
        "jsonSchemaExample": "{\n  \"platform\": \"platform\",\t\n  \"code1\": \"code1\",\n\t\"value\": \"value\",\n    \"terms\": \"terms and conditions\",\n    \"validUntil\": \"promo code period valid until(date)\"\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "9761d0fa-448a-4f85-b00a-75dcff57b101",
      "name": "Get row(s)",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        -1792,
        240
      ],
      "parameters": {
        "limit": 3,
        "filters": {
          "conditions": [
            {
              "keyName": "platform",
              "keyValue": "={{ $json.query }}"
            }
          ]
        },
        "matchType": "allConditions",
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "taO6FckoITHcml0P",
          "cachedResultUrl": "/projects/HdidsWtKEV9HXZzb/datatables/taO6FckoITHcml0P",
          "cachedResultName": "Kode Promo Valid"
        }
      },
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "6f75fcf8-5d94-4c7f-a51d-4111f6ac6491",
      "name": "Code Exist?",
      "type": "n8n-nodes-base.if",
      "position": [
        -1568,
        240
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "4a12f7e0-f301-4efd-9b06-31489a31b6a3",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $json.platform }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "8383580c-8c71-4f47-a521-5b6f7d89ad68",
      "name": "On form submission",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        -2464,
        0
      ],
      "parameters": {
        "options": {},
        "formTitle": "Promo Seeker",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Platform",
              "placeholder": "nama platformnya",
              "requiredField": true
            },
            {
              "fieldLabel": "email",
              "placeholder": "nanti kodenya bakal dikirim kesini",
              "requiredField": true
            }
          ]
        },
        "formDescription": "temukan promocode disini"
      },
      "typeVersion": 2.3
    },
    {
      "id": "bd7d5a97-15f3-442a-9e73-0dbda8511982",
      "name": "Send a message",
      "type": "n8n-nodes-base.gmail",
      "onError": "continueErrorOutput",
      "position": [
        -1216,
        336
      ],
      "parameters": {
        "sendTo": "={{ $('Webhook').item.json.body.email }}",
        "message": "=<!DOCTYPE html>\n<html lang=\"id\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Kode Promo Spesial</title>\n</head>\n<body style=\"margin: 0; padding: 0; font-family: Arial, sans-serif; background-color: #f4f4f4;\">\n    <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"background-color: #f4f4f4; padding: 20px;\">\n        <tr>\n            <td align=\"center\">\n                <table width=\"600\" cellpadding=\"0\" cellspacing=\"0\" style=\"background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);\">\n                    <!-- Header -->\n                    <tr>\n                        <td style=\"background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 30px; text-align: center; border-radius: 8px 8px 0 0;\">\n                            <h1 style=\"margin: 0; color: #ffffff; font-size: 28px;\">\ud83c\udf89 Kode Promo Buatmu nih!</h1>\n                        </td>\n                    </tr>\n                    \n                    <!-- Content -->\n                    <tr>\n                        <td style=\"padding: 40px 30px;\">\n                            <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n                                <tr>\n                                    <td style=\"padding-bottom: 20px;\">\n                                        <table width=\"100%\" cellpadding=\"10\" cellspacing=\"0\" style=\"background-color: #f8f9fa; border-radius: 6px;\">\n                                            <tr>\n                                                <td style=\"font-weight: bold; color: #333333; width: 40%;\">Platform:</td>\n                                                <td style=\"color: #666666;\">{{ $json.platform }}</td>\n                                            </tr>\n                                            <tr>\n                                                <td style=\"font-weight: bold; color: #333333;\">Kode Promo:</td>\n                                                <td>\n                                                    <span style=\"background-color: #667eea; color: #ffffff; padding: 8px 16px; border-radius: 4px; font-family: 'Courier New', monospace; font-size: 18px; font-weight: bold; display: inline-block;\">{{ $json.promoCode }}</span>\n                                                </td>\n                                            </tr>\n                                            <tr>\n                                                <td style=\"font-weight: bold; color: #333333;\">Value Promo:</td>\n                                                <td style=\"color: #28a745; font-weight: bold; font-size: 18px;\">{{ $json.value }}</td>\n                                            </tr>\n                                            <tr>\n                                                <td style=\"font-weight: bold; color: #333333; vertical-align: top;\">Keterangan:</td>\n                                                <td style=\"color: #666666; line-height: 1.6;\">{{ $json.termsConditions }}</td>\n                                            </tr>\n                                            <tr>\n                                                <td style=\"font-weight: bold; color: #333333;\">Masa Berlaku:</td>\n                                                <td style=\"color: #dc3545; font-weight: bold;\">{{ $json.validUntil.split('T')[0] }}</td>\n                                            </tr>\n                                        </table>\n                                    </td>\n                                </tr>\n                            </table>\n                        </td>\n                    </tr>\n                    \n                    <!-- Footer -->\n                    <tr>\n                        <td style=\"background-color: #f8f9fa; padding: 20px; text-align: center; border-radius: 0 0 8px 8px; border-top: 1px solid #e9ecef;\">\n                            <p style=\"margin: 0; color: #6c757d; font-size: 14px;\">\n                                Just for you by <a href=\"https://khmuhtadin.com\" style=\"color: #667eea; text-decoration: none; font-weight: bold;\">khmuhtadin.com</a>\n                            </p>\n                        </td>\n                    </tr>\n                </table>\n            </td>\n        </tr>\n    </table>\n</body>\n</html>",
        "options": {
          "appendAttribution": false
        },
        "subject": "=Kode Promo {{ $json.platform.toUpperCase() || $json.platform }} Sudah Tersedia"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "429dd741-9c1f-4c85-a07e-250e38620e4a",
      "name": "Upsert row(s)",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        -880,
        640
      ],
      "parameters": {
        "columns": {
          "value": {
            "value": "={{ $json.output.value }}",
            "platform": "={{ $json.output.platform }}",
            "promoCode": "={{ $json.output.code1 }}",
            "validUntil": "={{ $json.output.validUntil }}",
            "termsConditions": "={{ $json.output.terms }}"
          },
          "schema": [
            {
              "id": "platform",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "platform",
              "defaultMatch": false
            },
            {
              "id": "promoCode",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "promoCode",
              "defaultMatch": false
            },
            {
              "id": "value",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "value",
              "defaultMatch": false
            },
            {
              "id": "termsConditions",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "termsConditions",
              "defaultMatch": false
            },
            {
              "id": "validUntil",
              "type": "dateTime",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "validUntil",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "filters": {
          "conditions": [
            {
              "keyName": "promoCode",
              "keyValue": "={{ $json.output.code1 }}"
            }
          ]
        },
        "options": {},
        "operation": "upsert",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "taO6FckoITHcml0P",
          "cachedResultUrl": "/projects/HdidsWtKEV9HXZzb/datatables/taO6FckoITHcml0P",
          "cachedResultName": "Kode Promo Valid"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "5cacc658-19de-4d0d-9e18-012ae7db8ea6",
      "name": "notify telegram",
      "type": "n8n-nodes-base.telegram",
      "onError": "continueErrorOutput",
      "position": [
        -1216,
        144
      ],
      "parameters": {
        "text": "=<b>\ud83c\udf89 Kode Promo Buatmu nih!</b>\n\n<b>Platform:</b> {{ $json.platform }}\n<b>Kode Promo:</b> <code>{{ $json.promoCode }}</code>\n<b>Value Promo:</b> {{ $json.value }}\n<b>Keterangan:</b> {{ $json.termsConditions }}\n<b>Masa Berlaku:</b> {{ $json.validUntil.split('T')[0] }}",
        "chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
        "additionalFields": {
          "parse_mode": "HTML",
          "appendAttribution": false
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "17d33219-8587-406b-9243-6ea8f00a9aef",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -2464,
        384
      ],
      "parameters": {
        "path": "v1/promo-seeker",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "38d077ac-91eb-41ba-b58a-5122df0df77a",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "disabled": true,
      "position": [
        -1216,
        -48
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1.4
    },
    {
      "id": "b1ff0780-cb28-4d52-aae3-ed20ebf2b987",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2208,
        688
      ],
      "parameters": {
        "color": 3,
        "width": 370,
        "height": 100,
        "content": "## \u2615 Appreciate This Workflow?\n\nSupport the creator by sending coffee:\n\n**PayPal:** [paypal.me/khmuhtadin](https://paypal.me/khmuhtadin)\n\nThank you! \ud83d\ude80"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "SerpAPI": {
      "ai_tool": [
        [
          {
            "node": "Promo Seeker Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Webhook": {
      "main": [
        [
          {
            "node": "No Operation, do nothing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Platform": {
      "main": [
        [
          {
            "node": "Get row(s)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get row(s)": {
      "main": [
        [
          {
            "node": "Code Exist?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code Exist?": {
      "main": [
        [
          {
            "node": "notify telegram",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          },
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Promo Seeker Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini 2.5Pro": {
      "ai_languageModel": [
        [
          {
            "node": "Promo Seeker Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "No Operation, do nothing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Telegram Trigger": {
      "main": [
        [
          {
            "node": "No Operation, do nothing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "On form submission": {
      "main": [
        [
          {
            "node": "No Operation, do nothing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Promo Seeker Agent": {
      "main": [
        [
          {
            "node": "Upsert row(s)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "No Operation, do nothing": {
      "main": [
        [
          {
            "node": "Platform",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Promo Seeker Agent",
            "type": "ai_outputParser",
            "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

Promo Seeker automatically finds, verifies, and delivers active promo codes to users via Telegram or email using SerpAPI + Gemini (OpenRouter). Saves hours of manual searching and deduplicates results into an n8n Data Table for fast reuse.

Source: https://n8n.io/workflows/9172/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

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

AI & RAG

This cutting-edge n8n workflow is a comprehensive automation solution designed to streamline various Instagram operations. It combines an intelligent AI chatbot for direct message management, automate

Agent, OpenRouter Chat, Output Parser Structured +4
AI & RAG

This workflow automates end-to-end sustainability lifecycle management for corporate sustainability teams, ESG governance officers, and circular economy programme leads. It addresses the challenge of

Form Trigger, Agent, OpenAI Chat +11
AI & RAG

Pitch Paul. Uses lmChatOpenRouter, telegram, outputParserStructured, supabaseTool. Event-driven trigger; 33 nodes.

OpenRouter Chat, Telegram, Output Parser Structured +10
AI & RAG

This workflow automatically generates stock market insights for selected tickers (e.g. GAZP, SBER, LKOH) using historical data, technical indicators, and an AI model. The results are then sent to Tele

Agent, OpenRouter Chat, Telegram Trigger +5
AI & RAG

Generate & Schedule Social Media Posts with GPT-4 and Telegram Approval Workflow

Telegram, Telegram Trigger, Agent +4