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"
}
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}` };
}
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
}
}
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)
};
}
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
}
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;
}
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)