This workflow follows the Execute Workflow Trigger → 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 →
{
"name": "SignalForge-v4.1-KeywordWorker",
"nodes": [
{
"id": "b1b2c3d4-0000-4000-8000-000000000001",
"name": "Execute Workflow Trigger",
"type": "n8n-nodes-base.executeWorkflowTrigger",
"typeVersion": 1,
"position": [
240,
300
],
"parameters": {}
},
{
"id": "b1b2c3d4-0000-4000-8000-000000000002",
"name": "Cache Gate",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
440,
300
],
"parameters": {
"jsCode": "const input = $input.first().json;\nconst st = $getWorkflowStaticData('global');\nif (!st.cache) st.cache = {};\n\nconst keyword = input.keyword;\nconst config = input.config;\nconst ttlMs = (config.cache_ttl_minutes || 30) * 60 * 1000;\nconst now = Date.now();\n\nconst cached = st.cache[keyword];\n\nif (cached && (now - cached.ts_epoch_ms < ttlMs)) {\n return [{\n json: {\n keyword,\n config,\n cache_hit: true,\n cached_items: cached.items || [],\n cached_hits: 1,\n api_calls: 0,\n failed_calls: 0\n }\n }];\n}\n\nreturn [{\n json: {\n keyword,\n config,\n cache_hit: false,\n cached_hits: 0,\n api_calls: 0,\n failed_calls: 0\n }\n}];"
}
},
{
"id": "b1b2c3d4-0000-4000-8000-000000000003",
"name": "IF Cache Hit",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
640,
300
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true
},
"conditions": [
{
"leftValue": "={{ $json.cache_hit }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals"
}
}
],
"combinator": "and"
}
}
},
{
"id": "b1b2c3d4-0000-4000-8000-000000000004",
"name": "Emit Cached Items",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
840,
200
],
"parameters": {
"jsCode": "const data = $json;\nif (!data.cached_items || data.cached_items.length === 0) {\n return [{ json: { __empty: true, __stream: 'cache' } }];\n}\nreturn data.cached_items.map(i => ({\n json: { ...i, meta: { ...i.meta, cached: true, api_calls: 0, failed_calls: 0 } }\n}));"
}
},
{
"id": "b1b2c3d4-0000-4000-8000-000000000005",
"name": "Fetch HN",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
840,
400
],
"parameters": {
"method": "GET",
"url": "={{ 'https://hn.algolia.com/api/v1/search?query=' + encodeURIComponent($('Cache Gate').first().json.keyword) + '&tags=story&hitsPerPage=5' }}",
"options": {
"continueOnFail": true
}
}
},
{
"id": "b1b2c3d4-0000-4000-8000-000000000006",
"name": "Fetch Wikipedia",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
840,
500
],
"parameters": {
"method": "GET",
"url": "={{ 'https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=' + encodeURIComponent($('Cache Gate').first().json.keyword) + '&format=json&utf8=1&srlimit=5' }}",
"options": {
"continueOnFail": true
}
}
},
{
"id": "b1b2c3d4-0000-4000-8000-000000000007",
"name": "Fetch Crossref",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
840,
600
],
"parameters": {
"method": "GET",
"url": "={{ 'https://api.crossref.org/works?query=' + encodeURIComponent($('Cache Gate').first().json.keyword) + '&rows=5' }}",
"options": {
"continueOnFail": true
}
}
},
{
"id": "b1b2c3d4-0000-4000-8000-000000000008",
"name": "Normalize HN",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1040,
400
],
"parameters": {
"jsCode": "const cacheGate = $('Cache Gate').first().json;\nconst keyword = cacheGate.keyword;\nconst data = $json;\n\nif (!data || !data.hits || data.error) {\n return [{ json: { __empty: true, __src: 'hn', meta: { failed_calls: 1 } } }];\n}\n\nreturn data.hits.map(h => ({\n json: {\n source: 'hn',\n keyword,\n title: h.title,\n url: h.url || `https://news.ycombinator.com/item?id=${h.objectID}`,\n published_at: h.created_at ? new Date(h.created_at).toISOString() : null,\n raw_score: (h.points || 0) + (h.num_comments || 0),\n score: (h.points || 0) + (h.num_comments || 0),\n summary_raw: null,\n summary: null,\n meta: { author: h.author, venue: 'Hacker News', cached: false, api_calls: 0, failed_calls: 0 }\n }\n}));"
}
},
{
"id": "b1b2c3d4-0000-4000-8000-000000000009",
"name": "Normalize Wikipedia",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1040,
500
],
"parameters": {
"jsCode": "const cacheGate = $('Cache Gate').first().json;\nconst keyword = cacheGate.keyword;\nconst data = $json;\n\nif (!data || !data.query || !data.query.search || data.error) {\n return [{ json: { __empty: true, __src: 'wikipedia', meta: { failed_calls: 1 } } }];\n}\n\nreturn data.query.search.map(hit => ({\n json: {\n source: 'wikipedia',\n keyword,\n title: hit.title,\n url: `https://en.wikipedia.org/wiki/${encodeURIComponent(hit.title.replace(/ /g, '_'))}`,\n published_at: null,\n raw_score: Math.floor((hit.size || 0) / 100),\n score: Math.floor((hit.size || 0) / 100),\n summary_raw: hit.snippet || null,\n summary: null,\n meta: { author: null, venue: 'Wikipedia', cached: false, api_calls: 0, failed_calls: 0 }\n }\n}));"
}
},
{
"id": "b1b2c3d4-0000-4000-8000-000000000010",
"name": "Normalize Crossref",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1040,
600
],
"parameters": {
"jsCode": "const cacheGate = $('Cache Gate').first().json;\nconst keyword = cacheGate.keyword;\nconst data = $json;\n\nif (!data || !data.message || !data.message.items || data.error) {\n return [{ json: { __empty: true, __src: 'crossref', meta: { failed_calls: 1 } } }];\n}\n\nreturn data.message.items.map(item => ({\n json: {\n source: 'crossref',\n keyword,\n title: (item.title && item.title[0]) ? item.title[0] : 'Untitled',\n url: item.URL || '',\n published_at: (item['published-print'] && item['published-print']['date-time']) ? item['published-print']['date-time'] : null,\n raw_score: 20 + (item['is-referenced-by-count'] || 0),\n score: 20 + (item['is-referenced-by-count'] || 0),\n summary_raw: null,\n summary: null,\n meta: {\n author: item.author ? item.author[0].family : null,\n venue: item['container-title'] ? item['container-title'][0] : 'Crossref',\n cached: false,\n api_calls: 0,\n failed_calls: 0\n }\n }\n}));"
}
},
{
"id": "b1b2c3d4-0000-4000-8000-000000000011",
"name": "Merge HN Wiki",
"type": "n8n-nodes-base.merge",
"typeVersion": 2.1,
"position": [
1240,
450
],
"parameters": {
"mode": "append",
"options": {}
}
},
{
"id": "b1b2c3d4-0000-4000-8000-000000000012",
"name": "Merge All",
"type": "n8n-nodes-base.merge",
"typeVersion": 2.1,
"position": [
1440,
500
],
"parameters": {
"mode": "append",
"options": {}
}
},
{
"id": "b1b2c3d4-0000-4000-8000-000000000013",
"name": "Cache Save",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1640,
400
],
"parameters": {
"jsCode": "const cacheGate = $('Cache Gate').first().json;\nconst keyword = cacheGate.keyword;\nconst st = $getWorkflowStaticData('global');\nconst allItems = $input.all();\n\n// Count failed calls\nlet failedCalls = 0;\nconst validItems = allItems\n .map(i => i.json)\n .filter(i => {\n if (i.__empty) {\n if (i.meta?.failed_calls) failedCalls += i.meta.failed_calls;\n else failedCalls++;\n return false;\n }\n return true;\n });\n\nst.cache[keyword] = {\n ts_epoch_ms: Date.now(),\n items: validItems\n};\n\nif (validItems.length === 0) {\n return [{ json: { __empty: true, keyword, meta: { cached: false, api_calls: 3, failed_calls: failedCalls || 3 } } }];\n}\n\nreturn validItems.map(item => ({\n json: {\n ...item,\n meta: { ...item.meta, cached: false, api_calls: 3, failed_calls: 0 }\n }\n}));"
}
},
{
"id": "b1b2c3d4-0000-4000-8000-000000000014",
"name": "Merge Cached",
"type": "n8n-nodes-base.merge",
"typeVersion": 2.1,
"position": [
1640,
300
],
"parameters": {
"mode": "append",
"options": {}
}
}
],
"connections": {
"Execute Workflow Trigger": {
"main": [
[
{
"node": "Cache Gate",
"type": "main",
"index": 0
}
]
]
},
"Cache Gate": {
"main": [
[
{
"node": "IF Cache Hit",
"type": "main",
"index": 0
}
]
]
},
"IF Cache Hit": {
"main": [
[
{
"node": "Emit Cached Items",
"type": "main",
"index": 0
}
],
[
{
"node": "Fetch HN",
"type": "main",
"index": 0
},
{
"node": "Fetch Wikipedia",
"type": "main",
"index": 0
},
{
"node": "Fetch Crossref",
"type": "main",
"index": 0
}
]
]
},
"Emit Cached Items": {
"main": [
[
{
"node": "Merge Cached",
"type": "main",
"index": 0
}
]
]
},
"Fetch HN": {
"main": [
[
{
"node": "Normalize HN",
"type": "main",
"index": 0
}
]
]
},
"Fetch Wikipedia": {
"main": [
[
{
"node": "Normalize Wikipedia",
"type": "main",
"index": 0
}
]
]
},
"Fetch Crossref": {
"main": [
[
{
"node": "Normalize Crossref",
"type": "main",
"index": 0
}
]
]
},
"Normalize HN": {
"main": [
[
{
"node": "Merge HN Wiki",
"type": "main",
"index": 0
}
]
]
},
"Normalize Wikipedia": {
"main": [
[
{
"node": "Merge HN Wiki",
"type": "main",
"index": 1
}
]
]
},
"Merge HN Wiki": {
"main": [
[
{
"node": "Merge All",
"type": "main",
"index": 0
}
]
]
},
"Normalize Crossref": {
"main": [
[
{
"node": "Merge All",
"type": "main",
"index": 1
}
]
]
},
"Merge All": {
"main": [
[
{
"node": "Cache Save",
"type": "main",
"index": 0
}
]
]
},
"Cache Save": {
"main": [
[
{
"node": "Merge Cached",
"type": "main",
"index": 1
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
SignalForge-v4.1-KeywordWorker. Uses executeWorkflowTrigger, httpRequest. Event-driven trigger; 14 nodes.
Source: https://github.com/turtir-ai/n8n-workflow-studio/blob/main/public/fixtures/SignalForge-v4.1-KeywordWorker.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
02_LLM_Pipeline v1.0. Uses executeWorkflowTrigger, httpRequest, seaTable. Event-driven trigger; 65 nodes.
This template is a powerful, reusable utility for managing stateful, long-running processes. It allows a main workflow to be paused indefinitely at "checkpoints" and then be resumed by external, async
Upload files from any source to your account Kommo or AmoCRM with a simple and reusable workflow. It can split a large file into small ones and upload chunks. Works for Kommo and amoCRM There are 3 re
Remixed Backup your workflows to GitHub from Solomon's work. Check out his templates.
Remixed Backup your workflows to GitHub from Solomon's work. Check out his templates.