
Github Deal Sourcing Automation for VCs.
We've built an automation for VCs that finds startups before they're famous, using Github and n8n.
Workflow JSON
{
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "months"
}
]
}
},
"id": "95487402-4b92-45a2-92c0-600febe702db",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.1,
"position": [
0,
0
]
},
{
"parameters": {
"jsCode": "const topics = [\n 'devtools',\n 'developer-tools',\n 'ai',\n 'llm',\n 'fintech',\n 'infrastructure',\n 'cli',\n 'open-source'\n];\n\nconst today = new Date();\nconst oneMonthAgo = new Date(today);\noneMonthAgo.setMonth(today.getMonth() - 1);\nconst dateStr = oneMonthAgo.toISOString().split('T')[0];\n\nconst queries = topics.map(topic => ({\n topic,\n query: topic:${topic} stars:200..5000 created:>${dateStr},\n url: https://api.github.com/search/repositories?q=topic:${encodeURIComponent(topic)}+stars:200..5000+created:>${dateStr}&sort=stars&order=desc&per_page=10\n}));\n\nreturn queries.map(q => ({ json: q }));"
},
"id": "ede7e76f-af81-4e74-9956-7b3e139d6b55",
"name": "Build GitHub Search Queries",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
224,
0
]
},
{
"parameters": {
"url": "={{ $json.url }}",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"options": {
"response": {
"response": {}
}
}
},
"id": "f1fb1f80-dafd-4530-a70f-5c9ec02e902a",
"name": "GitHub Search API",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
448,
0
],
"credentials": {
"httpHeaderAuth": {
"id": "jqHyiflYNgDGE85N",
"name": "Github"
}
}
},
{
"parameters": {
"jsCode": "// Flatten all repos from all topic searches\nconst items = $input.all();\nconst seen = new Set();\nconst repos = [];\n\nfor (const item of items) {\n const results = item.json.items || [];\n for (const repo of results) {\n if (!seen.has(repo.id)) {\n seen.add(repo.id);\n repos.push({\n json: {\n id: repo.id,\n name: repo.full_name,\n description: repo.description || 'No description',\n url: repo.html_url,\n stars: repo.stargazers_count,\n forks: repo.forks_count,\n watchers: repo.watchers_count,\n language: repo.language || 'Unknown',\n topics: (repo.topics || []).join(', '),\n created_at: repo.created_at,\n updated_at: repo.updated_at,\n open_issues: repo.open_issues_count,\n owner: repo.owner?.login,\n owner_type: repo.owner?.type,\n readme_url: https://api.github.com/repos/${repo.full_name}/readme,\n commits_url: https://api.github.com/repos/${repo.full_name}/commits?per_page=1,\n contributors_url: https://api.github.com/repos/${repo.full_name}/contributors?per_page=5\n }\n });\n }\n }\n}\n\nreturn repos;"
},
"id": "9a75c758-de31-4624-9e0d-ebb5df73b4bd",
"name": "Flatten & Deduplicate Repos",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
672,
0
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 1
},
"conditions": [
{
"id": "filter-stars",
"leftValue": "={{ $json.stars }}",
"rightValue": 200,
"operator": {
"type": "number",
"operation": "gte"
}
},
{
"id": "filter-stars-max",
"leftValue": "={{ $json.stars }}",
"rightValue": 5000,
"operator": {
"type": "number",
"operation": "lte"
}
},
{
"id": "filter-forks",
"leftValue": "={{ $json.forks }}",
"rightValue": 10,
"operator": {
"type": "number",
"operation": "gte"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "026880ca-0faf-4cf0-ba05-39ac1dfe72bf",
"name": "Filter: Quality Threshold",
"type": "n8n-nodes-base.filter",
"typeVersion": 2,
"position": [
880,
0
]
},
{
"parameters": {
"url": "={{ $json.readme_url }}",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"options": {
"response": {
"response": {
"fullResponse": true,
"responseFormat": "json"
}
}
}
},
"id": "ac378f4b-9a89-49c1-a5e0-762f53427e3c",
"name": "Fetch README",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1120,
0
],
"credentials": {
"httpHeaderAuth": {
"id": "jqHyiflYNgDGE85N",
"name": "Github"
}
},
"continueOnFail": true
},
{
"parameters": {
"jsCode": "// Topics to track - customize these for your thesis\nconst topics = [\n 'devtools',\n 'developer-tools',\n 'ai',\n 'llm',\n 'fintech',\n 'infrastructure',\n 'cli',\n 'open-source'\n];\n\nconst today = new Date();\nconst oneYearAgo = new Date(today);\noneYearAgo.setFullYear(today.getFullYear() - 1);\nconst dateStr = oneYearAgo.toISOString().split('T')[0];\n\nconst queries = topics.map(topic => ({\n topic,\n query: topic:${topic} stars:200..5000 created:>${dateStr},\n url: https://api.github.com/search/repositories?q=topic:${encodeURIComponent(topic)}+stars:200..5000+created:>${dateStr}&sort=stars&order=desc&per_page=10\n}));\n\nreturn queries.map(q => ({ json: q }));"
},
"id": "108da8f5-223b-4452-8f6f-e9b5464b0376",
"name": "Build GitHub Search Queries1",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
224,
240
]
},
{
"parameters": {
"jsCode": "const readmeItems = $input.all();\nconst repoItems = $('Filter: Quality Threshold').all();\n\nconst results = [];\n\nfor (let i = 0; i < readmeItems.length; i++) {\n const readmeResponse = readmeItems[i].json.body; // <-- added .body here\n const repo = repoItems[i].json;\n\n let readmeText = 'README not available';\n try {\n if (readmeResponse.content) {\n readmeText = Buffer.from(readmeResponse.content, 'base64')\n .toString('utf-8')\n .slice(0, 2000);\n }\n } catch(e) {\n readmeText = 'README not available';\n }\n\n results.push({\n json: {\n ...repo,\n readme_excerpt: readmeText\n }\n });\n}\n\nreturn results;"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1312,
0
],
"id": "cc9bdbe8-aca9-4c9b-8e36-8dfb9b41404f",
"name": "Merge"
},
{
"parameters": {
"jsCode": "const items = $input.all();\nconst cards = [];\n\nfor (const item of items) {\n const r = item.json;\n \n const createdDate = new Date(r.created_at).toDateString();\n const updatedDate = new Date(r.updated_at).toDateString();\n \n const topicTags = (r.topics || '').split(', ').map(t => \n <span style=\"display:inline-block; background:#f3f4f6; color:#374151; font-size:11px; padding:2px 8px; border-radius:4px; margin:2px 2px 2px 0;\">${t}</span>\n ).join('');\n\n const card = \n <tr>\n <td style=\"padding: 24px 0; border-bottom: 1px solid #e5e7eb;\">\n <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n <tr>\n <td>\n\n <!-- Repo name -->\n <h2 style=\"margin: 0 0 6px 0; font-size: 18px; font-family: Arial, sans-serif;\">\n <a href=\"${r.url}\" style=\"color: #111827; text-decoration: none;\">${r.name}</a>\n </h2>\n\n <!-- Description -->\n <p style=\"margin: 0 0 14px 0; font-size: 14px; color: #6b7280; font-family: Arial, sans-serif; line-height: 1.6;\">\n ${r.description || 'No description'}\n </p>\n\n <!-- Stats row -->\n <table cellpadding=\"0\" cellspacing=\"0\" style=\"margin-bottom: 14px;\">\n <tr>\n <td style=\"font-size: 13px; color: #374151; font-family: Arial, sans-serif; padding-right: 20px;\">★ ${r.stars?.toLocaleString()} stars</td>\n <td style=\"font-size: 13px; color: #374151; font-family: Arial, sans-serif; padding-right: 20px;\">⑂ ${r.forks?.toLocaleString()} forks</td>\n <td style=\"font-size: 13px; color: #374151; font-family: Arial, sans-serif; padding-right: 20px;\">👁 ${r.watchers?.toLocaleString()} watchers</td>\n <td style=\"font-size: 13px; color: #374151; font-family: Arial, sans-serif; padding-right: 20px;\">⚠ ${r.open_issues} issues</td>\n <td style=\"font-size: 13px; color: #374151; font-family: Arial, sans-serif;\">${r.language}</td>\n </tr>\n </table>\n\n <!-- Owner + dates row -->\n <table cellpadding=\"0\" cellspacing=\"0\" style=\"margin-bottom: 14px;\">\n <tr>\n <td style=\"font-size: 13px; color: #374151; font-family: Arial, sans-serif; padding-right: 20px;\">\n 👤 <a href=\"https://github.com/${r.owner}\" style=\"color: #374151;\">${r.owner}</a> · ${r.owner_type}\n </td>\n <td style=\"font-size: 13px; color: #6b7280; font-family: Arial, sans-serif; padding-right: 20px;\">Created: ${createdDate}</td>\n <td style=\"font-size: 13px; color: #6b7280; font-family: Arial, sans-serif;\">Updated: ${updatedDate}</td>\n </tr>\n </table>\n\n <!-- Topics -->\n <div style=\"margin-bottom: 16px;\">\n ${topicTags}\n </div>\n\n <!-- README excerpt -->\n <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin-bottom: 16px;\">\n <tr>\n <td style=\"background: #f9fafb; border-left: 3px solid #111827; padding: 12px 16px; border-radius: 0 6px 6px 0;\">\n <p style=\"margin: 0 0 6px 0; font-size: 11px; font-weight: 600; color: #6b7280; text-transform: uppercase; letter-spacing: 0.05em; font-family: Arial, sans-serif;\">README</p>\n <p style=\"margin: 0; font-size: 13px; color: #374151; font-family: Arial, sans-serif; line-height: 1.7; white-space: pre-line;\">\n ${(r.readme_excerpt || 'Not available').slice(0, 600)}\n </p>\n </td>\n </tr>\n </table>\n\n <!-- Links row -->\n <table cellpadding=\"0\" cellspacing=\"0\" style=\"margin-bottom: 16px;\">\n <tr>\n <td style=\"padding-right: 12px;\">\n <a href=\"${r.url}\" style=\"font-size: 12px; color: #6b7280; font-family: Arial, sans-serif;\">Repo</a>\n </td>\n <td style=\"padding-right: 12px;\">\n <a href=\"${r.commits_url.replace('?per_page=1', '')}\" style=\"font-size: 12px; color: #6b7280; font-family: Arial, sans-serif;\">Commits</a>\n </td>\n <td>\n <a href=\"${r.contributors_url.replace('?per_page=5', '')}\" style=\"font-size: 12px; color: #6b7280; font-family: Arial, sans-serif;\">Contributors</a>\n </td>\n </tr>\n </table>\n\n <!-- CTA -->\n <a href=\"${r.url}\" style=\"display: inline-block; background: #111827; color: #ffffff; font-size: 13px; font-weight: 600; padding: 10px 20px; border-radius: 6px; text-decoration: none; font-family: Arial, sans-serif;\">\n View on GitHub →\n </a>\n\n </td>\n </tr>\n </table>\n </td>\n </tr>\n ;\n \n cards.push(card);\n}\n\nconst html = \n<!DOCTYPE html>\n<html>\n<head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width\"></head>\n<body style=\"margin: 0; padding: 0; background: #f3f4f6;\">\n\n <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"background: #f3f4f6; padding: 32px 0;\">\n <tr>\n <td align=\"center\">\n <table width=\"640\" cellpadding=\"0\" cellspacing=\"0\" style=\"background: #ffffff; border-radius: 12px; overflow: hidden;\">\n \n <!-- Header -->\n <tr>\n <td style=\"background: #111827; padding: 28px 32px;\">\n <h1 style=\"margin: 0 0 6px 0; color: #ffffff; font-size: 22px; font-family: Arial, sans-serif;\">\n GitHub Deal Sourcing Digest\n </h1>\n <p style=\"margin: 0; color: #9ca3af; font-size: 14px; font-family: Arial, sans-serif;\">\n ${new Date().toDateString()} · ${items.length} repositories\n </p>\n </td>\n </tr>\n\n <!-- Cards -->\n <tr>\n <td style=\"padding: 0 32px;\">\n <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n ${cards.join('')}\n </table>\n </td>\n </tr>\n\n <!-- Footer -->\n <tr>\n <td style=\"padding: 24px 32px; background: #f9fafb; border-top: 1px solid #e5e7eb;\">\n <p style=\"margin: 0; font-size: 12px; color: #9ca3af; font-family: Arial, sans-serif;\">\n Generated automatically by your GitHub deal sourcing workflow\n </p>\n </td>\n </tr>\n\n </table>\n </td>\n </tr>\n </table>\n\n</body>\n</html>\n;\n\nreturn [{ json: { html } }];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1520,
0
],
"id": "8c59dc59-2085-41fb-abef-bc2a9d67fe53",
"name": "Code in JavaScript"
},
{
"parameters": {
"sendTo": "daksh@neurolooplab.com",
"subject": "GitHub Upcoming Repos For This Month",
"message": "={{ $json.html }}",
"options": {}
},
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.2,
"position": [
1728,
0
],
"id": "11c5a806-0c2f-4bc1-b1d5-5f9db6e6ce50",
"name": "Send a message",
"webhookId": "2622bb19-3ac6-48af-aa1e-4a5916efb0ec",
"credentials": {
"gmailOAuth2": {
"id": "MbrD9BQtwtesvYq8",
"name": "Gmail account"
}
}
}
],
"connections": {
"Schedule Trigger": {
"main": [
[
{
"node": "Build GitHub Search Queries",
"type": "main",
"index": 0
}
]
]
},
"Build GitHub Search Queries": {
"main": [
[
{
"node": "GitHub Search API",
"type": "main",
"index": 0
}
]
]
},
"GitHub Search API": {
"main": [
[
{
"node": "Flatten & Deduplicate Repos",
"type": "main",
"index": 0
}
]
]
},
"Flatten & Deduplicate Repos": {
"main": [
[
{
"node": "Filter: Quality Threshold",
"type": "main",
"index": 0
}
]
]
},
"Filter: Quality Threshold": {
"main": [
[
{
"node": "Fetch README",
"type": "main",
"index": 0
}
]
]
},
"Fetch README": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"Merge": {
"main": [
[
{
"node": "Code in JavaScript",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript": {
"main": [
[
{
"node": "Send a message",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"meta": {
"instanceId": "68e38a4d16f6bb0fc4dc0d12c182b0537e5cd43d16c8c06d03fb68dbc38c8ff4"
}
}