Skip to content

Public services API

The no-auth client and its lookup value types.

anafpy.public.client

Async client for ANAF's unauthenticated public web services.

These are the no-auth services on webservicesp.anaf.ro (see docs/anaf-reference/public/api.md): the taxpayer/VAT registry, the RO e-Factura register, the farmers' and cult-entities registers, public annual financial statements — plus the stateless e-Factura document services validare (:meth:PublicClient.validate_invoice) and transformare (:meth:PublicClient.render_invoice_pdf), which are public, no-auth, and prod-only (their test paths answer HTTP 404, live-confirmed 2026-07-02) — they validate/render a document without filing anything. Key differences from the OAuth clients:

  1. No authentication — no TokenProvider, no certificate; and no test/prod split: there is only the production host.
  2. Client-side pacing. ANAF states a 1 request/second limit as a usage rule (not via 429s), so — unlike the OAuth clients' no-auto-backoff stance — this client spaces its own requests (min_request_interval, set 0 to opt out).
  3. Registry queries are batched: a list of CUIs evaluated at one date, capped at 100 (taxpayer, e-Factura register) or 500 (RegAgric, RegCult) per request.
  4. The RO e-Factura register answers HTTP 404 when no queried CUI has data — that is a business "not found" (returned), not a transport error (raised).

The synchronous services only. The async job variant of the taxpayer lookup (/AsynchWebService/…) is deliberately not wrapped: its result is downloadable exactly once and its not-ready response is undocumented, so wrapping it blind would invent semantics — revisit if a real batching need appears.

PublicClient

PublicClient(*, http: AsyncClient | None = None, timeout: float = 60.0, min_request_interval: float = 1.0)

Bases: HttpClientBase

Talks to ANAF's unauthenticated public services (webservicesp.anaf.ro).

No credentials are needed; the client owns an httpx.AsyncClient unless one is injected. An injected client must carry a non-empty base_url (an empty one raises :class:~anafpy.exceptions.AnafConfigError; injected clients are never mutated). Use it as an async context manager so owned clients close cleanly. Requests are paced at min_request_interval seconds (default 1.0, ANAF's stated limit); pass 0 to disable pacing and bring your own.

lookup_taxpayers async

lookup_taxpayers(cuis: Sequence[int | str], *, date: date | str | None = None) -> TaxpayerLookup

Query the taxpayer/VAT registry (v9) for up to 100 CUIs at one date.

The workhorse lookup: each :class:~anafpy.public.models.TaxpayerRecord answers VAT registration (art. 316), VAT-on-collection, inactive status, split-VAT, and RO e-Factura register membership as of date (default today), plus general company data. CUIs ANAF has no data for come back in not_found.

lookup_efactura_register async

lookup_efactura_register(cuis: Sequence[int | str], *, date: date | str | None = None) -> EfacturaRegisterLookup

Query the Registrul RO e-Factura (opt-in register) for up to 100 CUIs.

ANAF answers HTTP 404 when no queried CUI has data, with a regular found/notFound body — returned here as a normal (empty) lookup, not raised. For a plain "is this CUI e-Factura-registered" check, prefer lookup_taxpayers — its efactura_registered answers it alongside everything else.

lookup_farmers async

lookup_farmers(cuis: Sequence[int | str], *, date: date | str | None = None) -> FarmerLookup

Query the farmers' special-regime register (art. 315¹) for up to 500 CUIs.

A CUI that is not in the register still comes back under found with registered is False — read membership from the boolean, not from presence in found.

lookup_cult_entities async

lookup_cult_entities(cuis: Sequence[int | str], *, date: date | str | None = None) -> CultLookup

Query the cult-entities register for up to 500 CUIs at one date.

Same conventions as lookup_farmers (membership is the registered boolean); a future date is answered as of the current date by ANAF.

get_financial_statement async

get_financial_statement(cui: int | str, year: int) -> FinancialStatement

Fetch the public indicators of one CUI's annual financial statement.

One CUI + one year per call (that is the API's shape). The indicator set varies by statement type; see :class:~anafpy.public.models.FinancialStatement.

validate_invoice async

validate_invoice(xml: str | bytes, *, standard: TransformStandard = INVOICE) -> RemoteValidationResult

Validate e-Factura XML server-side (validare/{std}) without filing it.

ANAF's own validator — authoritative, unlike any local pre-check. Pass standard=TransformStandard.CREDIT_NOTE for a credit note. An invalid document is returned as a :class:RemoteValidationResult with valid=False and the findings in messages, not raised. Nothing is filed anywhere.

render_invoice_pdf async

render_invoice_pdf(xml: str | bytes, *, standard: TransformStandard = INVOICE, validate: bool = True) -> bytes

Render e-Factura XML to a PDF (transformare/{std}).

validate=False skips ANAF's validation (it then does not guarantee the PDF — use it for documents that already passed validation at filing). A body that cannot be rendered still answers HTTP 200, with a JSON error payload instead of PDF bytes; callers decide how strictly to check.

anafpy.public.models

Value types returned by :class:anafpy.public.client.PublicClient.

The public services answer JSON with Romanian wire names in wildly inconsistent casing (scpTVA, statusRO_e_Factura, nrRegCom); the models here expose an English snake_case surface and keep the wire names as pydantic aliases, so the reference doc (docs/anaf-reference/public/api.md) stays the map between the two.

Conventions shared with the other clients: string fields tolerate ANAF returning numbers, empty strings (ANAF's "absent") become None, and every response container keeps the raw body bytes for fidelity — the typed view is a convenience, not the source of truth.

Membership caveat (per the live-confirmed reference): RegAgric/RegCult return CUIs that are not in the register under found with empty fields and a False status — read membership from the registered booleans, never from presence in found.

TransformStandard

Bases: StrEnum

std path segment for /validare and /transformare.

RemoteValidationResult

Bases: BaseModel

Outcome of ANAF's server-side validare endpoint.

An invalid document is a business outcome: valid is False and messages carries ANAF's findings — never an exception.

GeneralData

Bases: _WireModel

date_generale: company identity, registration, and e-Factura membership.

VatPeriod

Bases: _WireModel

One perioade_TVA[] entry: an interval of art. 316 VAT registration.

VatRegistration

Bases: _WireModel

inregistrare_scop_Tva: art. 316 Cod Fiscal VAT registration.

VatOnCollection

Bases: _WireModel

inregistrare_RTVAI: TVA la încasare (VAT on collection).

InactiveState

Bases: _WireModel

stare_inactiv: inactive / reactivated taxpayer status.

SplitVat

Bases: _WireModel

inregistrare_SplitTVA: split-VAT registration.

Address

Bases: _WireModel

A structured address (registered office or fiscal domicile), wire prefix (s/d) already stripped by the parent model.

TaxpayerRecord

Bases: _WireModel

One found[] entry of the v9 taxpayer/VAT registry lookup.

The status booleans answer membership as of the queried date; absent registrations come back False with empty date fields. Note that efactura_registered is False only means the CUI never joined the pre-mandate opt-in register — post-2024 it does not mean it can't receive e-Factura.

EfacturaRegisterEntry

Bases: _WireModel

One found[] entry of the Registrul RO e-Factura query.

Shape per docV1.txt; not yet live-confirmed (see the reference doc §2).

FarmerRecord

Bases: _WireModel

One found[] entry of the farmers' special-regime register (art. 315¹).

CultRecord

Bases: _WireModel

One found[] entry of the cult-entities register (tax-credit eligible).

RegistryLookup

Bases: _WireModel

A registry query outcome: matched records plus the CUIs ANAF had no data for.

Membership in a specific register must be read from the record's registered / status booleans — RegAgric/RegCult put unknown CUIs in found too.

FinancialIndicator

Bases: _WireModel

One i[] entry: a published balance-sheet indicator.

FinancialStatement

Bases: _WireModel

Public indicators from an annual financial statement (GET /bilant).

The indicator set varies by statement type (commercial vs banking vs insurance), so indicators are a list, not fixed fields.