AutomationFlowsSocial Media › Automatically Collect Bug Bounty Tips From Twitter to Google Sheets

Automatically Collect Bug Bounty Tips From Twitter to Google Sheets

ByKunsh @ethicxlhuman on n8n.io

Automatically monitors Twitter for bug bounty tips and educational content every 4 hours, then saves valuable insights to Google Sheets for easy reference and organization. Get your API key from https://twitterapi.io/ (free tier available) Configure Google Sheets credentials in…

Cron / scheduled trigger★★★★☆ complexity8 nodesHTTP RequestGoogle Sheets
Social Media Trigger: Cron / scheduled Nodes: 8 Complexity: ★★★★☆ Added:

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

This workflow follows the Google Sheets → HTTP Request 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": "Hl5YR8MHd1AyDYXk",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "BB Tip Harvestor",
  "tags": [],
  "nodes": [
    {
      "id": "95247308-430c-4f13-bc7d-98beecb5b945",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        320,
        80
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 4
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "6b347ec0-fb37-4ce8-889f-2b29bc1b2e1b",
      "name": "\ud83d\udcd2 Workflow Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -272,
        16
      ],
      "parameters": {
        "color": 4,
        "width": 480,
        "height": 232,
        "content": "## \ud83c\udfaf Bug Bounty Tip Harvester\n\nAutomatically collects bug bounty tips from Twitter every 4 hours and saves to Google Sheets.\n\n**Setup required:**\n1. Get API key from https://twitterapi.io/\n2. Configure Google Sheets credentials\n3. Update Google Sheet ID"
      },
      "typeVersion": 1
    },
    {
      "id": "fa97a28d-cb1c-4e37-8272-3b6efc7f1a44",
      "name": "HTTP Request",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        544,
        80
      ],
      "parameters": {
        "url": "https://api.twitterapi.io/twitter/tweet/advanced_search",
        "options": {},
        "sendQuery": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "queryParameters": {
          "parameters": [
            {
              "name": "query",
              "value": "(#bugbountytips OR #bugbounty OR #bugbountytip) -Writeups -discount -phishing -fuck"
            },
            {
              "name": "queryType",
              "value": "Latest"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "d7ef4868-ef6b-4a57-95f4-b987dbcea50c",
      "name": "\ud83d\udcd2 Twitter API",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        496,
        -256
      ],
      "parameters": {
        "color": 2,
        "width": 348,
        "height": 198,
        "content": "## \ud83d\udd10 Twitter API Setup\n\n1. Sign up at https://twitterapi.io/\n2. Get your API key\n3. Add HTTP Header Auth:\n   - **Header**: `x-api-key`\n   - **Value**: `YOUR_API_KEY`"
      },
      "typeVersion": 1
    },
    {
      "id": "e76166b2-e6e4-4543-aad2-68bc8885d231",
      "name": "Code",
      "type": "n8n-nodes-base.code",
      "position": [
        752,
        80
      ],
      "parameters": {
        "jsCode": "// This approach handles both single tweets and collections\n// It focuses on properly formatting the output for n8n\n\nconsole.log(\"Input item structure:\", JSON.stringify($input.item, null, 2));\n\n// Function to format the date in a more human-readable way\nfunction formatDate(dateString) {\n  if (!dateString) return '';\n\n  try {\n    const date = new Date(dateString);\n    // Format: \"March 13, 2025 at 19:25\"\n    return date.toLocaleString('en-US', {\n      year: 'numeric',\n      month: 'long',\n      day: 'numeric',\n      hour: '2-digit',\n      minute: '2-digit',\n    });\n  } catch (error) {\n    console.log(\"Error formatting date:\", error);\n    return dateString; // Return original if parsing fails\n  }\n}\n\n// Check if this is a Twitter Search result with multiple tweets\nif ($input.item.json.tweets && Array.isArray($input.item.json.tweets)) {\n  const items = $input.item.json.tweets.map(tweet => {\n    return {\n      json: {\n        tweetId: tweet.id || '',\n        url: tweet.url || '',\n        content: tweet.text || '',\n        likeCount: tweet.likeCount || 0,\n        retweetCount: tweet.retweetCount || 0,\n        replyCount: tweet.replyCount || 0,\n        quoteCount: tweet.quoteCount || 0,\n        viewCount: tweet.viewCount || 0,\n        createdAt: formatDate(tweet.createdAt),\n      }\n    };\n  });\n\n  console.log(`Processing ${items.length} tweets`);\n  // Return all items\n  return items;\n} else {\n  // This is a single tweet. Just extract and return its data\n  const tweetData = {\n    tweetId: $input.item.json.id || '',\n    url: $input.item.json.url || '',\n    content: $input.item.json.text || '',\n    likeCount: $input.item.json.likeCount || 0,\n    retweetCount: $input.item.json.retweetCount || 0,\n    replyCount: $input.item.json.replyCount || 0,\n    quoteCount: $input.item.json.quoteCount || 0,\n    viewCount: $input.item.json.viewCount || 0,\n    createdAt: formatDate($input.item.json.createdAt)\n  };\n\n  console.log(\"Processing single tweet\");\n\n  // Return as a single item\n  return {\n    json: tweetData\n  };\n}"
      },
      "typeVersion": 2
    },
    {
      "id": "ca01565b-ded3-42af-832c-0662f0ea2c4d",
      "name": "\ud83d\udcd2 Processing",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        736,
        368
      ],
      "parameters": {
        "color": 3,
        "width": 376,
        "height": 168,
        "content": "## \u2699\ufe0f Data Processing\n\nExtracts tweet data and formats for Google Sheets:\n- Tweet content, URLs, engagement metrics\n- Date formatting for proper sorting\n- Handles both single tweets and collections"
      },
      "typeVersion": 1
    },
    {
      "id": "ed06b8aa-c7ac-4e69-8e19-c2ce422f2bdb",
      "name": "Append or update row in sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        992,
        80
      ],
      "parameters": {
        "columns": {
          "value": {
            "Url": "={{ $json.url }}",
            "Date": "={{ \n  (() => {\n    const raw = $json.createdAt; // e.g. \"July 7, 2025 at 04:43 AM\"\n    const [monthStr, day, yearTime, , time, meridian] = raw.split(/[\\s,]+/);\n    const months = {\n      January: \"01\", February: \"02\", March: \"03\", April: \"04\", May: \"05\", June: \"06\",\n      July: \"07\", August: \"08\", September: \"09\", October: \"10\", November: \"11\", December: \"12\"\n    };\n\n    const [hour, minute] = time.split(\":\").map(Number);\n    let hh = meridian === \"PM\" && hour !== 12 ? hour + 12 : (meridian === \"AM\" && hour === 12 ? 0 : hour);\n    const formattedHour = String(hh).padStart(2, '0');\n\n    return `${yearTime}-${months[monthStr]}-${String(day).padStart(2, '0')} ${formattedHour}:${minute}:00`;\n  })() \n}}",
            "Content": "={{ $json.content }}",
            "TweetID": "={{ $json.tweetId }}",
            "Created At": "={{ $json.createdAt }}"
          },
          "schema": [
            {
              "id": "Content",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Content",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "TweetID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "TweetID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Url",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Url",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Created At",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Created At",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "TweetID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": "Your Google Sheet"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "462cb2c0-136f-454e-9e32-f7fe734c51a9",
      "name": "\ud83d\udcca Google Sheets",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1248,
        -64
      ],
      "parameters": {
        "color": 5,
        "width": 448,
        "height": 198,
        "content": "## \ud83d\udcca Google Sheets Setup\n\n1. Create Google Sheet with columns: Date, Created At, TweetID, Content, Url\n2. Set up Google Sheets OAuth2 credentials\n3. Replace empty documentId with your Sheet ID\n\n**Prevents duplicates using TweetID matching**"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "65c1d539-9a53-41ed-af0a-330c314826b9",
  "connections": {
    "Code": {
      "main": [
        [
          {
            "node": "Append or update row in sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request": {
      "main": [
        [
          {
            "node": "Code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "HTTP Request",
            "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

Automatically monitors Twitter for bug bounty tips and educational content every 4 hours, then saves valuable insights to Google Sheets for easy reference and organization. Get your API key from https://twitterapi.io/ (free tier available) Configure Google Sheets credentials in…

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

More Social Media workflows → · Browse all categories →

Related workflows

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

Social Media

This template is ideal for sales teams, recruiters, business development professionals, and relationship managers who need to monitor changes in their network's LinkedIn profiles. Perfect for agencies

Google Sheets, HTTP Request, Slack
Social Media

Save time - Eliminate manual LinkedIn posting and content scheduling tasks Stay consistent - Automated daily posting keeps your LinkedIn profile active and engaging Keep control - Preview every post b

HTTP Request, Telegram, Google Sheets +2
Social Media

Send Personalized DMs to LinkedIn Profile Visitors. Uses httpRequest, googleSheets. Scheduled trigger; 21 nodes.

HTTP Request, Google Sheets
Social Media

&gt; Set up n8n self-hosted instance using https://tino.vn/vps-n8n?affid=388 &gt; Use the code ==VPSN8N== for up to 39% off.

Google Drive, HTTP Request, Google Sheets
Social Media

&gt; Recommended: Self-hosted via tino.vn/vps-n8n?affid=388 — use code VPSN8N for up to 39% off.

Google Drive, Google Sheets, HTTP Request