Skip to main content

FiscalRail Python SDK

Typed Python client for issuing immutable invoices through FiscalRail.

Documentation · Changelog

python -m pip install fiscalrail

Pass a Test or Live secret explicitly when creating the client.

Issue an invoice

import os
from decimal import Decimal

from fiscalrail import FiscalRail
from fiscalrail.tax_regimes.es import irpf, vat

client = FiscalRail(os.environ["FISCALRAIL_API_KEY"])

invoice = client.invoices.issue(
    customer="cus_...",
    lines=[
        {
            "description": "Consulting services",
            "unit_price": Decimal("2500.00"),
            "taxes": [vat.general, irpf.professionals],
        }
    ],
)

pdf = client.invoice_pdfs.render_content(invoice.id, locale="en")
pdf.write_to_file(f"{invoice.code}.pdf")

The API key is required. Your application may read it from an environment variable or secret manager, but the SDK never reads process configuration on its own. The key selects the Test or Live account; the SDK has no separate environment switch.

The client owns a pooled requests.Session by default. Applications that need custom proxy, TLS, adapter or observability configuration can inject one:

import os

import requests

from fiscalrail import FiscalRail

session = requests.Session()
client = FiscalRail(os.environ["FISCALRAIL_API_KEY"], session=session)

Injected sessions remain owned by the caller and are not closed by the SDK.

Invoice issuance automatically uses an idempotency key. Durable workflows can provide and persist their own:

invoice = client.invoices.issue(
    idempotency_key="a49b50f6-1571-4e06-a243-e258bda98e40",
    customer="cus_...",
    lines=[
        {
            "description": "Consulting services",
            "unit_price": "2500.00",
            "taxes": [vat.general],
        }
    ],
)

Payment instructions

Create reusable payment instructions, optionally make them account defaults, and set an invoice due date without sending bank details on every issuance:

from datetime import date

instruction = client.payment_instructions.create(
    label="Main EUR account",
    type="bank_transfer",
    bank_transfer={
        "beneficiary": "Example supplier",
        "iban": "ES91 2100 0418 4502 0005 1332",
        "bic": "CAIXESBBXXX",
    },
)

client.account_invoicing.update(
    default_payment_instructions=[instruction.id],
)

invoice = client.invoices.issue(
    payment_terms={"due_date": date(2026, 9, 30)},
    lines=[
        {
            "description": "Consulting services",
            "unit_price": "2500.00",
            "taxes": [vat.general],
        }
    ],
)

Pass payment_terms={"options": [instruction.id]} to override the account defaults for a specific invoice. Pass an empty options list to render no payment instructions.

Typed request values

Calls are type checked directly. Exported TypedDict definitions also make larger payloads reusable without introducing runtime parameter wrappers:

from fiscalrail.params import InvoiceIssueParams

params = InvoiceIssueParams(
    customer="cus_...",
    lines=[
        {
            "description": "Consulting services",
            "unit_price": Decimal("2500.00"),
            "taxes": [vat.general, irpf.professionals],
        }
    ],
)

invoice = client.invoices.issue(**params)

Responses are dependency-free frozen dataclasses. Dates, timestamps and monetary amounts are parsed into date, datetime and Decimal values. Unknown response fields are retained in response.extra_fields for forward compatibility and remain available through attribute access.

The response dataclasses, request TypedDicts, enums and operation registry are generated from FiscalRail's OpenAPI contract. The public client and resource methods remain hand-written so they can expose domain verbs, pooling, idempotency and retry behavior instead of generator-shaped HTTP calls.

Resources

  • client.accounts
  • client.account_invoicing
  • client.account_tax_regimes
  • client.api_keys
  • client.balances
  • client.customers
  • client.event_destinations
  • client.events
  • client.invoice_series
  • client.invoices
  • client.invoice_pdfs
  • client.payment_instructions
  • client.tax_ids
  • client.tax_regimes

Invoices use the domain verbs issue and amend; they are never updated. The current account and its invoicing settings each expose retrieve and update. Customer and series and payment-instruction resources expose ordinary create, retrieve, update, list and delete operations.

Account balance and tax-regime state

balance = client.balances.retrieve()
print(balance.amount)  # Decimal, including zero or negative balances

regime = client.account_tax_regimes.retrieve()
if regime.key == "es" and regime.es.representation is not None:
    print(regime.es.representation.status)

Balances exist only for Live accounts; retrieving a Test account's balance raises ResourceNotFoundError. The amount is informational: paid operations can still fail with BalanceExhaustedError if the balance changes.

Account tax regimes return a GlobalAccountTaxRegime or SpanishAccountTaxRegime. Spanish Test accounts have es.representation=None; unverified Live representation timestamps can also be None. Both resources retain request_id, preserve unknown fields, and use the client's safe-read retry behavior. client.tax_regimes continues to expose the general tax catalog; client.account_tax_regimes exposes account-specific state.

Verify webhooks

Verify the exact request body before parsing or processing it:

from fiscalrail.webhooks import construct_event

event = construct_event(raw_body, signature_header, signing_secret)

construct_event checks the HMAC in constant time, applies a five-minute timestamp tolerance, and raises WebhookSignatureError when verification fails.

Development

uv sync --all-groups
uv run python scripts/generate_contract.py
uv run python scripts/generate_contract.py --check
uv run pytest
uv run ty check
uv run ruff check .
uv build

Release maintainers should follow the release guide.

Spanish AEAT submission

Use a Live Spanish account key. Upload a .p12/.pfx file (up to 128 KiB), including its private key, using native multipart upload. Omit the password for an unprotected bundle. The certificate's issuer NIF must match the account.

import os

with open("issuer.p12", "rb") as certificate:
    setup = client.account_tax_regimes.es.upload_certificate(
        certificate_file=certificate,
        certificate_password=os.environ["CERTIFICATE_PASSWORD"],
    )
setup = client.account_tax_regimes.retrieve()
# Inspect setup.es.pending_submission.status / error_code and setup.es.submission.ready.
client.account_tax_regimes.es.verify_submission()  # retry the pending or active check
client.account_tax_regimes.es.cancel_submission_change()
client.account_tax_regimes.es.verify_representation()  # after granting AEAT authority

Each mutation above is a separate operation; choose the one needed. Upload and verification return the account setup while AEAT checks run asynchronously. Poll the generic account tax-regime resource for pending verification status, error code and the active setup's readiness. A working setup remains active until a replacement verifies; failed checks retain the pending certificate for retry. Cancelling removes only the pending change. Uploads are not automatically retried. The ES mutations use /account/tax-regime/es/...; reads use /account/tax-regime.

Metadata

Release files for fiscalrail 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fiscalrail 0.6.0
File Size Uploaded
fiscalrail-0.6.0.tar.gz 90.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fiscalrail 0.6.0
File Interpreter ABI Platform
fiscalrail-0.6.0-py3-none-any.whl Python 3 none any Details

Total release size: 122.7 kB

Release files / fiscalrail-0.6.0.tar.gz

Download URL fiscalrail-0.6.0.tar.gz
Size 90.9 kB
Tags Source
SHA-256 checksum
How to use checksums
d09827415aa04db4a9aca8436cb6bd5cdc28a7c733b1ae8a72632ca929013607
BLAKE2b-256 checksum
How to use checksums
13fdce0e7245651d838e5bfb60478951f6ed711a70db9a6bbffe8396cba23480
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.

Transparency log

Release files / fiscalrail-0.6.0-py3-none-any.whl

Download URL fiscalrail-0.6.0-py3-none-any.whl
Size 31.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
28ae16ff69ab90dca795764cb4a2e3e8fb3eedb6253a7a921db2fa0c1540e495
BLAKE2b-256 checksum
How to use checksums
45f0d8e34c35ad81fe9f81c33a43b8ced3be3d9ca1935d4eab542e8f89dab5dc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.6.0 This release

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page