Ca să integrezi „CUI → date firmă” într-o aplicație, validezi CUI-ul local cu cifra de control, apoi faci de pe serverul tău o cerere GET la https://bazadate.ro/api/v1/firma/{cui} cu cheia în antetul Authorization: Bearer și primești în JSON denumirea oficială, numărul de la Registrul Comerțului, adresa, codurile CAEN și statutul ANAF (TVA, TVA la încasare, inactiv, e-Factura). Restul ține de detalii: tratarea erorilor, cache și limitele de cereri.
Ghidul este scris pentru programatori care construiesc un formular de facturare, un CRM intern sau un proces de onboarding pentru clienți firme. Documentația completă și cheia API sunt pe pagina API date firme.
Cazul de utilizare: autocompletarea formularului de facturare
Scenariul cel mai frecvent: un client își face cont pe site-ul tău și are nevoie de factură pe firmă. În loc să-i ceri șase câmpuri (denumire, CUI, nr. Registrul Comerțului, adresă, județ, dacă e plătitor de TVA), îi ceri doar CUI-ul. Fluxul arată așa:
- utilizatorul scrie CUI-ul (cu sau fără prefixul RO);
- în browser verifici formatul și cifra de control, ca să semnalezi imediat o greșeală de tastare;
- browserul trimite CUI-ul către propriul tău backend;
- backendul verifică cache-ul, iar dacă nu are datele, apelează API-ul BazaDate.ro cu cheia secretă;
- backendul întoarce browserului doar câmpurile de care formularul are nevoie;
- utilizatorul confirmă datele precompletate.
Apelul se face obligatoriu de pe server: API-ul nu are CORS activat pentru browsere, iar cheia ta nu trebuie să ajungă niciodată în codul JavaScript public. Același flux îl poți folosi și pentru îmbogățirea datelor existente, descrisă în articolul data enrichment explicat.
Validarea CUI-ului cu cifra de control
CUI-ul românesc are între 2 și 10 cifre, iar ultima cifră este o cifră de control calculată din celelalte. Algoritmul folosește cheia de testare 753217532:
- elimini prefixul RO și spațiile;
- separi ultima cifră (cifra de control) de restul;
- completezi restul cifrelor cu zerouri la stânga până la 9 cifre;
- înmulțești fiecare cifră cu cifra de pe aceeași poziție din cheie și aduni rezultatele;
- înmulțești suma cu 10 și calculezi restul împărțirii la 11;
- dacă restul este 10, cifra de control corectă este 0; altfel este chiar restul.
Exemplu real, CUI 160796 (AUTOMOBILE DACIA SA): cifrele fără ultima sunt 16079, completate devin 000016079. Produsele cu cheia 7-5-3-2-1-7-5-3-2 sunt 0, 0, 0, 0, 1, 42, 0, 21, 18, cu suma 82. 82 × 10 = 820, iar 820 mod 11 = 6. Ultima cifră a CUI-ului este 6, deci CUI-ul este valid.
În JavaScript, funcția arată așa:
function cuiValid(valoare) {
var s = String(valoare).trim().toUpperCase().replace(/^RO/, '').replace(/\s+/g, '');
if (!/^[0-9]{2,10}$/.test(s)) return false;
var cheie = '753217532';
var control = Number(s.slice(-1));
var corp = s.slice(0, -1).padStart(9, '0');
var suma = 0;
for (var i = 0; i < 9; i++) {
suma += Number(corp[i]) * Number(cheie[i]);
}
var rest = (suma * 10) % 11;
if (rest === 10) rest = 0;
return rest === control;
}
Validarea locală nu garantează că firma există sau că este activă; confirmă doar că numărul nu are o greșeală de tastare. API-ul face oricum aceeași verificare și răspunde cu 400 fără să taxeze cererea, dar e mai rapid și mai ieftin să oprești greșelile înainte.
Apelul de pe server: Node.js și PHP
Node.js (fetch nativ, Node 18+)
async function dateFirma(cui) {
const raspuns = await fetch('https://bazadate.ro/api/v1/firma/' + cui, {
headers: { Authorization: 'Bearer ' + process.env.BAZADATE_API_KEY },
signal: AbortSignal.timeout(10000),
});
if (raspuns.status === 404) return null;
if (!raspuns.ok) {
const eroare = await raspuns.json().catch(function () { return {}; });
throw new Error('BazaDate ' + raspuns.status + ': ' + (eroare.cod || 'necunoscut'));
}
return raspuns.json();
}
PHP (cURL)
function dateFirma($cui) {
$ch = curl_init('https://bazadate.ro/api/v1/firma/' . $cui);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer ' . getenv('BAZADATE_API_KEY')]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$corp = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status === 404) return null;
if ($status !== 200) throw new Exception('BazaDate ' . $status);
return json_decode($corp, true);
}
Cheia se poate trimite și în antetul x-api-key, dacă platforma ta preferă asta. Păstreaz-o într-o variabilă de mediu, nu în cod și nu în depozitul git.
Ce primești în răspuns
Răspunsul JSON conține, printre altele: cui, denumire, regCom, euid, forma, stare, dataInmatriculare, un obiect adresa (județ, localitate, adresă, cod poștal), caenPrincipal și lista caenAutorizate (cu versiunea nomenclatorului, rev. 2 sau rev. 3), un obiect anaf (platitorTva, tvaIncasare, splitTva, inactiv, eFactura, organFiscal, cu datele aferente) și anafActualizatLa, data la care statutul ANAF a fost citit ultima dată. Pentru formularul de facturare folosești de obicei denumire, regCom, adresa și anaf.platitorTva (ca să știi dacă pui prefixul RO la codul fiscal). Structura exactă și exemple complete sunt în documentația de pe pagina API.
Tratarea erorilor
Erorile au un corp JSON cu un câmp eroare (mesaj lizibil) și un câmp cod (identificator stabil pe care îl poți testa în cod).
| Cod HTTP | Când apare | Se taxează? | Ce faci |
|---|---|---|---|
| 400 | CUI invalid (cifra de control nu se potrivește) | Nu | Ceri utilizatorului să verifice CUI-ul |
| 401 | Cheie lipsă, invalidă sau revocată | Nu | Verifici configurarea serverului; alertă internă |
| 402 | Nu mai ai cereri în sold | Nu | Cumperi un pachet; până atunci lași câmpurile editabile manual |
| 404 | Nu există firmă activă cu acest CUI (PFA, II și IF nu sunt incluse) | Nu | Permiți completarea manuală |
| 429 | Peste 10 cereri pe secundă pe cheie | Nu | Aștepți cât indică antetul Retry-After și reîncerci |
Regula de aur: dacă API-ul nu răspunde sau răspunde cu eroare, formularul trebuie să rămână utilizabil. Autocompletarea este o comoditate, nu un punct unic de eșec. Un caz de reținut este 404 pentru PFA-uri: dacă vinzi și către persoane fizice autorizate, lasă întotdeauna varianta de completare manuală.
Cache și limite de cereri
Denumirea, numărul de la Registrul Comerțului și adresa se schimbă rar. Statutul ANAF se schimbă mai des, dar și la noi este reîmprospătat doar dacă e mai vechi de 7 zile. De aici câteva reguli practice:
- păstrează răspunsurile într-un cache pe server, indexat după CUI, cu o durată de câteva zile pentru statutul ANAF și mai lungă pentru datele de identificare;
- nu apela API-ul la fiecare tastă apăsată; apelează doar când CUI-ul trece validarea locală, eventual cu un mic debounce;
- pentru procesări în lot (de exemplu reverificarea tuturor clienților), pune cererile într-o coadă și limitează-te sub 10 pe secundă;
- folosește GET /api/v1/cont (gratuit) ca să monitorizezi soldul și să primești o alertă înainte să ajungi la zero.
Pentru căutarea după nume, în loc de CUI, există GET /api/v1/cauta?q=...&judet=...&limit=..., care caută după începutul denumirii sau după CUI și întoarce cel mult 20 de rezultate. Bilanțurile pentru 2016–2025 vin din GET /api/v1/firma/{cui}/bilant, iar firmele înregistrate recent din GET /api/v1/firme-noi (1–30 de zile, cu filtre de județ și CAEN, 50 pe pagină).
Ce nu conține API-ul
API-ul conține date oficiale de identificare, statut fiscal și bilanțuri. Nu conține date de contact: nici telefon, nici email, nici site. Dacă ai nevoie de liste de firme cu telefon pentru vânzări, folosești căutarea în registru cu filtrul „doar cu telefon” și exportul Excel, plătit per firmă. De asemenea, API-ul nu include firmele radiate și nici PFA, II sau IF.
Alternativa: serviciul web public ANAF
ANAF oferă un serviciu web public și gratuit pentru verificarea statutului de plătitor de TVA și a altor informații despre contribuabili (cunoscut ca PlatitorTvaRest), documentat pe anaf.ro. Pentru volume mici și dacă ai nevoie doar de statutul fiscal, poate fi suficient. Ține cont de câteva aspecte: serviciul are limite de utilizare stabilite de ANAF, disponibilitatea lui poate varia, iar structura răspunsului se poate schimba odată cu versiunile. În plus, dacă vrei și date ONRC (EUID, forma juridică, codurile CAEN autorizate) sau bilanțuri pe mai mulți ani, trebuie să combini singur mai multe surse. Noi folosim același serviciu ANAF ca sursă pentru statutul fiscal și îl combinăm cu datele ONRC și cu bilanțurile Ministerului Finanțelor.
Prețuri și pachete
Modelul este preplătit, fără abonament și fără expirare: cumperi un pachet de cereri și le consumi când ai nevoie. Sunt taxate doar cererile reușite.
| Pachet | Cereri | Preț final |
|---|---|---|
| Gratuit | 100 (la prima cheie) | 0 lei |
| Start | 5.000 | 99 lei |
| Business | 25.000 | 199 lei |
| Pro | 100.000 | 499 lei |
| Volum | 500.000 | 1.990 lei |
Pentru un formular de facturare cu câteva sute de clienți noi pe lună, cu cache, pachetul Start ajunge de regulă pentru mult timp. Dacă îți faci cont prin înregistrare gratuită, primești și cele 100 de cereri de test. Pentru importul inițial al unei liste mari în CRM, vezi ghidul cum imporți corect firmele în CRM.