Table of contents

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.

Try WebScraping.AI for Your Web Scraping Needs

Looking for a powerful web scraping solution? WebScraping.AI provides an LLM-powered API that combines Chromium JavaScript rendering with rotating proxies for reliable data extraction.

Key Features:

  • AI-powered extraction: Ask questions about web pages or extract structured data fields
  • JavaScript rendering: Full Chromium browser support for dynamic content
  • Rotating proxies: Datacenter and residential proxies from multiple countries
  • Easy integration: Simple REST API with SDKs for Python, Ruby, PHP, and more
  • Reliable & scalable: Built for developers who need consistent results

Getting Started:

Get page content with AI analysis:

curl "https://api.webscraping.ai/ai/question?url=https://example.com&question=What is the main topic?&api_key=YOUR_API_KEY"

Extract structured data:

curl "https://api.webscraping.ai/ai/fields?url=https://example.com&fields[title]=Page title&fields[price]=Product price&api_key=YOUR_API_KEY"

Try in request builder

Related Questions

Get Started Now

WebScraping.AI provides rotating proxies, Chromium rendering and built-in HTML parser for web scraping
Icon