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:
- No authentication — no
TokenProvider, no certificate; and no test/prod split: there is only the production host. - 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, set0to opt out). - 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.
- 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.