How can I parse JSON data in n8n workflows?
Parsing JSON data is one of the most fundamental operations in n8n workflows. Since most modern APIs return JSON responses, and n8n itself uses JSON as its internal data format, understanding how to effectively parse, manipulate, and transform JSON data is essential for building robust automation workflows. This guide covers everything from basic JSON operations to advanced parsing techniques.
Understanding JSON in n8n
JSON (JavaScript Object Notation) is n8n's native data format. Every node in an n8n workflow processes and outputs data as JSON objects. When you make HTTP requests to APIs, scrape web data, or integrate with services, you'll frequently encounter JSON responses that need to be parsed and transformed.
n8n provides multiple ways to work with JSON data:
- Automatic JSON parsing for HTTP Request nodes
- Built-in JSON operations through expression syntax
- Code nodes for complex JSON manipulation
- Set node for restructuring JSON data
- JSON node for advanced parsing scenarios
Method 1: Automatic JSON Parsing with HTTP Request Node
The HTTP Request node automatically parses JSON responses when the Content-Type
header is application/json
. This is the simplest and most common way to work with JSON data.
Basic Example:
HTTP Request → Process JSON → Output
HTTP Request Node Configuration:
{
"method": "GET",
"url": "https://api.example.com/products",
"responseFormat": "json",
"options": {
"response": {
"response": {
"fullResponse": false
}
}
}
}
The parsed JSON is automatically available in subsequent nodes via {{ $json }}
syntax.
Method 2: Using Code Node for JSON Parsing
For advanced JSON parsing scenarios, the Code node provides full JavaScript capabilities. This is ideal when you need to transform data structures, filter arrays, or perform complex operations.
Basic JSON Parsing
// Access JSON from previous node
const data = $input.item.json;
// Parse JSON string if needed
// const data = JSON.parse($input.item.json.rawData);
// Extract specific fields
const result = {
id: data.id,
name: data.name,
email: data.contact.email,
createdAt: new Date().toISOString()
};
return { json: result };
Parsing Nested JSON Structures
// Sample nested JSON from API response
const response = $input.item.json;
// Extract nested data with safe access
const products = response.data?.items || [];
const parsedProducts = products.map(product => ({
productId: product.id,
productName: product.attributes?.name || 'Unknown',
price: product.pricing?.regular?.amount || 0,
currency: product.pricing?.regular?.currency || 'USD',
inStock: product.inventory?.available || false,
categories: product.categories?.map(cat => cat.name) || [],
imageUrl: product.images?.[0]?.url || null,
specifications: {
weight: product.specs?.weight,
dimensions: product.specs?.dimensions,
material: product.specs?.material
}
}));
return parsedProducts.map(product => ({ json: product }));
Handling Array Responses
// Parse array of JSON objects
const items = $input.all();
const processedItems = items.map((item, index) => {
const data = item.json;
return {
index: index,
userId: data.user?.id,
userName: data.user?.name,
totalOrders: data.orders?.length || 0,
totalSpent: data.orders?.reduce((sum, order) => sum + order.amount, 0) || 0,
lastOrderDate: data.orders?.[0]?.date || null,
tags: data.tags?.join(', ') || 'none'
};
});
return processedItems.map(item => ({ json: item }));
Method 3: Using n8n Expression Syntax
n8n's expression syntax allows you to parse and transform JSON data without writing full code blocks. This is perfect for simple extractions and transformations.
Basic Field Extraction
In any node, you can reference JSON data using expressions:
// Extract top-level field
{{ $json.fieldName }}
// Extract nested field
{{ $json.user.profile.email }}
// Extract from array
{{ $json.items[0].name }}
// Extract with fallback
{{ $json.optionalField || 'default value' }}
Expression Functions for JSON
// Get array length
{{ $json.items.length }}
// Check if field exists
{{ $json.hasOwnProperty('fieldName') }}
// Join array items
{{ $json.tags.join(', ') }}
// Filter array
{{ $json.items.filter(item => item.active) }}
// Map array
{{ $json.users.map(user => user.email) }}
// Find in array
{{ $json.products.find(p => p.id === 123) }}
Method 4: Using Set Node for JSON Restructuring
The Set node provides a visual interface for restructuring JSON data without writing code. This is ideal for mapping API responses to your desired format.
Set Node Configuration:
{
"keepOnlySet": true,
"values": {
"string": [
{
"name": "userId",
"value": "={{ $json.data.user.id }}"
},
{
"name": "fullName",
"value": "={{ $json.data.user.firstName }} {{ $json.data.user.lastName }}"
}
],
"number": [
{
"name": "age",
"value": "={{ $json.data.user.age }}"
}
],
"boolean": [
{
"name": "isActive",
"value": "={{ $json.data.user.status === 'active' }}"
}
]
}
}
Advanced JSON Parsing Techniques
Parsing JSON from Web Scraping
When scraping websites, you often encounter JSON embedded in HTML or returned as strings. Here's how to extract and parse it:
// Extract JSON from HTML script tag
const html = $input.item.json.html;
// Method 1: Using regex to extract JSON
const jsonMatch = html.match(/<script type="application\/json">(.*?)<\/script>/s);
if (jsonMatch) {
const data = JSON.parse(jsonMatch[1]);
return { json: data };
}
// Method 2: Extract JSON from data attributes
const cheerio = require('cheerio');
const $ = cheerio.load(html);
const jsonString = $('#app').attr('data-config');
const config = JSON.parse(jsonString);
return { json: config };
Flattening Nested JSON
// Function to flatten deeply nested JSON
function flattenObject(obj, prefix = '') {
const flattened = {};
for (const key in obj) {
const newKey = prefix ? `${prefix}_${key}` : key;
if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
Object.assign(flattened, flattenObject(obj[key], newKey));
} else if (Array.isArray(obj[key])) {
flattened[newKey] = JSON.stringify(obj[key]);
} else {
flattened[newKey] = obj[key];
}
}
return flattened;
}
const nested = $input.item.json;
const flat = flattenObject(nested);
return { json: flat };
Parsing JSON with Type Validation
// Validate and parse JSON with type checking
function parseWithValidation(data) {
const schema = {
id: 'number',
name: 'string',
email: 'string',
isActive: 'boolean',
tags: 'array'
};
const validated = {};
const errors = [];
for (const [field, expectedType] of Object.entries(schema)) {
const value = data[field];
const actualType = Array.isArray(value) ? 'array' : typeof value;
if (actualType !== expectedType) {
errors.push(`Field ${field}: expected ${expectedType}, got ${actualType}`);
validated[field] = null;
} else {
validated[field] = value;
}
}
return {
data: validated,
isValid: errors.length === 0,
errors: errors
};
}
const input = $input.item.json;
const result = parseWithValidation(input);
return { json: result };
Merging Multiple JSON Sources
// Combine data from multiple API calls
const items = $input.all();
// Merge by common key (e.g., user ID)
const merged = {};
items.forEach(item => {
const userId = item.json.userId || item.json.id;
if (!merged[userId]) {
merged[userId] = {};
}
// Deep merge
merged[userId] = {
...merged[userId],
...item.json
};
});
// Convert back to array
const result = Object.values(merged);
return result.map(item => ({ json: item }));
Working with JSON from Different Sources
Parsing API Responses
Most APIs return structured JSON. Here's how to handle common API response patterns:
// Pattern 1: Data wrapper
// Response: { "success": true, "data": {...}, "meta": {...} }
const apiResponse = $input.item.json;
const actualData = apiResponse.data;
// Pattern 2: Paginated response
// Response: { "items": [...], "page": 1, "total": 100 }
const items = apiResponse.items;
const hasMore = apiResponse.page * apiResponse.per_page < apiResponse.total;
// Pattern 3: Error handling
if (apiResponse.error || apiResponse.status === 'error') {
throw new Error(apiResponse.message || 'API request failed');
}
return { json: actualData };
Parsing JSON from Webhooks
When receiving webhook data, you may need to parse and validate incoming JSON:
// Webhook data from $input
const webhookData = $input.item.json.body;
// Parse if string
const data = typeof webhookData === 'string'
? JSON.parse(webhookData)
: webhookData;
// Validate webhook signature (example with HMAC)
const signature = $input.item.json.headers['x-webhook-signature'];
const crypto = require('crypto');
const secret = 'your-webhook-secret';
const calculatedSignature = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(data))
.digest('hex');
if (signature !== calculatedSignature) {
throw new Error('Invalid webhook signature');
}
return { json: data };
Parsing JSON from File Storage
// Read JSON from file system or cloud storage
const fileContent = $input.item.binary.data;
// Convert buffer to string if needed
const jsonString = fileContent.toString('utf-8');
// Parse JSON
const data = JSON.parse(jsonString);
// Process data
const processed = data.records.map(record => ({
id: record.id,
processed: true,
processedAt: new Date().toISOString()
}));
return processed.map(item => ({ json: item }));
Error Handling and Best Practices
Robust Error Handling
// Comprehensive error handling for JSON parsing
try {
const rawData = $input.item.json.data;
// Validate input exists
if (!rawData) {
throw new Error('No data received');
}
// Parse if string
let data = rawData;
if (typeof rawData === 'string') {
try {
data = JSON.parse(rawData);
} catch (parseError) {
throw new Error(`JSON parse error: ${parseError.message}`);
}
}
// Validate structure
if (!data.hasOwnProperty('results')) {
throw new Error('Invalid data structure: missing results field');
}
// Process data
const results = data.results.map(item => ({
id: item.id || null,
value: item.value || 0,
status: 'processed'
}));
return results.map(item => ({ json: item }));
} catch (error) {
// Return error information for debugging
return [{
json: {
error: true,
message: error.message,
timestamp: new Date().toISOString(),
originalData: $input.item.json
}
}];
}
Safe Property Access
// Use optional chaining to prevent errors
const data = $input.item.json;
// Traditional null checking
const email = data && data.user && data.user.contact && data.user.contact.email;
// Modern optional chaining (Node.js 14+)
const emailModern = data?.user?.contact?.email;
// With nullish coalescing
const emailWithDefault = data?.user?.contact?.email ?? 'no-email@example.com';
// Array safe access
const firstItem = data?.items?.[0];
const firstName = data?.users?.[0]?.name ?? 'Unknown';
Performance Optimization
// Optimize JSON parsing for large datasets
const items = $input.all();
// Use efficient array methods
const processedItems = items
.filter(item => item.json.status === 'active')
.map(item => ({
id: item.json.id,
name: item.json.name,
value: item.json.value
}));
// Batch processing for very large datasets
const BATCH_SIZE = 100;
const results = [];
for (let i = 0; i < items.length; i += BATCH_SIZE) {
const batch = items.slice(i, i + BATCH_SIZE);
const processed = batch.map(item => processItem(item.json));
results.push(...processed);
}
return results.map(item => ({ json: item }));
Data Cleaning and Normalization
// Clean and normalize JSON data
function normalizeData(data) {
return {
id: String(data.id || '').trim(),
name: String(data.name || '').trim(),
email: String(data.email || '').toLowerCase().trim(),
phone: String(data.phone || '').replace(/\D/g, ''),
amount: parseFloat(data.amount) || 0,
date: data.date ? new Date(data.date).toISOString() : null,
tags: Array.isArray(data.tags)
? data.tags.filter(Boolean).map(t => String(t).trim())
: [],
metadata: typeof data.metadata === 'object' ? data.metadata : {}
};
}
const rawData = $input.item.json;
const normalized = normalizeData(rawData);
return { json: normalized };
Integration with Web Scraping
When combining JSON parsing with web scraping workflows, you can use Puppeteer for handling AJAX requests that return JSON data, or parse JSON embedded in HTML pages.
Extracting JSON from API Calls During Scraping
// Monitor network requests and extract JSON responses
// This example shows processing API responses captured during scraping
const networkData = $input.item.json.apiResponses;
const products = networkData
.filter(response => response.url.includes('/api/products'))
.flatMap(response => {
try {
const data = JSON.parse(response.body);
return data.products || [];
} catch {
return [];
}
})
.map(product => ({
id: product.id,
name: product.name,
price: product.price,
availability: product.stock > 0
}));
return products.map(p => ({ json: p }));
Common JSON Parsing Patterns in n8n
Pattern 1: Transform API Response for Database
// Transform API response to database schema
const apiData = $input.item.json;
const dbRecord = {
external_id: apiData.id,
name: apiData.attributes.name,
email: apiData.attributes.email,
status: apiData.attributes.status === 'active' ? 1 : 0,
metadata: JSON.stringify(apiData.metadata),
created_at: new Date(apiData.created_at).toISOString(),
updated_at: new Date().toISOString()
};
return { json: dbRecord };
Pattern 2: Aggregate Multiple JSON Sources
// Combine user data from multiple APIs
const items = $input.all();
const combined = items.reduce((acc, item) => {
const source = item.json.source;
const data = item.json.data;
if (!acc[data.userId]) {
acc[data.userId] = { userId: data.userId };
}
if (source === 'profile') {
acc[data.userId].profile = data;
} else if (source === 'orders') {
acc[data.userId].orders = data.orders;
} else if (source === 'preferences') {
acc[data.userId].preferences = data;
}
return acc;
}, {});
return Object.values(combined).map(user => ({ json: user }));
Pattern 3: Filter and Transform Arrays
// Process large JSON arrays with filtering and transformation
const data = $input.item.json;
const result = data.records
.filter(record => {
// Filter conditions
return record.status === 'active' &&
record.amount > 100 &&
new Date(record.created_at) > new Date('2024-01-01');
})
.map(record => ({
// Transform to desired format
recordId: record.id,
customerName: `${record.customer.firstName} ${record.customer.lastName}`,
totalAmount: record.amount + (record.tax || 0),
category: record.category?.name || 'Uncategorized',
processedDate: new Date().toISOString()
}))
.sort((a, b) => b.totalAmount - a.totalAmount);
return result.map(item => ({ json: item }));
Using WebScraping.AI with JSON Parsing
WebScraping.AI provides structured JSON responses that are perfect for n8n workflows. The API can return HTML, text, or use AI to extract structured data directly as JSON.
HTTP Request Configuration for WebScraping.AI:
{
"method": "GET",
"url": "https://api.webscraping.ai/html",
"qs": {
"api_key": "{{$credentials.webScrapingAI.apiKey}}",
"url": "{{$parameter.targetUrl}}",
"return_page_source": "true"
}
}
Then parse the response with a Code node to extract and structure the data you need.
Debugging JSON Parsing Issues
Issue 1: JSON Parse Errors
Problem: Unexpected token
or Invalid JSON
errors
Solution:
// Validate and clean JSON strings before parsing
function safeJsonParse(str) {
try {
return JSON.parse(str);
} catch (error) {
// Try to fix common issues
const cleaned = str
.replace(/^\uFEFF/, '') // Remove BOM
.replace(/[\u0000-\u0019]+/g, '') // Remove control characters
.trim();
try {
return JSON.parse(cleaned);
} catch (secondError) {
console.error('JSON parse failed:', secondError.message);
console.error('First 200 chars:', str.substring(0, 200));
throw secondError;
}
}
}
const data = safeJsonParse($input.item.json.rawData);
return { json: data };
Issue 2: Undefined Property Access
Problem: Cannot read property of undefined
Solution:
// Use defensive programming
const data = $input.item.json;
// Check existence before access
if (data && data.results && Array.isArray(data.results)) {
const items = data.results.map(item => {
return {
id: item?.id ?? null,
name: item?.attributes?.name ?? 'Unknown',
value: item?.metrics?.value ?? 0
};
});
return items.map(item => ({ json: item }));
} else {
return [{ json: { error: 'Invalid data structure' } }];
}
Issue 3: Type Mismatches
Problem: Expecting object but receiving string or array
Solution:
// Handle different data types gracefully
const input = $input.item.json.data;
let normalized;
if (typeof input === 'string') {
normalized = JSON.parse(input);
} else if (Array.isArray(input)) {
normalized = { items: input };
} else if (typeof input === 'object' && input !== null) {
normalized = input;
} else {
throw new Error(`Unexpected data type: ${typeof input}`);
}
return { json: normalized };
Conclusion
Parsing JSON data in n8n workflows is a fundamental skill that enables you to build powerful automation solutions. Whether you're working with API responses, webhook data, or scraped content, n8n provides flexible tools for parsing, transforming, and restructuring JSON data to meet your needs.
For simple operations, use n8n's expression syntax or the Set node. For complex transformations, leverage the Code node's full JavaScript capabilities. When scraping dynamic websites that return JSON data, consider using techniques for monitoring network requests in Puppeteer to capture API responses directly.
By combining proper error handling, type validation, and efficient data transformation techniques, you can build robust workflows that reliably process JSON data from any source. When dealing with complex single-page applications, WebScraping.AI's API can handle the heavy lifting of rendering and data extraction, returning clean JSON responses ready for processing in your n8n workflows.