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": "AI Keyword & Entity Extractor (Ollama)",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "extract-keywords",
"responseMode": "responseNode",
"options": {}
},
"id": "c3d4e5f6-0003-4000-8000-000000000001",
"name": "Webhook - Receive Text",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
240,
300
]
},
{
"parameters": {
"jsCode": "const input = $input.first().json;\nlet text = '';\n\nif (input.body && input.body.text) {\n text = input.body.text;\n} else if (input.body && input.body.url) {\n return [{ json: { text: '', url: input.body.url, mode: 'url', valid: true } }];\n} else {\n return [{ json: { error: 'Provide { \"text\": \"...\" } in the request body', valid: false } }];\n}\n\nif (text.length < 20) {\n return [{ json: { error: 'Text must be at least 20 characters for meaningful extraction', valid: false } }];\n}\n\n// Cap at 6000 chars for context window\nconst processedText = text.substring(0, 6000);\n\nreturn [{ json: { text: processedText, original_length: text.length, mode: 'text', valid: true } }];"
},
"id": "c3d4e5f6-0003-4000-8000-000000000002",
"name": "Validate & Prepare",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
480,
300
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": false
},
"combinator": "and",
"conditions": [
{
"leftValue": "={{ $json.valid }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals"
}
}
]
}
},
"id": "c3d4e5f6-0003-4000-8000-000000000003",
"name": "Is Valid?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
700,
300
]
},
{
"parameters": {
"url": "http://localhost:11434/api/generate",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ model: 'llama3.1:8b', prompt: 'You are a keyword and entity extraction engine. Analyze the following text and extract:\\n\\n1. **Keywords**: The top 5-10 most important keywords or phrases\\n2. **Named Entities**: People, organizations, locations, dates, products mentioned\\n3. **Topics**: 2-4 main topics/themes\\n4. **Category**: The best single-word category for this text (Technology, Business, Health, Science, Politics, Sports, Entertainment, Education, Other)\\n\\nRespond ONLY in valid JSON:\\n{\"keywords\": [\"keyword1\", \"keyword2\"], \"entities\": {\"people\": [], \"organizations\": [], \"locations\": [], \"dates\": [], \"products\": []}, \"topics\": [\"topic1\", \"topic2\"], \"category\": \"Technology\"}\\n\\nText:\\n' + $json.text, stream: false, options: { temperature: 0.1, num_predict: 1500 } }) }}",
"options": {
"timeout": 120000
}
},
"id": "c3d4e5f6-0003-4000-8000-000000000004",
"name": "Extract Keywords (Ollama)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
960,
200
]
},
{
"parameters": {
"jsCode": "const ollamaResponse = $input.first().json;\nlet responseText;\n\ntry {\n const parsed = typeof ollamaResponse.data === 'string' ? JSON.parse(ollamaResponse.data) : ollamaResponse;\n responseText = parsed.response || parsed.data?.response || '';\n} catch (e) {\n responseText = JSON.stringify(ollamaResponse);\n}\n\n// Extract JSON from response\nlet extraction;\ntry {\n const jsonMatch = responseText.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n extraction = JSON.parse(jsonMatch[0]);\n } else {\n extraction = { raw_extraction: responseText };\n }\n} catch (e) {\n extraction = { raw_extraction: responseText };\n}\n\nconst inputData = $('Validate & Prepare').first().json;\n\nreturn [{\n json: {\n ...extraction,\n metadata: {\n text_length: inputData.original_length,\n model: 'llama3.1:8b',\n processed_at: new Date().toISOString()\n }\n }\n}];"
},
"id": "c3d4e5f6-0003-4000-8000-000000000005",
"name": "Parse Extraction Results",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1200,
200
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify($json) }}"
},
"id": "c3d4e5f6-0003-4000-8000-000000000006",
"name": "Send Extraction Response",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
1440,
200
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ error: $('Validate & Prepare').first().json.error }) }}",
"options": {
"responseCode": 400
}
},
"id": "c3d4e5f6-0003-4000-8000-000000000007",
"name": "Send Error Response",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
960,
440
]
}
],
"connections": {
"Webhook - Receive Text": {
"main": [
[
{
"node": "Validate & Prepare",
"type": "main",
"index": 0
}
]
]
},
"Validate & Prepare": {
"main": [
[
{
"node": "Is Valid?",
"type": "main",
"index": 0
}
]
]
},
"Is Valid?": {
"main": [
[
{
"node": "Extract Keywords (Ollama)",
"type": "main",
"index": 0
}
],
[
{
"node": "Send Error Response",
"type": "main",
"index": 0
}
]
]
},
"Extract Keywords (Ollama)": {
"main": [
[
{
"node": "Parse Extraction Results",
"type": "main",
"index": 0
}
]
]
},
"Parse Extraction Results": {
"main": [
[
{
"node": "Send Extraction Response",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"tags": [
{
"name": "AI"
},
{
"name": "Ollama"
},
{
"name": "NLP"
},
{
"name": "Free Sample"
}
]
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
AI Keyword & Entity Extractor (Ollama). Uses httpRequest. Webhook trigger; 7 nodes.
Source: https://github.com/bonskari/n8n-ollama-workflows/blob/main/workflows/keyword-extractor.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.
<section> <h2>🌊 What it Does</h2> <p> This workflow <strong>automatically classifies uploaded files</strong> (PDFs or images) as <span>floorplans</span> or <span>non‑floorplans</span>. It filters out
This n8n workflow provides a ready-to-use API endpoint for extracting structured data from images. It processes an image URL using an AI-powered OCR model and returns the extracted details in a struct
This workflow automates the process of extracting images from uploaded documents in Google Drive using the VLM Run Execute Agent, then downloads and saves those extracted images into a designated Driv
Who is this for? Event organizers, conference planners, and marketing teams fighting registration drop-off who want 4-field forms with LinkedIn-level attendee intelligence. What problem is this workfl
Use Case: Analyze images with multiple subjects. In this use case I have a bookshelf and am extracting and verifying book titles/authors from a bookshelf photo.