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.
// 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.
// 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.
// 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.
// 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
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
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
Stripe: high (5G, 3R)
Scavio: high (4G, 2R)
Unknown: low (0G, 0R)
Cost: $0.030