--- name: n8n-code-python description: Write Python code in n8n Code nodes. Use when writing Python in n8n, using _input/_json/_node syntax, working with standard library, or need to understand Python limitations in n8n Code nodes. --- # Python Code Node (Beta) Expert guidance for writing Python code in n8n Code nodes. --- ## ⚠️ Important: JavaScript First **Recommendation**: Use **JavaScript for 95% of use cases**. Only use Python when: - You need specific Python standard library functions - You're significantly more comfortable with Python syntax - You're doing data transformations better suited to Python **Why JavaScript is preferred:** - Full n8n helper functions ($helpers.httpRequest, etc.) - Luxon DateTime library for advanced date/time operations - No external library limitations - Better n8n documentation and community support --- ## Quick Start ```python # Basic template for Python Code nodes items = _input.all() # Process data processed = [] for item in items: processed.append({ "json": { **item["json"], "processed": True, "timestamp": datetime.now().isoformat() } }) return processed ``` ### Essential Rules 1. **Consider JavaScript first** - Use Python only when necessary 2. **Access data**: `_input.all()`, `_input.first()`, or `_input.item` 3. **CRITICAL**: Must return `[{"json": {...}}]` format 4. **CRITICAL**: Webhook data is under `_json["body"]` (not `_json` directly) 5. **CRITICAL LIMITATION**: **No external libraries** (no requests, pandas, numpy) 6. **Standard library only**: json, datetime, re, base64, hashlib, urllib.parse, math, random, statistics --- ## Mode Selection Guide Same as JavaScript - choose based on your use case: ### Run Once for All Items (Recommended - Default) **Use this mode for:** 95% of use cases - **How it works**: Code executes **once** regardless of input count - **Data access**: `_input.all()` or `_items` array (Native mode) - **Best for**: Aggregation, filtering, batch processing, transformations - **Performance**: Faster for multiple items (single execution) ```python # Example: Calculate total from all items all_items = _input.all() total = sum(item["json"].get("amount", 0) for item in all_items) return [{ "json": { "total": total, "count": len(all_items), "average": total / len(all_items) if all_items else 0 } }] ``` ### Run Once for Each Item **Use this mode for:** Specialized cases only - **How it works**: Code executes **separately** for each input item - **Data access**: `_input.item` or `_item` (Native mode) - **Best for**: Item-specific logic, independent operations, per-item validation - **Performance**: Slower for large datasets (multiple executions) ```python # Example: Add processing timestamp to each item item = _input.item return [{ "json": { **item["json"], "processed": True, "processed_at": datetime.now().isoformat() } }] ``` --- ## Python Modes: Beta vs Native n8n offers two Python execution modes: ### Python (Beta) - Recommended - **Use**: `_input`, `_json`, `_node` helper syntax - **Best for**: Most Python use cases - **Helpers available**: `_now`, `_today`, `_jmespath()` - **Import**: `from datetime import datetime` ```python # Python (Beta) example items = _input.all() now = _now # Built-in datetime object return [{ "json": { "count": len(items), "timestamp": now.isoformat() } }] ``` ### Python (Native) (Beta) - **Use**: `_items`, `_item` variables only - **No helpers**: No `_input`, `_now`, etc. - **More limited**: Standard Python only - **Use when**: Need pure Python without n8n helpers ```python # Python (Native) example processed = [] for item in _items: processed.append({ "json": { "id": item["json"].get("id"), "processed": True } }) return processed ``` **Recommendation**: Use **Python (Beta)** for better n8n integration. --- ## Data Access Patterns ### Pattern 1: _input.all() - Most Common **Use when**: Processing arrays, batch operations, aggregations ```python # Get all items from previous node all_items = _input.all() # Filter, transform as needed valid = [item for item in all_items if item["json"].get("status") == "active"] processed = [] for item in valid: processed.append({ "json": { "id": item["json"]["id"], "name": item["json"]["name"] } }) return processed ``` ### Pattern 2: _input.first() - Very Common **Use when**: Working with single objects, API responses ```python # Get first item only first_item = _input.first() data = first_item["json"] return [{ "json": { "result": process_data(data), "processed_at": datetime.now().isoformat() } }] ``` ### Pattern 3: _input.item - Each Item Mode Only **Use when**: In "Run Once for Each Item" mode ```python # Current item in loop (Each Item mode only) current_item = _input.item return [{ "json": { **current_item["json"], "item_processed": True } }] ``` ### Pattern 4: _node - Reference Other Nodes **Use when**: Need data from specific nodes in workflow ```python # Get output from specific node webhook_data = _node["Webhook"]["json"] http_data = _node["HTTP Request"]["json"] return [{ "json": { "combined": { "webhook": webhook_data, "api": http_data } } }] ``` **See**: [DATA_ACCESS.md](DATA_ACCESS.md) for comprehensive guide --- ## Critical: Webhook Data Structure **MOST COMMON MISTAKE**: Webhook data is nested under `["body"]` ```python # ❌ WRONG - Will raise KeyError name = _json["name"] email = _json["email"] # ✅ CORRECT - Webhook data is under ["body"] name = _json["body"]["name"] email = _json["body"]["email"] # ✅ SAFER - Use .get() for safe access webhook_data = _json.get("body", {}) name = webhook_data.get("name") ``` **Why**: Webhook node wraps all request data under `body` property. This includes POST data, query parameters, and JSON payloads. **See**: [DATA_ACCESS.md](DATA_ACCESS.md) for full webhook structure details --- ## Return Format Requirements **CRITICAL RULE**: Always return list of dictionaries with `"json"` key ### Correct Return Formats ```python # ✅ Single result return [{ "json": { "field1": value1, "field2": value2 } }] # ✅ Multiple results return [ {"json": {"id": 1, "data": "first"}}, {"json": {"id": 2, "data": "second"}} ] # ✅ List comprehension transformed = [ {"json": {"id": item["json"]["id"], "processed": True}} for item in _input.all() if item["json"].get("valid") ] return transformed # ✅ Empty result (when no data to return) return [] # ✅ Conditional return if should_process: return [{"json": processed_data}] else: return [] ``` ### Incorrect Return Formats ```python # ❌ WRONG: Dictionary without list wrapper return { "json": {"field": value} } # ❌ WRONG: List without json wrapper return [{"field": value}] # ❌ WRONG: Plain string return "processed" # ❌ WRONG: Incomplete structure return [{"data": value}] # Should be {"json": value} ``` **Why it matters**: Next nodes expect list format. Incorrect format causes workflow execution to fail. **See**: [ERROR_PATTERNS.md](ERROR_PATTERNS.md) #2 for detailed error solutions --- ## Critical Limitation: No External Libraries **MOST IMPORTANT PYTHON LIMITATION**: Cannot import external packages ### What's NOT Available ```python # ❌ NOT AVAILABLE - Will raise ModuleNotFoundError import requests # ❌ No import pandas # ❌ No import numpy # ❌ No import scipy # ❌ No from bs4 import BeautifulSoup # ❌ No import lxml # ❌ No ``` ### What IS Available (Standard Library) ```python # ✅ AVAILABLE - Standard library only import json # ✅ JSON parsing import datetime # ✅ Date/time operations import re # ✅ Regular expressions import base64 # ✅ Base64 encoding/decoding import hashlib # ✅ Hashing functions import urllib.parse # ✅ URL parsing import math # ✅ Math functions import random # ✅ Random numbers import statistics # ✅ Statistical functions ``` ### Workarounds **Need HTTP requests?** - ✅ Use **HTTP Request node** before Code node - ✅ Or switch to **JavaScript** and use `$helpers.httpRequest()` **Need data analysis (pandas/numpy)?** - ✅ Use Python **statistics** module for basic stats - ✅ Or switch to **JavaScript** for most operations - ✅ Manual calculations with lists and dictionaries **Need web scraping (BeautifulSoup)?** - ✅ Use **HTTP Request node** + **HTML Extract node** - ✅ Or switch to **JavaScript** with regex/string methods **See**: [STANDARD_LIBRARY.md](STANDARD_LIBRARY.md) for complete reference --- ## Common Patterns Overview Based on production workflows, here are the most useful Python patterns: ### 1. Data Transformation Transform all items with list comprehensions ```python items = _input.all() return [ { "json": { "id": item["json"].get("id"), "name": item["json"].get("name", "Unknown").upper(), "processed": True } } for item in items ] ``` ### 2. Filtering & Aggregation Sum, filter, count with built-in functions ```python items = _input.all() total = sum(item["json"].get("amount", 0) for item in items) valid_items = [item for item in items if item["json"].get("amount", 0) > 0] return [{ "json": { "total": total, "count": len(valid_items) } }] ``` ### 3. String Processing with Regex Extract patterns from text ```python import re items = _input.all() email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b' all_emails = [] for item in items: text = item["json"].get("text", "") emails = re.findall(email_pattern, text) all_emails.extend(emails) # Remove duplicates unique_emails = list(set(all_emails)) return [{ "json": { "emails": unique_emails, "count": len(unique_emails) } }] ``` ### 4. Data Validation Validate and clean data ```python items = _input.all() validated = [] for item in items: data = item["json"] errors = [] # Validate fields if not data.get("email"): errors.append("Email required") if not data.get("name"): errors.append("Name required") validated.append({ "json": { **data, "valid": len(errors) == 0, "errors": errors if errors else None } }) return validated ``` ### 5. Statistical Analysis Calculate statistics with statistics module ```python from statistics import mean, median, stdev items = _input.all() values = [item["json"].get("value", 0) for item in items if "value" in item["json"]] if values: return [{ "json": { "mean": mean(values), "median": median(values), "stdev": stdev(values) if len(values) > 1 else 0, "min": min(values), "max": max(values), "count": len(values) } }] else: return [{"json": {"error": "No values found"}}] ``` **See**: [COMMON_PATTERNS.md](COMMON_PATTERNS.md) for 10 detailed Python patterns --- ## Error Prevention - Top 5 Mistakes ### #1: Importing External Libraries (Python-Specific!) ```python # ❌ WRONG: Trying to import external library import requests # ModuleNotFoundError! # ✅ CORRECT: Use HTTP Request node or JavaScript # Add HTTP Request node before Code node # OR switch to JavaScript and use $helpers.httpRequest() ``` ### #2: Empty Code or Missing Return ```python # ❌ WRONG: No return statement items = _input.all() # Processing... # Forgot to return! # ✅ CORRECT: Always return data items = _input.all() # Processing... return [{"json": item["json"]} for item in items] ``` ### #3: Incorrect Return Format ```python # ❌ WRONG: Returning dict instead of list return {"json": {"result": "success"}} # ✅ CORRECT: List wrapper required return [{"json": {"result": "success"}}] ``` ### #4: KeyError on Dictionary Access ```python # ❌ WRONG: Direct access crashes if missing name = _json["user"]["name"] # KeyError! # ✅ CORRECT: Use .get() for safe access name = _json.get("user", {}).get("name", "Unknown") ``` ### #5: Webhook Body Nesting ```python # ❌ WRONG: Direct access to webhook data email = _json["email"] # KeyError! # ✅ CORRECT: Webhook data under ["body"] email = _json["body"]["email"] # ✅ BETTER: Safe access with .get() email = _json.get("body", {}).get("email", "no-email") ``` **See**: [ERROR_PATTERNS.md](ERROR_PATTERNS.md) for comprehensive error guide --- ## Standard Library Reference ### Most Useful Modules ```python # JSON operations import json data = json.loads(json_string) json_output = json.dumps({"key": "value"}) # Date/time from datetime import datetime, timedelta now = datetime.now() tomorrow = now + timedelta(days=1) formatted = now.strftime("%Y-%m-%d") # Regular expressions import re matches = re.findall(r'\d+', text) cleaned = re.sub(r'[^\w\s]', '', text) # Base64 encoding import base64 encoded = base64.b64encode(data).decode() decoded = base64.b64decode(encoded) # Hashing import hashlib hash_value = hashlib.sha256(text.encode()).hexdigest() # URL parsing import urllib.parse params = urllib.parse.urlencode({"key": "value"}) parsed = urllib.parse.urlparse(url) # Statistics from statistics import mean, median, stdev average = mean([1, 2, 3, 4, 5]) ``` **See**: [STANDARD_LIBRARY.md](STANDARD_LIBRARY.md) for complete reference --- ## Best Practices ### 1. Always Use .get() for Dictionary Access ```python # ✅ SAFE: Won't crash if field missing value = item["json"].get("field", "default") # ❌ RISKY: Crashes if field doesn't exist value = item["json"]["field"] ``` ### 2. Handle None/Null Values Explicitly ```python # ✅ GOOD: Default to 0 if None amount = item["json"].get("amount") or 0 # ✅ GOOD: Check for None explicitly text = item["json"].get("text") if text is None: text = "" ``` ### 3. Use List Comprehensions for Filtering ```python # ✅ PYTHONIC: List comprehension valid = [item for item in items if item["json"].get("active")] # ❌ VERBOSE: Manual loop valid = [] for item in items: if item["json"].get("active"): valid.append(item) ``` ### 4. Return Consistent Structure ```python # ✅ CONSISTENT: Always list with "json" key return [{"json": result}] # Single result return results # Multiple results (already formatted) return [] # No results ``` ### 5. Debug with print() Statements ```python # Debug statements appear in browser console (F12) items = _input.all() print(f"Processing {len(items)} items") print(f"First item: {items[0] if items else 'None'}") ``` --- ## When to Use Python vs JavaScript ### Use Python When: - ✅ You need `statistics` module for statistical operations - ✅ You're significantly more comfortable with Python syntax - ✅ Your logic maps well to list comprehensions - ✅ You need specific standard library functions ### Use JavaScript When: - ✅ You need HTTP requests ($helpers.httpRequest()) - ✅ You need advanced date/time (DateTime/Luxon) - ✅ You want better n8n integration - ✅ **For 95% of use cases** (recommended) ### Consider Other Nodes When: - ❌ Simple field mapping → Use **Set** node - ❌ Basic filtering → Use **Filter** node - ❌ Simple conditionals → Use **IF** or **Switch** node - ❌ HTTP requests only → Use **HTTP Request** node --- ## Integration with Other Skills ### Works With: **n8n Expression Syntax**: - Expressions use `{{ }}` syntax in other nodes - Code nodes use Python directly (no `{{ }}`) - When to use expressions vs code **n8n MCP Tools Expert**: - How to find Code node: `search_nodes({query: "code"})` - Get configuration help: `get_node_essentials("nodes-base.code")` - Validate code: `validate_node_operation()` **n8n Node Configuration**: - Mode selection (All Items vs Each Item) - Language selection (Python vs JavaScript) - Understanding property dependencies **n8n Workflow Patterns**: - Code nodes in transformation step - When to use Python vs JavaScript in patterns **n8n Validation Expert**: - Validate Code node configuration - Handle validation errors - Auto-fix common issues **n8n Code JavaScript**: - When to use JavaScript instead - Comparison of JavaScript vs Python features - Migration from Python to JavaScript --- ## Quick Reference Checklist Before deploying Python Code nodes, verify: - [ ] **Considered JavaScript first** - Using Python only when necessary - [ ] **Code is not empty** - Must have meaningful logic - [ ] **Return statement exists** - Must return list of dictionaries - [ ] **Proper return format** - Each item: `{"json": {...}}` - [ ] **Data access correct** - Using `_input.all()`, `_input.first()`, or `_input.item` - [ ] **No external imports** - Only standard library (json, datetime, re, etc.) - [ ] **Safe dictionary access** - Using `.get()` to avoid KeyError - [ ] **Webhook data** - Access via `["body"]` if from webhook - [ ] **Mode selection** - "All Items" for most cases - [ ] **Output consistent** - All code paths return same structure --- ## Additional Resources ### Related Files - [DATA_ACCESS.md](DATA_ACCESS.md) - Comprehensive Python data access patterns - [COMMON_PATTERNS.md](COMMON_PATTERNS.md) - 10 Python patterns for n8n - [ERROR_PATTERNS.md](ERROR_PATTERNS.md) - Top 5 errors and solutions - [STANDARD_LIBRARY.md](STANDARD_LIBRARY.md) - Complete standard library reference ### n8n Documentation - Code Node Guide: https://docs.n8n.io/code/code-node/ - Python in n8n: https://docs.n8n.io/code/builtin/python-modules/ --- **Ready to write Python in n8n Code nodes - but consider JavaScript first!** Use Python for specific needs, reference the error patterns guide to avoid common mistakes, and leverage the standard library effectively.