Tutorial

How to Build an n8n Lead Enrichment Normalizer

Build an n8n workflow that enriches leads with live search data and normalizes JSON into consistent schemas. No more broken automations.

n8n workflows break when enrichment APIs return inconsistent JSON: one lead has a phone field, the next does not, a third returns null instead of empty string. This tutorial builds an n8n-compatible enrichment normalizer that queries the Scavio API for company context, then normalizes every response into a guaranteed schema with no missing fields.

Prerequisites

  • n8n installed (self-hosted or cloud)
  • A Scavio API key from scavio.dev
  • Basic familiarity with n8n HTTP Request nodes

Walkthrough

Step 1: Set up the HTTP Request node for Scavio

Configure an n8n HTTP Request node to call the Scavio API for each lead's company.

JavaScript
// n8n HTTP Request Node:
// Method: POST
// URL: https://api.scavio.dev/api/v1/search
// Headers: x-api-key: {{ $env.SCAVIO_API_KEY }}
// Body: { "query": "{{ $json.company_name }} company", "country_code": "us" }

// Python equivalent for testing:
import os, requests
API_KEY = os.environ['SCAVIO_API_KEY']

def enrich_company(name):
    return requests.post('https://api.scavio.dev/api/v1/search',
        headers={'x-api-key': API_KEY, 'Content-Type': 'application/json'},
        json={'query': f'{name} company', 'country_code': 'us'}).json()

print(f"Results: {len(enrich_company('Scavio').get('organic_results', []))}")

Step 2: Normalize the response into a guaranteed schema

Use an n8n Function node to transform raw responses into objects with guaranteed fields.

JavaScript
// n8n Function Node after HTTP Request
const raw = $input.first().json;
const results = raw.organic_results || [];
const top = results[0] || {};
const kg = raw.knowledge_graph || {};

return [{ json: {
  company_name: $('Trigger').first().json.company_name || '',
  lead_email: $('Trigger').first().json.email || '',
  website: top.link || '',
  description: top.snippet || '',
  serp_title: top.title || '',
  kg_description: kg.description || '',
  kg_type: kg.type || '',
  total_results: results.length,
  enriched_at: new Date().toISOString(),
  has_data: results.length > 0,
}}];

Step 3: Add Reddit context enrichment

Chain a second HTTP Request to search Reddit for the company, adding sentiment context.

JavaScript
// Second HTTP Request: POST https://api.scavio.dev/api/v1/search
// Body: { "query": "{{ $json.company_name }} review", "platform": "reddit", "country_code": "us" }

// Reddit Normalizer Function Node:
const raw = $input.first().json;
const results = raw.organic_results || [];
const prev = $('Google Normalizer').first().json;

return [{ json: {
  ...prev,
  reddit_mentions: results.length,
  reddit_top_title: results[0]?.title || '',
  reddit_top_snippet: results[0]?.snippet || '',
  has_reddit_presence: results.length > 0,
}}];

Step 4: Route leads by enrichment quality

Use IF nodes to send well-enriched leads to outreach and weak ones to manual review.

JavaScript
// n8n IF Node:
// Condition: {{ $json.has_data }} equals true AND {{ $json.total_results }} >= 3
// True -> auto-route to CRM
// False -> manual review queue

// Python routing equivalent:
def route_lead(enriched):
    if enriched['has_data'] and enriched['total_results'] >= 3:
        return 'high_confidence' if enriched['has_reddit_presence'] else 'medium_confidence'
    return 'manual_review'

for lead in [{'company_name': 'Stripe', 'has_data': True, 'total_results': 5, 'has_reddit_presence': True},
             {'company_name': 'Unknown', 'has_data': False, 'total_results': 0, 'has_reddit_presence': False}]:
    print(f"{lead['company_name']}: {route_lead(lead)}")

Python Example

Python
import os, requests

API_KEY = os.environ['SCAVIO_API_KEY']
H = {'x-api-key': API_KEY, 'Content-Type': 'application/json'}

def enrich(company):
    g = requests.post('https://api.scavio.dev/api/v1/search',
        headers=H, json={'query': f'{company} company', 'country_code': 'us'}).json()
    r = requests.post('https://api.scavio.dev/api/v1/search',
        headers=H, json={'query': f'{company} review', 'platform': 'reddit', 'country_code': 'us'}).json()
    gr = g.get('organic_results', [])
    rr = r.get('organic_results', [])
    return {'company': company, 'website': gr[0]['link'] if gr else '', 'google': len(gr),
            'reddit': len(rr), 'confidence': 'high' if len(gr) >= 3 and rr else 'low'}

for c in ['Stripe', 'Scavio', 'Unknown']:
    e = enrich(c)
    print(f"{e['company']}: {e['confidence']} ({e['google']}G, {e['reddit']}R)")

JavaScript Example

JavaScript
const API_KEY = process.env.SCAVIO_API_KEY;
const H = { 'x-api-key': API_KEY, 'Content-Type': 'application/json' };

async function enrich(company) {
  const [g, r] = await Promise.all([
    fetch('https://api.scavio.dev/api/v1/search', {
      method: 'POST', headers: H,
      body: JSON.stringify({ query: `${company} company`, country_code: 'us' })
    }).then(r => r.json()),
    fetch('https://api.scavio.dev/api/v1/search', {
      method: 'POST', headers: H,
      body: JSON.stringify({ query: `${company} review`, platform: 'reddit', country_code: 'us' })
    }).then(r => r.json()),
  ]);
  const gr = g.organic_results || [];
  const rr = r.organic_results || [];
  console.log(`${company}: ${gr.length}G, ${rr.length}R`);
}
(async () => { for (const c of ['Stripe', 'Scavio']) await enrich(c); })();

Expected Output

JSON
Stripe: high (5G, 3R)
Scavio: high (4G, 2R)
Unknown: low (0G, 0R)
Cost: $0.030

Related Tutorials

Frequently Asked Questions

Most developers complete this tutorial in 15 to 30 minutes. You will need a Scavio API key (free tier works) and a working Python or JavaScript environment.

n8n installed (self-hosted or cloud). A Scavio API key from scavio.dev. Basic familiarity with n8n HTTP Request nodes. A Scavio API key gives you 250 free credits per month.

Yes. The free tier includes 250 credits per month, which is more than enough to complete this tutorial and prototype a working solution.

Scavio has a native LangChain package (langchain-scavio), an MCP server, and a plain REST API that works with any HTTP client. This tutorial uses the raw REST API, but you can adapt to your framework of choice.

Start Building

Build an n8n workflow that enriches leads with live search data and normalizes JSON into consistent schemas. No more broken automations.