Verificare firmă România prin API: CUI, surse și coverage

Verificare firmă România prin API pentru agenți AI: ce identifică un CUI, ce surse există, ce înseamnă coverage și cum plătești fără cont.

Un agent AI care pregătește o ofertă, emite o factură sau face due diligence pe un partener din România are nevoie de răspunsuri simple: firma există, e activă, e plătitoare de TVA, ce cifră de afaceri a raportat. Un om găsește asta în câteva minute pe site-uri publice. Un agent are nevoie de aceleași date ca JSON, de la un singur apel, și, mai ales, de o distincție pe care multe API-uri o ignoră: diferența dintre „nu există” și „n-am verificat”. Articolul de față explică ce identifică un CUI, ce surse de date există, cum le folosește unealta ro_company de la 402check și cum plătește un agent pentru un apel fără să-și facă cont.

CUI-ul: cheia de căutare

Firmele românești sunt identificate fiscal prin codul unic de înregistrare (CUI). Unealta primește CUI-ul în URL, GET /v1/ro/company/{cui}, și îl normalizează înainte de orice altceva:

TypeScript src/tools/company.ts
export function normalizeCui(input: string): string | null {
  const cui = input.replace(/\s+/g, "").replace(/^ro/i, "");
  return CUI_PATTERN.test(cui) ? cui : null;
}

Spațiile dispar, prefixul RO (scris cu litere mari sau mici) e eliminat, iar ce rămâne trebuie să aibă între 2 și 10 cifre. RO 14399840, ro14399840 și 14399840 duc la aceeași căutare. Orice altceva primește 400 invalid_cui și nu se taxează.

Nu verificăm cifra de control a CUI-ului. Un cod cu format corect care nu corespunde unei firme ajunge la furnizor, iar răspunsul e 404 not_found, tot netaxat. Pentru un agent asta înseamnă că poate încerca fără grijă un CUI extras dintr-un PDF sau dintr-un email: o greșeală de citire costă doar timpul unui apel.

Ce surse există

Datele despre firme din România nu vin dintr-un singur loc. Pentru un agent contează trei categorii.

Serviciul web gratuit al ANAF pentru TVA. ANAF publică un serviciu web pentru starea de plătitor de TVA. Documentația versiunii 9 spune că cererea se trimite prin POST, ca listă JSON de perechi CUI–dată, că „un request poate contine maxim 100 de CUI-uri” și că „un client poate executa maxim 1 request pe secunda”. Răspunsul conține, pentru data cerută, câmpuri precum scpTVA (înregistrat în scopuri de TVA), statusInactivi, statusTvaIncasare, statusSplitTVA și statusRO_e_Factura. E o sursă bună pentru TVA și statut de inactiv, dar nu dă bilanțuri.

Furnizori comerciali care agregă date. Bilanțurile, numărul de angajați, adresa sediului, codul CAEN și starea din registrul comerțului se obțin mai ușor printr-un furnizor care le agregă. 402check folosește datele FirmeAPI.ro pentru identificare și bilanțuri. La fiecare apel facem două cereri către furnizor, în paralel: datele de identificare și bilanțurile.

Surse pe care v1 nu le interoghează. Datoriile la ANAF, insolvența și dosarele din instanță ar cere cereri plătite separate la furnizor. În v1 nu le facem și spunem asta explicit în fiecare răspuns, prin coverage.

Ce primește agentul

Pentru firma fictivă din fixture-ul nostru de test, răspunsul complet arată așa:

JSON
{
  "cui": "12345678",
  "name": "EXEMPLU SRL",
  "registrationNo": "J40/1234/2010",
  "status": "INREGISTRAT",
  "vatPayer": true,
  "address": "Str. Victoriei, 10, Bucuresti, Bucuresti",
  "caen": "6201",
  "financials": [
    { "year": 2022, "turnover": 900000, "profit": 80000, "employees": 11 },
    { "year": 2023, "turnover": 1000000, "profit": null, "employees": 12 }
  ],
  "anafDebts": null,
  "insolvency": null,
  "courtCases": null,
  "coverage": { "financials": true, "anafDebts": false, "insolvency": false, "courtCases": false },
  "sources": ["firmeapi.ro"],
  "fetchedAt": "2026-10-27T09:01:47.974Z"
}

Câteva reguli de citire:

  • financials e sortat crescător după an. turnover e cifra de afaceri, iar profit e profitul net; o pierdere apare ca număr negativ. employees e numărul mediu de salariați. Un indicator lipsă din bilanț e null.
  • vatPayer și caen pot fi null când furnizorul nu le are.
  • Datele personale nu apar niciodată: nici administratori, nici asociați, nici date de contact. Se filtrează la mapare, nu la afișare.

Ce înseamnă coverage

Aici e partea care contează cel mai mult pentru un agent. În răspuns, null înseamnă „nu am verificat”, niciodată „nu există”. anafDebts: null nu spune că firma nu are datorii, ci că nu am întrebat. Obiectul coverage spune, sursă cu sursă, ce s-a verificat la fiecare apel.

Pentru bilanțuri există trei situații, și codul le separă explicit:

TypeScript src/tools/company.ts
  try {
    const bilant = parseUpstream(bilantSchema, await getJson(`/bilant/${cui}`, env, fetchImpl));
    return { financials: mapFinancials(bilant), covered: true };
  } catch (error: unknown) {
    if (error instanceof NotFoundError) return { financials: [], covered: true };
    console.warn("ro_company financials unavailable", { error });
    return { financials: [], covered: false };
  }
  1. Furnizorul a dat bilanțuri: financials are ani, coverage.financials e true.
  2. Furnizorul a răspuns că nu are bilanțuri pentru firmă: financials e [], iar coverage.financials e tot true. Am verificat și nu există.
  3. Cererea de bilanțuri a eșuat: financials e [], dar coverage.financials e false. Nu știm.

Un agent care tratează [] ca „firma n-a depus bilanț” fără să se uite la coverage va trage concluzia greșită în cazul 3. Regula practică: înainte să afirmi ceva despre o sursă, verifică coverage pentru acea sursă. Pentru anafDebts, insolvency și courtCases, coverage e false în v1, deci agentul trebuie să spună „neverificat”, nu „curat”.

Dacă datele de identificare lipsesc complet (furnizorul nu are firma sau nu are o denumire pentru ea), răspunsul e 404 not_found. Dacă furnizorul cade, e 502 upstream_error. Ambele sunt netaxate.

Cum plătește un agent fără cont

Un dosar de firmă costă $0.01 per apel reușit. Nu există cont, formular de înregistrare sau abonament. Sunt trei variante, iar serverul o alege per apel în această ordine:

  1. Cheie cu credite. Cumperi $5 sau $20 de credite cu cardul la /buy (Stripe Checkout) și primești o cheie rk_…, afișată o singură dată pe pagina de confirmare. O trimiți ca Authorization: Bearer rk_…, prin HTTP sau prin MCP. La $0.01 per apel, $5 înseamnă 500 de dosare. Soldul rămas se vede cu GET /v1/balance, gratuit.
  2. Nivelul gratuit (free tier). Fără cheie, primele 10 apeluri pe zi pentru fiecare adresă IP sunt gratuite.
  3. x402. Fără cheie, după nivelul gratuit, un client x402 plătește apelul în USDC pe Base și repetă cererea, doar pe rutele HTTP.

Creditele se scad doar la un răspuns 2xx. Un CUI invalid, o firmă inexistentă sau o eroare a furnizorului nu costă nimic.

Limitele, spuse direct

  • O singură sursă comercială pentru identificare și bilanțuri. Dacă furnizorul are o întârziere față de registrul oficial, o avem și noi.
  • Fără datorii ANAF, insolvență sau dosare în v1; coverage le marchează ca neverificate.
  • Fără date personale, intenționat.
  • Răspunsul e un ajutor de căutare, nu o opinie juridică sau un certificat constatator.

Încearcă

Primul apel folosește nivelul gratuit, fără cheie. Al doilea folosește o cheie cu credite și arată că prefixul RO e acceptat:

Shell
curl "https://402check.com/v1/ro/company/14399840"

curl -H "Authorization: Bearer rk_YOUR_KEY" "https://402check.com/v1/ro/company/RO14399840"

Parametrii, un formular care rulează apelul din browser și apelul MCP echivalent sunt pe pagina de documentație ro_company.