AutomationFlowsEmail & Gmail › Gmail Internship Scanner

Gmail Internship Scanner

Gmail Internship Scanner. Uses gmail, httpRequest, supabase. Scheduled trigger; 10 nodes.

Cron / scheduled trigger★★★★☆ complexity10 nodesGmailHTTP RequestSupabase
Email & Gmail Trigger: Cron / scheduled Nodes: 10 Complexity: ★★★★☆ Added:

This workflow follows the Gmail → 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
{
  "name": "Gmail Internship Scanner",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 48
            }
          ]
        }
      },
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.1,
      "position": [
        200,
        300
      ],
      "id": "schedule-trigger"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "gmail-scan",
        "responseMode": "responseNode"
      },
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1.1,
      "position": [
        200,
        450
      ],
      "id": "webhook-trigger"
    },
    {
      "parameters": {
        "operation": "getAll",
        "returnAll": false,
        "limit": 50,
        "filters": {
          "q": "from:(linkedin.com OR handshake.com OR internship OR hiring OR opportunity OR job alert) newer_than:2d",
          "labelIds": [
            "INBOX"
          ]
        },
        "options": {
          "format": "full",
          "dataPropertyAttachmentsPrefixName": "attachment_"
        }
      },
      "name": "Gmail Get Messages",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        500,
        300
      ],
      "id": "gmail-get",
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Deduplicate and extract email bodies\nconst items = $input.all();\nconst seen = new Set();\nconst result = [];\n\nfor (const item of items) {\n  const msg = item.json;\n  const subject = msg.subject || '';\n  const from = msg.from || '';\n  const body = msg.text || msg.html || '';\n  \n  const key = subject.toLowerCase().trim();\n  if (seen.has(key)) continue;\n  seen.add(key);\n  \n  // Filter relevant emails\n  const relevant = [\n    'internship', 'intern', 'opportunity', 'job', 'hiring', 'role',\n    'engineer', 'researcher', 'analyst', 'quant'\n  ];\n  const text = (subject + ' ' + body).toLowerCase();\n  if (!relevant.some(kw => text.includes(kw))) continue;\n  \n  result.push({\n    subject,\n    from,\n    body: body.substring(0, 3000), // Limit for Claude\n    date: msg.date || new Date().toISOString()\n  });\n}\n\nreturn result.map(r => ({ json: r }));"
      },
      "name": "Deduplicate Emails",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        700,
        300
      ],
      "id": "dedup-emails"
    },
    {
      "parameters": {
        "batchSize": 5,
        "options": {}
      },
      "name": "Batch Emails",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        900,
        300
      ],
      "id": "batch-emails"
    },
    {
      "parameters": {
        "url": "https://api.anthropic.com/v1/messages",
        "method": "POST",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "contentType": "json",
        "body": {
          "model": "claude-haiku-4-5-20251001",
          "max_tokens": 2048,
          "messages": [
            {
              "role": "user",
              "content": "=Extract internship listings from these email subjects/bodies. Return ONLY valid JSON array of objects with these fields: role, company, location, loc_type (remote/hybrid/onsite), wage (number or null), wage_period (month/week or null), paid (boolean), description (max 200 chars), requirements (max 200 chars), skills_required (string array), deadline (YYYY-MM-DD or null), apply_url (string or null), source (LinkedIn/Handshake/Email). Only include actual internship listings, not newsletters. Emails:\n\n{{ $json.subject }}\n\n{{ $json.body }}"
            }
          ]
        }
      },
      "name": "Claude Extract Jobs",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1100,
        300
      ],
      "id": "claude-extract"
    },
    {
      "parameters": {
        "jsCode": "// Parse Claude response and score listings\nconst items = $input.all();\nconst USER_SKILLS = [\n  'python', 'pytorch', 'tensorflow', 'langchain', 'fastapi', 'react',\n  'docker', 'aws', 'supabase', 'redis', 'ml', 'ai', 'machine learning',\n  'computer vision', 'rag', 'llm', 'diffusion', 'transformer', 'c++', 'java'\n];\n\nconst result = [];\n\nfor (const item of items) {\n  const content = item.json?.content?.[0]?.text || '';\n  let listings = [];\n  \n  try {\n    const jsonMatch = content.match(/\\[.*\\]/s);\n    if (jsonMatch) listings = JSON.parse(jsonMatch[0]);\n  } catch (e) { continue; }\n  \n  for (const listing of listings) {\n    // Score against user skills\n    const skills = (listing.skills_required || []).map(s => s.toLowerCase());\n    const matched = skills.filter(s => USER_SKILLS.some(us => s.includes(us) || us.includes(s)));\n    const matchScore = Math.min(100, Math.round((matched.length / Math.max(skills.length, 1)) * 100));\n    \n    // Calculate days until deadline\n    let daysUntilDeadline = null;\n    if (listing.deadline) {\n      const diff = new Date(listing.deadline) - new Date();\n      daysUntilDeadline = Math.ceil(diff / (1000 * 60 * 60 * 24));\n    }\n    \n    result.push({\n      json: {\n        ...listing,\n        match_score: matchScore,\n        days_until_deadline: daysUntilDeadline,\n        status: 'saved',\n        is_new: true,\n        scan_id: $('Webhook Trigger').first()?.json?.body?.scan_id || null,\n        created_at: new Date().toISOString(),\n        updated_at: new Date().toISOString()\n      }\n    });\n  }\n}\n\nreturn result;"
      },
      "name": "Score Listings",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1300,
        300
      ],
      "id": "score-listings"
    },
    {
      "parameters": {
        "operation": "upsert",
        "tableId": "internships",
        "onConflict": "role,company",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "role",
              "fieldValue": "={{ $json.role }}"
            },
            {
              "fieldId": "company",
              "fieldValue": "={{ $json.company }}"
            },
            {
              "fieldId": "location",
              "fieldValue": "={{ $json.location }}"
            },
            {
              "fieldId": "loc_type",
              "fieldValue": "={{ $json.loc_type }}"
            },
            {
              "fieldId": "wage",
              "fieldValue": "={{ $json.wage }}"
            },
            {
              "fieldId": "paid",
              "fieldValue": "={{ $json.paid }}"
            },
            {
              "fieldId": "description",
              "fieldValue": "={{ $json.description }}"
            },
            {
              "fieldId": "requirements",
              "fieldValue": "={{ $json.requirements }}"
            },
            {
              "fieldId": "skills_required",
              "fieldValue": "={{ $json.skills_required }}"
            },
            {
              "fieldId": "match_score",
              "fieldValue": "={{ $json.match_score }}"
            },
            {
              "fieldId": "deadline",
              "fieldValue": "={{ $json.deadline }}"
            },
            {
              "fieldId": "days_until_deadline",
              "fieldValue": "={{ $json.days_until_deadline }}"
            },
            {
              "fieldId": "status",
              "fieldValue": "saved"
            },
            {
              "fieldId": "source",
              "fieldValue": "={{ $json.source }}"
            },
            {
              "fieldId": "apply_url",
              "fieldValue": "={{ $json.apply_url }}"
            },
            {
              "fieldId": "is_new",
              "fieldValue": "=true"
            },
            {
              "fieldId": "scan_id",
              "fieldValue": "={{ $json.scan_id }}"
            }
          ]
        }
      },
      "name": "Upsert to Supabase",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        1500,
        300
      ],
      "id": "upsert-supabase",
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "insert",
        "tableId": "scan_logs",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "started_at",
              "fieldValue": "={{ $now }}"
            },
            {
              "fieldId": "completed_at",
              "fieldValue": "={{ $now }}"
            },
            {
              "fieldId": "listings_found",
              "fieldValue": "={{ $items().length }}"
            },
            {
              "fieldId": "listings_added",
              "fieldValue": "={{ $items().length }}"
            },
            {
              "fieldId": "status",
              "fieldValue": "completed"
            },
            {
              "fieldId": "log_text",
              "fieldValue": "Scan complete via n8n Gmail Scanner"
            }
          ]
        }
      },
      "name": "Log Scan",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        1700,
        300
      ],
      "id": "log-scan",
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "sendTo": "rumaisa.kashif2109@gmail.com",
        "subject": "=\ud83c\udfaf Internship Scan Complete \u2014 {{ $items().length }} new listings",
        "emailType": "html",
        "message": "=<h2>Scan Results</h2><p>Found {{ $items().length }} new internship listings.</p><p>View them at your <a href='https://your-app.vercel.app'>Internship Tracker</a>.</p>"
      },
      "name": "Send Digest Email",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        1900,
        300
      ],
      "id": "send-digest",
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Gmail Get Messages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook Trigger": {
      "main": [
        [
          {
            "node": "Gmail Get Messages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gmail Get Messages": {
      "main": [
        [
          {
            "node": "Deduplicate Emails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Deduplicate Emails": {
      "main": [
        [
          {
            "node": "Batch Emails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Batch Emails": {
      "main": [
        [
          {
            "node": "Claude Extract Jobs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude Extract Jobs": {
      "main": [
        [
          {
            "node": "Score Listings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Score Listings": {
      "main": [
        [
          {
            "node": "Upsert to Supabase",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upsert to Supabase": {
      "main": [
        [
          {
            "node": "Log Scan",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Scan": {
      "main": [
        [
          {
            "node": "Send Digest Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "1"
}

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

Gmail Internship Scanner. Uses gmail, httpRequest, supabase. Scheduled trigger; 10 nodes.

Source: https://github.com/RumaisaKashif/internship-tracker/blob/2ef5a87d5cb89c64d05c64273be41642c7f793ae/n8n/gmail-scanner.json — original creator credit. Request a take-down →

More Email & Gmail workflows → · Browse all categories →

Related workflows

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

Email & Gmail

This workflow is an automated invoice payment tracking and reminder system for the Polish accounting service iFirma.pl. It monitors unpaid and overdue invoices, then automatically sends escalating rem

HTTP Request, Stop And Error, Slack +1
Email & Gmail

Automatically extract structured information from emails using AI-powered document analysis. This workflow processes emails from specified domains, classifies them by type, and extracts structured dat

Gmail, HTTP Request, AWS S3 +1
Email & Gmail

What This Flow Does

Gmail, Google Sheets, HTTP Request +1
Email & Gmail

This n8n template allows you to automatically monitor your company's budget by comparing live Bexio accounting data against targets defined in Google Sheets, sending automated weekly email reports. It

Google Sheets, HTTP Request, Gmail
Email & Gmail

Klaviyo List Decay Detection

HTTP Request, Postgres, Error Trigger +1