DEV Community

Alessandro Binda
Alessandro Binda

Posted on

API Dati Aziendali: Guida Tecnica per Sviluppatori che Lavorano con il Registro Imprese Italiano

Se hai mai tentato di accedere programmaticamente ai dati del Registro Imprese italiano, sai già che l'esperienza è... migliorabile. Questa guida documenta il landscape reale delle API disponibili, i workaround che funzionano in produzione, e come strutturare un'integrazione robusta.

Il Panorama delle Fonti Dati Ufficiali

Infocamere / Telemaco

È il sistema ufficiale di accesso ai dati delle Camere di Commercio italiane. Tecnicamente capace, ma costruito pensando a notai, avvocati e commercialisti — non agli sviluppatori.

Cosa offre:

  • Visure camerali complete (dati anagrafici, soci, amministratori, capitale)
  • Bilanci in formato XBRL
  • Atti depositati
  • Procedure concorsuali

Accesso:

  • Prevede registrazione professionale o rivenditori accreditati
  • Pricing per query (non per piano mensile)
  • Documentazione tecnica disponibile ma datata
  • Non esiste un ambiente sandbox

Per uno sviluppatore che vuole prototipare velocemente, questa non è la strada.

OpenAPI del MISE (Ministero delle Imprese)

Il Ministero ha pubblicato alcune API per l'accesso a dati aggregati e statistici, ma non per query individuali su singole aziende.

API di terze parti (aggregatori)

La strada pratica per la maggior parte degli use case è lavorare con un aggregatore che ha già costruito il layer di normalizzazione sopra le fonti ufficiali. L'API S.C.A.L.A. score è una di queste, con un free tier per il prototyping.

Schema della Risposta API

{
  "company_name": "Rossi e Figli Srl",
  "vat_number": "IT08765432109",
  "fiscal_code": "08765432109",
  "status": "active",
  "legal_form": "Srl",
  "ateco_code": "43.21.01",
  "ateco_description": "Installazione di impianti elettrici",
  "registered_address": {
    "street": "Via Roma 42",
    "city": "Milano",
    "province": "MI",
    "region": "Lombardia",
    "zip": "20121"
  },
  "founding_date": "2008-03-15",
  "employees_range": "10-49",
  "revenue": 2340000,
  "last_filing_year": 2023,
  "score": 71,
  "risk_category": "low",
  "financial_summary": {
    "total_assets": 1850000,
    "total_liabilities": 920000,
    "equity": 930000,
    "net_result": 145000
  },
  "data_confidence": 0.87,
  "last_updated": "2026-04-12"
}
Enter fullscreen mode Exit fullscreen mode

Validazione della Partita IVA Prima della Query

Risparmia quota API e gestisci gli errori in modo più chiaro validando il formato della P.IVA prima di chiamare l'API.

function validateItalianVAT(vat) {
  // Normalizza: rimuovi IT prefix se presente, rimuovi spazi
  const normalized = vat.replace(/^IT/i, '').replace(/\s/g, '');

  // Deve essere esattamente 11 cifre
  if (!/^\d{11}$/.test(normalized)) {
    return { valid: false, error: 'Formato non valido: attese 11 cifre numeriche' };
  }

  // Checksum (algoritmo Luhn adattato per P.IVA italiana)
  let sum = 0;
  for (let i = 0; i < 10; i++) {
    const digit = parseInt(normalized[i]);
    if (i % 2 === 0) {
      sum += digit;
    } else {
      const doubled = digit * 2;
      sum += doubled > 9 ? doubled - 9 : doubled;
    }
  }
  const checkDigit = (10 - (sum % 10)) % 10;

  if (checkDigit !== parseInt(normalized[10])) {
    return { valid: false, error: 'Checksum non valido' };
  }

  return { valid: true, normalized: `IT${normalized}` };
}
Enter fullscreen mode Exit fullscreen mode

Pattern di Integrazione per Casi d'Uso Comuni

Autofill del Form di Registrazione

async function autofillFromVAT(vatNumber) {
  const validation = validateItalianVAT(vatNumber);
  if (!validation.valid) {
    return { error: validation.error };
  }

  try {
    const res = await fetch(
      `https://api.get-scala.com/score/company?vat=${validation.normalized}`,
      { headers: { Authorization: `Bearer ${process.env.SCALA_API_KEY}` } }
    );

    if (!res.ok) throw new Error(`API error: ${res.status}`);
    const data = await res.json();

    return {
      companyName: data.company_name,
      address: data.registered_address,
      ateco: data.ateco_code,
      employees: data.employees_range,
      status: data.status
    };
  } catch (err) {
    console.error('Company lookup failed:', err);
    return null; // Graceful degradation — lascia l'utente compilare manualmente
  }
}
Enter fullscreen mode Exit fullscreen mode

Screening di una Lista Fornitori

async function screenSuppliers(suppliers, riskThreshold = 40) {
  const BATCH_SIZE = 10;
  const results = [];

  for (let i = 0; i < suppliers.length; i += BATCH_SIZE) {
    const batch = suppliers.slice(i, i + BATCH_SIZE);

    const batchResults = await Promise.allSettled(
      batch.map(async (supplier) => {
        const res = await fetch(
          `https://api.get-scala.com/score/company?vat=${supplier.vat}`,
          { headers: { Authorization: `Bearer ${process.env.SCALA_API_KEY}` } }
        );
        const data = await res.json();
        return {
          ...supplier,
          score: data.score,
          riskCategory: data.risk_category,
          flagged: data.score < riskThreshold
        };
      })
    );

    results.push(...batchResults
      .filter(r => r.status === 'fulfilled')
      .map(r => r.value)
    );

    // Rispetta i rate limit — pausa tra batch
    if (i + BATCH_SIZE < suppliers.length) {
      await new Promise(resolve => setTimeout(resolve, 200));
    }
  }

  return {
    total: results.length,
    flagged: results.filter(r => r.flagged),
    clean: results.filter(r => !r.flagged)
  };
}
Enter fullscreen mode Exit fullscreen mode

Gestione della Qualità dei Dati

I dati del Registro Imprese hanno alcune caratteristiche specifiche da gestire:

Bilanci non ancora depositati: Le aziende hanno 6-7 mesi dalla chiusura dell'esercizio per depositare il bilancio. last_filing_year: 2022 in un'API call di maggio 2026 non significa necessariamente che l'azienda sia inattiva — potrebbe avere il 2023 in attesa di deposito.

function assessDataFreshness(lastFilingYear) {
  const currentYear = new Date().getFullYear();
  const lag = currentYear - lastFilingYear;

  if (lag <= 1) return 'fresh';
  if (lag === 2) return 'acceptable'; // probabile deposito in corso
  if (lag === 3) return 'stale';      // attenzione
  return 'very_stale';                // segnale negativo significativo
}
Enter fullscreen mode Exit fullscreen mode

Campo revenue nullo: Le microimprese possono depositare bilanci in forma abbreviata senza esporre il fatturato. Un campo revenue: null non è un errore — è un dato mancante per ragioni di legge.

Aziende in liquidazione: status: "in_liquidation" non significa insolvenza immediata, ma richiede attenzione. La liquidazione volontaria è normale alla fine della vita di un'azienda; quella forzata è un segnale negativo.

Caching e Rate Limiting

La maggior parte dei dati aziendali cambia lentamente. Un TTL di 24 ore è appropriato per la maggior parte degli use case:

const cache = new Map();

async function getCachedScore(vatNumber) {
  const cached = cache.get(vatNumber);
  if (cached && Date.now() - cached.timestamp < 24 * 60 * 60 * 1000) {
    return cached.data;
  }

  const data = await fetchFromAPI(vatNumber);
  cache.set(vatNumber, { data, timestamp: Date.now() });
  return data;
}
Enter fullscreen mode Exit fullscreen mode

In produzione, usa Redis con TTL nativo invece di una Map in memoria.


La documentazione completa dell'API S.C.A.L.A. score e il free tier sono disponibili su get-scala.com/score/. Registrazione su get-scala.com.

Top comments (0)