Quando i dati arrivano da molte fonti (advertising, CRM, billing, supporto), il valore nasce dalla capacità di unirli in modo coerente. Con aggregazione dati con n8n puoi costruire pipeline ETL/ELT low‑code che estraggono da API eterogenee, normalizzano i payload JSON, eseguono join e deduplica dei record e pubblicano un unico report per marketing e vendite. In questa guida pratica costruirai un’architettura riutilizzabile: dall’ingest tramite HTTP Request con paginazione e autenticazione, alla normalizzazione dei dati JSON e mapping dello schema dati, fino alla scrittura su Google Sheets o DB. Vedrai pattern reali per join e dedup (anche tra array annidati), rate limiting e backoff, e calcolo KPI con Code/Function. Obiettivo: passare da screenshot manuali a report quotidiani affidabili che combinano più API, riducendo errori e tempo uomo. Perfetta per marketer che desiderano orchestrare integrazione API con n8n senza scrivere un backend complesso, mantenendo controllo su performance, costi e governance.

📚 Nuovo a n8n? Parti dalla guida completa: cos'è n8n e come funziona.

[IMG: Panoramica flusso: HTTP Request (API A/B/C) → Set/Code (normalize) → Item Lists (split) → Merge (join) → Code (KPI) → Google Sheets/DB (report)]


Architettura ETL/ELT low‑code con n8n: principi e design

Per unire dati multi‑API serve uno schema e un flusso chiari:

  • Estrai (E): usa HTTP Request per interrogare ogni sorgente (ads, CRM, fatturazione), gestendo query, header, autenticazione e rate limit.
  • Trasforma (T): normalizzazione dei dati JSON e mapping dello schema dati per uniformare campi (es. campaignId, date, spend, leads, revenue).
  • Carica (L): scrivi il risultato in un’unica tabella (Google Sheets, Airtable o DB) e alimenta dashboard BI.

Componenti tipici

  • HTTP Request, Item Lists (splitIntoItems), Set (keepOnlySet), Code/Function (functionCode), Merge (mergeByKey), Split In Batches (batchSize), IF/Switch per routing condizionale, Google Sheets (append).
  • Scelta ELT vs ETL: ELT spinge i dati quasi raw a un DB/warehouse e trasforma a valle; ETL normalizza già in n8n. Per marketer, ETL low‑code spesso è più veloce da mettere in produzione.

Linee guida

  • Progetta upfront lo schema “reportReady”: campi coerenti tra sorgenti, tipizzazioni, chiavi di join, granularità temporale (giorno/settimana).
  • Inserisci metadati: source, fetchTimestamp, version per audit e tracciabilità.
  • Versiona i workflow: una modifica al mapping non deve “rompere” i report esistenti.

[IMG: Schema logico: API raw → Normalize → Join → KPI → Report]


Ingest da API multiple: HTTP Request, credenziali e paginazione

Il nodo “n8n-nodes-base.httpRequest” è il cavallo di battaglia per integrazione API con n8n. Parametri chiave (esatti) da configurare nel JSON del nodo:

  • url
  • method
  • responseFormat
  • jsonParameters
  • queryParametersUi
  • headerParametersUi
  • bodyParametersUi o bodyParametersJson (se invii payload JSON)
  • options (es. bodyContentType: “json”)
  • credentials (se usi auth gestite)

Esempio GET con query e header

{
  "name": "Fetch Ads",
  "type": "n8n-nodes-base.httpRequest",
  "typeVersion": 1,
  "parameters": {
    "url": "https://api.example-ads.com/v1/spend",
    "method": "GET",
    "responseFormat": "json",
    "queryParametersUi": {
      "parameter": [
        { "name": "date", "value": "={{ $json.reportDate }}" },
        { "name": "page", "value": "={{ $json.page || 1 }}" },
        { "name": "limit", "value": "100" }
      ]
    },
    "headerParametersUi": {
      "parameter": [
        { "name": "Authorization", "value": "Bearer {{ $json.apiToken }}" }
      ]
    },
    "jsonParameters": true,
    "options": { "bodyContentType": "json" }
  }
}

Gestione paginazione delle API

  • Non esiste un parametro “pagination” dedicato: implementa loop variando “page” (o “cursor”) via queryParametersUi.
  • Pattern consigliato:
    1) Set iniziale con page=1.
    2) HTTP Request → If “items.length > 0”.
    3) Item Lists (splitIntoItems) per elaborare i record.
    4) Merge/Append in una lista cumulativa o scrittura batch.
    5) Incrementa page e ripeti finché la risposta è vuota.

Idempotenza e resilienza

  • Passa un “fetchId” (es. data + sorgente) per evitare duplicati in scrittura.
  • Se l’API supporta etag o since/updated_after, preferiscili a page/offset per job incrementali.

[IMG: Loop paginazione: Set(page=1) → HTTP → IF (hasItems) → split → aggregate/append → page++ → loop]


Normalizzazione dei dati JSON e mapping dello schema

Prima di unire le fonti, porta ogni payload a uno schema coerente.

Set node come “mapper visivo”

  • Parametri chiave:
  • keepOnlySet: true per mantenere solo i campi mappati.
  • values: definisci i nuovi campi, tipizzati.
  • removeFields: opzionale per eliminare campi indesiderati.

Esempio Set (mapping minimo)

{
  "name": "Normalize Ads Row",
  "type": "n8n-nodes-base.set",
  "typeVersion": 3,
  "parameters": {
    "keepOnlySet": true,
    "values": {
      "string": [
        { "name": "source", "value": "example-ads" },
        { "name": "campaignId", "value": "={{ $json.campaign_id }}" },
        { "name": "date", "value": "={{ $json.date }}" }
      ],
      "number": [
        { "name": "spend", "value": "={{ +$json.spend_cents/100 }}" },
        { "name": "clicks", "value": "={{ +$json.clicks }}" }
      ]
    }
  }
}

Code node per trasformazioni custom

  • functionCode utile per normalizzare formati, calcolare campi derivati, validare valori:
// Run Once For Each Item
const row = $json;
row.cpc = row.clicks ? +(row.spend / row.clicks).toFixed(2) : 0;
row.fetchTimestamp = $now; // timestamp corrente
return [{ json: row }];

Best practice di normalizzazione dei dati JSON

  • Tipizza subito numeri e date (evita stringhe ambigue).
  • Uniforma i nomi dei campi tra fonti (campaignId, date, spend, leads, revenue).
  • Aggiungi “source” per tracciare la provenienza e facilitare debug/join.

[IMG: Set (keepOnlySet) → Code (derive fields) con anteprima campi normalizzati]


Join e deduplica dei record tra fonti

Per unire più dataset (es. Ads + CRM + Billing):

  • Allinea la granularità: per data e campaignId (o altra chiave).
  • Esegui join e deduplica dei record con i nodi Item Lists e Merge.

Item Lists per preparare i dataset

  • splitIntoItems: trasforma un array di record in item singoli per elaborazioni per‑riga.
{
  "name": "Split CRM Leads",
  "type": "n8n-nodes-base.itemLists",
  "typeVersion": 1,
  "parameters": {
    "operation": "splitIntoItems",
    "property": "results"
  }
}
  • aggregateItems: ricompone più item in un singolo array quando necessario.

Merge node per join

  • mergeByKey con propertyName per unire per chiave (es. campaignId+date).
{
  "name": "Join Ads + CRM",
  "type": "n8n-nodes-base.merge",
  "typeVersion": 1,
  "parameters": {
    "mode": "mergeByKey",
    "propertyName": "joinKey",
    "outputDataFrom": "both"
  }
}

Suggerimento: crea “joinKey” prima del Merge (es. “{{ $json.campaignId }}|{{ $json.date }}”).

Deduplica

  • In caso di duplicati (es. CRM con più lead sulla stessa chiave), consolida con Code:
// Run Once For All Items: deduplica per joinKey sommando metriche
const map = new Map();
for (const item of items) {
  const r = item.json;
  const k = r.joinKey;
  const acc = map.get(k) || { ...r, leads: 0, revenue: 0 };
  acc.leads += +r.leads || 0;
  acc.revenue += +r.revenue || 0;
  map.set(k, acc);
}
return Array.from(map.values()).map(v => ({ json: v }));

[IMG: Normalize (A/B/C) → Set(joinKey) → Merge by Key → Code(dedup/sum) → Report]


Calcolo KPI e arricchimento: dal dataset al report

Dopo il join, calcola KPI utili per marketing:

  • CPC = spend / clicks
  • CPL = spend / leads
  • ROAS = revenue / spend
  • Conversion rate = leads / clicks

Esempio Code (calcolo KPI)

// Run Once For Each Item
const r = $json;
const safe = (n) => (isFinite(n) && !isNaN(n)) ? n : 0;
r.cpc = safe(r.spend) && safe(r.clicks) ? +(r.spend / r.clicks).toFixed(2) : 0;
r.cpl = safe(r.spend) && safe(r.leads) ? +(r.spend / r.leads).toFixed(2) : 0;
r.roas = safe(r.spend) ? +(safe(r.revenue) / r.spend).toFixed(2) : 0;
return [{ json: r }];

Regole e segmentazione

  • Usa Switch per segmentare per canale/paese:
{
  "name": "Segment By Channel",
  "type": "n8n-nodes-base.switch",
  "typeVersion": 1,
  "parameters": {
    "dataType": "string",
    "value1": "={{ $json.source }}",
    "rules": { "rules": [
      { "operation": "equal", "value2": "example-ads" },
      { "operation": "equal", "value2": "example-crm" }
    ] },
    "fallbackOutput": 2
  }
}
  • IF per filtri qualità dati (es. escludi linee senza date/campaignId):
{
  "name": "Has Required Fields",
  "type": "n8n-nodes-base.if",
  "typeVersion": 2,
  "parameters": {
    "conditions": {
      "string": [
        {
          "value1": "={{ $json.campaignId }}",
          "operation": "isEmpty"
        }
      ]
    },
    "combineOperation": "and"
  }
}

(Usa operatori coerenti con la tua versione; in alternativa verifica con Code e droppa l’item.)

[IMG: Code(KPI) → Switch(segment) → IF(filter) → Append]


Scrittura del report: Google Sheets, Airtable o DB

Google Sheets: append automatico

{
  "name": "Append Report",
  "type": "n8n-nodes-base.googleSheets",
  "typeVersion": 4,
  "parameters": {
    "operation": "append",
    "spreadsheetId": "1A2b3C4D5E6FgHiJkLMnoPQrstu",
    "sheetName": "Campaign_Report",
    "dataMode": "autoMap",
    "options": { "valueInputMode": "USER_ENTERED" }
  },
  "credentials": {
    "googleSheetsOAuth2Api": { "name": "Google Sheets OAuth2" }
  }
}

Alternative

  • Airtable: tabella con schema “reportReady”.
  • DB: più robusto per storici (MySQL/Postgres) e BI. In questo caso, valuta ELT: carica raw e trasforma con viste/materialized views.

Consigli pratici

  • Mappa le colonne con header coerenti: date, campaignId, source, spend, clicks, leads, revenue, cpc, cpl, roas.
  • Appendi solo i record del giorno/periodo corrente; per ricalcoli completi (backfill) esegui job separati.

[IMG: Foglio “Campaign_Report” con colonne mappate automaticamente]


Rate limiting, backoff e performance

Rate limiting e backoff

  • Throttling per API “sensibili”: usa Split In Batches con batchSize per limitare le chiamate simultanee.
{
  "name": "Batch Loop",
  "type": "n8n-nodes-base.splitInBatches",
  "typeVersion": 1,
  "parameters": { "batchSize": 10 }
}
  • Tra un batch e l’altro, inserisci una breve pausa (Code o nodo di attesa) e riprendi il loop finché ci sono items.
  • In caso di errori/429, implementa backoff crescente (2s → 5s → 10s) e ritenta.

Gestione paginazione efficiente

  • Interrompi il loop quando la risposta non ritorna più elementi.
  • Usa parametri “updated_after” quando disponibili per job incrementali.

Ottimizzazioni

  • Riduci i campi al minimo prima di Merge/Sheets (Set keepOnlySet).
  • Esegui calcoli KPI “Run Once For Each Item” per ridurre la memoria.
  • Evita log ridondanti: conserva solo il necessario per audit.

[IMG: SplitInBatches(10) → HTTP → (retry/backoff) → Append/Join → next batch]


Esempio end‑to‑end: report giornaliero Ads + CRM + Billing

Obiettivo: unire spend (Ads), leads (CRM), revenue (Billing) per campaignId e date.

Step‑by‑step
1) Inizializza

  • Set: reportDate = oggi, page=1, apiToken da credenziali (o variabili).
  • [IMG: Nodo Set iniziale con costanti job]

2) Ingest Ads

  • HTTP Request (GET) con queryParametersUi (date, page, limit), headerParametersUi (Authorization).
  • Loop paginato fino a esaurimento.
  • Item Lists splitIntoItems su “data” (array di righe).
  • Set keepOnlySet: source, campaignId, date, spend, clicks.

3) Ingest CRM

  • HTTP Request su endpoint leads per date.
  • splitIntoItems, Set => source=crm, campaignId, date, leads.
  • Deduplica per campaignId/date con Code (somma leads).

4) Ingest Billing

  • HTTP Request su fatture pagate per date.
  • splitIntoItems, Set => source=billing, campaignId, date, revenue.
  • Deduplica revenue per campaignId/date.

5) Join

  • Per ogni dataset, crea joinKey = “{{ $json.campaignId }}|{{ $json.date }}”.
  • Merge by Key (Ads + CRM), poi Merge by Key (risultato + Billing) con:
{
  "parameters": {
    "mode": "mergeByKey",
    "propertyName": "joinKey",
    "outputDataFrom": "both"
  }
}

6) KPI e filtri

  • Code: calcola cpc, cpl, roas.
  • IF/Switch: filtra righe incomplete o segmenta per mercato.

7) Report

  • Google Sheets (append) su Campaign_Report con dataMode: autoMap.
  • Aggiungi fetchTimestamp = $now per audit.

8) Osservabilità

  • Log sintetico: righe processate, sorgenti chiamate, durata job.
  • Notifica Slack finale con conteggi record.

[IMG: Canvas completo con nodi principali e rami di join]


Osservabilità, governance e versioning

  • Logging: salva contatori per source (righe lette/scritte), errori per endpoint, tempo totale job.
  • Alert: Slack in caso di errori ripetuti o risposte 5xx.
  • Versioning: duplica workflow per nuove versioni; annota in header foglio la “schema_version”.
  • Sicurezza: credenziali delle API nelle “credentials” n8n, variabili d’ambiente per segreti; limita permessi in scrittura.

Scalabilità

  • Quando crescono le fonti, replica il pattern: una fase di ingest/normalize per source, poi join per chiave, KPI e scrittura.
  • Passa da Sheets a DB quando i volumi aumentano, mantenendo lo stesso schema concettuale.

[IMG: Dashboard KPI di job con record processati e time per fase]


Quick Takeaways

  • Progetta uno schema “reportReady” e usa Set/Code per normalizzare le fonti prima del join.
  • Implementa la gestione paginazione delle API con loop su page/limit via queryParametersUi.
  • Esegui join con Merge (mode: mergeByKey, propertyName: joinKey) e deduplica con Code aggregando metriche.
  • Applica rate limiting e backoff con Split In Batches (batchSize) e retry controllati su errori/429.
  • Pubblica il report con Google Sheets (append) o DB e aggiungi KPI (CPC, CPL, ROAS) in Code.
  • Monitora: logging sintetico, alert Slack, versioning del workflow per modifiche sicure.

Conclusione

L’aggregazione dati con n8n ti permette di creare un layer di integrazione API low‑code rapido da rilasciare e semplice da mantenere. Con pochi nodi chiave — HTTP Request, Set, Item Lists, Merge, Code e Google Sheets — trasformi payload diversi in un report unico e coerente. La chiave è progettare lo schema a monte, normalizzare presto e unire con chiavi stabili, aggiungendo KPI utili per decisioni quotidiane. Con rate limiting, paginazione e deduplica riduci errori e costi, mentre logging e versioning assicurano governance. Inizia con due sorgenti e poche metriche core, poi estendi a nuove API e breakdown (canali, paesi, segmenti). In poche iterazioni avrai un’unica vista affidabile di spend, lead e ricavi, pronta per BI e pianificazione. Se vuoi fare il salto di qualità nella produttività marketing, imposta oggi il primo workflow: domani non tornerai più agli export manuali.


FAQ

1) Come gestisco più pagine di un’API in n8n?
Usa queryParametersUi per passare page/limit e implementa un loop: incrementa “page” finché la risposta non ritorna più record. Puoi orchestrare il ciclo con IF e Set, e processare i risultati con Item Lists (splitIntoItems).

2) Come unisco dati di diverse fonti in un’unica riga?
Crea una joinKey (es. campaignId|date) con Set/Code e usa il Merge node con mode: “mergeByKey”, propertyName: “joinKey”, outputDataFrom: “both”. Prima del join, assicurati la normalizzazione dei dati JSON.

3) Come evito duplicati dopo il join?
Applica join e deduplica dei record con un Code “Run Once For All Items” aggregando per joinKey (somma leads/revenue). In alternativa, pulisci i duplicati a monte in ciascuna fonte.

4) Come rispetto i limiti delle API durante l’ingest?
Usa Split In Batches con batchSize adeguato, inserisci pause tra i batch e implementa rate limiting e backoff: in caso di 429/5xx, ritenta con delay progressivo e limita la concorrenza.

5) È meglio scrivere su Google Sheets o su un database?
Per volumi piccoli e prototipi, Google Sheets con operation: “append” e dataMode: “autoMap” è rapido. Per storici e query avanzate, passa a un DB (MySQL/Postgres) mantenendo lo stesso mapping dello schema dati.


Ci dai una mano?

Quale combinazione di API vuoi unire per prima (Ads + CRM, CRM + Billing, altro)? Raccontaci il tuo caso e condividi questo articolo con il team: confrontiamo setup e pattern per costruire insieme report più completi e affidabili!

Vuoi automazioni AI su misura per la tua azienda?
Scopri la consulenza →