Python SDK for Digifact FEL Guatemala e-invoicing API
Project description
Digifact FEL Guatemala — Python SDK
Python SDK for the Digifact FEL (Factura Electrónica en Línea) Guatemala e-invoicing API.
Installation
pip install digifact-sdk
Or from source:
pip install -e sdk/python/
Quick start
from digifact_sdk import DigifactClient
client = DigifactClient(
taxid="12345678",
username="FELUSER",
password="secret",
environment="test", # or "production"
)
# FACT CF — consumer final, IVA calculated automatically
result = client.invoice(
buyer="CF",
items=[{"description": "Consultoría", "qty": 1, "price": 100.00}]
)
print(result.auth_number)
# FACT to NIT — buyer name fetched from SAT automatically
result = client.invoice(
buyer="12345678",
items=[
{"description": "Laptop", "qty": 1, "price": 5000.00, "type": "Bien"},
{"description": "Soporte anual", "qty": 1, "price": 500.00},
]
)
# FACT to CUI buyer
result = client.invoice(
buyer={"taxid": "3730617490101", "type": "CUI", "name": "Juan Pérez"},
items=[{"description": "Producto", "qty": 2, "price": 50.00}]
)
# Full NIT buyer with explicit details (no auto-lookup)
result = client.invoice(
buyer={
"taxid": "12345678",
"name": "EMPRESA EJEMPLO S.A.",
"address": "6 AV 6-48 ZONA 9",
"city": "01009",
"district": "GUATEMALA",
"state": "GUATEMALA",
"country": "GT",
"email": "facturacion@empresa.com", # optional
},
items=[{"description": "Producto", "qty": 1, "price": 100.00}]
)
# FCAM (Factura Cambiaria)
result = client.invoice(
buyer="12345678",
items=[{"description": "Servicio", "qty": 1, "price": 500.00}],
doc_type="FCAM",
payment_terms=[{"date": "2026-04-18", "amount": 500.00}]
)
# Credit note (NCRE)
result = client.credit_note(
buyer="12345678",
items=[{"description": "Devolución", "qty": 1, "price": 100.00}],
origin={
"auth_number": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"date": "2026-03-18",
"series": "XXXXXXXX",
"number": "123456",
},
reason="Producto defectuoso"
)
# Debit note (NDEB)
result = client.debit_note(
buyer="12345678",
items=[{"description": "Cargo adicional", "qty": 1, "price": 50.00}],
origin={...},
reason="Cargo por entrega express"
)
# Cancel a DTE
result = client.cancel(
auth_number="XXXXXXXX-...",
receiver_id="CF",
issue_datetime="2026-03-18 21:40:14",
reason="Error en monto"
)
# Total credit note
result = client.credit_note_total(
auth_number="XXXXXXXX-...",
issue_datetime="2026-03-18 21:40:14",
reason="Nota de crédito total"
)
# NIT lookup
info = client.lookup_nit("12345678")
print(info["name"])
# Retrieve DTE
doc = client.get_dte("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
Document types
| Type | Description | IVA |
|---|---|---|
FACT |
Standard invoice | Yes |
FCAM |
Factura Cambiaria (installments) | Yes |
NDEB |
Debit note | Yes |
NCRE |
Credit note | Yes |
NABN |
Nota de Abono | No |
FESP |
Factura Especial | Yes |
RDON |
Recibo por Donación | No |
FPEQ |
Factura Pequeño Contribuyente | No |
RECI |
Recibo universitario | No |
CCA |
Cobro por Cuenta Ajena | Yes |
IVA calculation
Prices are IVA-inclusive (what the customer pays):
line_total = qty × price
taxable_amount = line_total / 1.12
iva_amount = line_total − taxable_amount
All money values are formatted as strings with 6 decimal places.
Item dict keys
{
"description": str, # required
"price": float | Decimal, # required — unit price, IVA-inclusive
"qty": float | Decimal, # optional, default 1
"type": str, # optional: "Servicio" (default) | "Bien"
"unit_of_measure": str, # optional, default "UNI"
"discount": float | None, # optional — line discount amount
}
Fuel invoices (FACT Combustible)
Fuel invoices emit IVA and a PETROLEO pass-through tax per SAT spec.
Items without petroleo_amount are treated as regular IVA-only items and
can coexist in the same invoice.
Option A — rates set once at client init (recommended for gas stations)
# Set PETROLEO rates once per fuel type (Q/gallon, from MEM or supplier invoice)
client = DigifactClient(
taxid="12345678",
username="FELUSER",
password="secret",
petroleo_rates={"1": 4.70, "2": 4.60, "4": 1.30}, # SUPER / REGULAR / DIESEL
)
# Only petroleo_code is needed — petroleo_amount is filled in automatically
result = client.fuel_invoice(
buyer="CF",
items=[
{"description": "GASOLINA SUPER", "qty": 30, "price": 35.00, "petroleo_code": "1", "type": "Bien"},
{"description": "GASOLINA REGULAR", "qty": 20, "price": 34.00, "petroleo_code": "2", "type": "Bien"},
{"description": "GASOLINA DIESEL", "qty": 50, "price": 32.00, "petroleo_code": "4", "type": "Bien"},
# Regular items (no petroleo_code): IVA only, can coexist
{"description": "FILTRO DE ACEITE", "qty": 1, "price": 45.00, "type": "Bien"},
{"description": "SET DE CANDELAS NGK", "qty": 1, "price": 400.00, "type": "Bien"},
],
)
print(result.auth_number)
Option B — explicit per-item amount
result = client.fuel_invoice(
buyer="CF",
items=[
{"description": "GASOLINA SUPER", "qty": 1, "price": 35.00, "petroleo_amount": 4.70, "petroleo_code": "1", "type": "Bien"},
{"description": "GASOLINA DIESEL", "qty": 1, "price": 32.00, "petroleo_amount": 1.30, "petroleo_code": "4", "type": "Bien"},
],
)
Fuel item dict keys
| Key | Type | Default | Description |
|---|---|---|---|
description |
str |
required | Line description |
price |
float|Decimal |
required | Full consumer price per unit (PETROLEO + IVA-inclusive). This is what the customer pays at the pump. If the receipt shows a unit price without PETROLEO/IDP (e.g. 37.99), add the per-unit IDP rate: price = 37.99 + 4.70 = 42.69. |
qty |
float|Decimal |
1 |
Quantity |
type |
str |
"Servicio" |
"Bien" or "Servicio" |
unit_of_measure |
str |
"UNI" |
SAT unit code |
petroleo_amount |
float|Decimal |
— | Per-unit PETROLEO tax; omit for IVA-only items |
petroleo_code |
str |
"1" |
"1"=SUPER, "2"=REGULAR, "4"=DIESEL. Required when petroleo_amount is omitted and petroleo_rates is set; raises DigifactValidationError if the code is not found in the rates dict. |
Running tests
# Unit tests (no credentials needed)
python -m pytest tests/ -v
# Integration tests
export DIGIFACT_TAXID=12345678
export DIGIFACT_USERNAME=FELUSER
export DIGIFACT_PASSWORD=your_password
python -m pytest tests/ -v
Environment variables
| Variable | Description |
|---|---|
DIGIFACT_TAXID |
Fiscal ID (e.g. 12345678) |
DIGIFACT_USERNAME |
Username (e.g. FELUSER) |
DIGIFACT_PASSWORD |
Account password |
Error handling
from digifact_sdk import (
DigifactError, # base
DigifactAuthError, # auth failure
DigifactApiError, # HTTP / API error
DigifactValidationError, # SAT rejection
DigifactNitNotFoundError, # NIT not found
)
try:
result = client.invoice("CF", [...])
except DigifactValidationError as exc:
print(f"SAT rejected: {exc}")
print(f"Code: {exc.code}")
print(f"Raw: {exc.raw}")
except DigifactError as exc:
print(f"SDK error: {exc}")
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file digifact_sdk-2.2.0.tar.gz.
File metadata
- Download URL: digifact_sdk-2.2.0.tar.gz
- Upload date:
- Size: 24.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fb28e84ae2970923c2b3916d6d12f417d66187b8a76571d8b8a102cb3430c366
|
|
| MD5 |
b5bbf2a2af48745979ee2c9bbf3e9e08
|
|
| BLAKE2b-256 |
35531b202721ac5b91b39889d20f848265484a6c3cc045daaa8f8e4cd5dd31bd
|
Provenance
The following attestation bundles were made for digifact_sdk-2.2.0.tar.gz:
Publisher:
publish.yml on aalonzolu/digifact
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
digifact_sdk-2.2.0.tar.gz -
Subject digest:
fb28e84ae2970923c2b3916d6d12f417d66187b8a76571d8b8a102cb3430c366 - Sigstore transparency entry: 1310895658
- Sigstore integration time:
-
Permalink:
aalonzolu/digifact@c88a90f4f8224d428ba16b5524f910237b2fe0f0 -
Branch / Tag:
refs/tags/v2.2.0 - Owner: https://github.com/aalonzolu
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c88a90f4f8224d428ba16b5524f910237b2fe0f0 -
Trigger Event:
push
-
Statement type:
File details
Details for the file digifact_sdk-2.2.0-py3-none-any.whl.
File metadata
- Download URL: digifact_sdk-2.2.0-py3-none-any.whl
- Upload date:
- Size: 19.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
45f4515d1d199b45b7b9a475f9d38106ed789d6e67ae8e65490ec2243dd79558
|
|
| MD5 |
e3000e6f7240e8b08b83fc4c8d09d505
|
|
| BLAKE2b-256 |
59d68a21326ecc6816e4136ff5f04dfdf140bae25cec7f84b225e9cf9af3cc98
|
Provenance
The following attestation bundles were made for digifact_sdk-2.2.0-py3-none-any.whl:
Publisher:
publish.yml on aalonzolu/digifact
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
digifact_sdk-2.2.0-py3-none-any.whl -
Subject digest:
45f4515d1d199b45b7b9a475f9d38106ed789d6e67ae8e65490ec2243dd79558 - Sigstore transparency entry: 1310895754
- Sigstore integration time:
-
Permalink:
aalonzolu/digifact@c88a90f4f8224d428ba16b5524f910237b2fe0f0 -
Branch / Tag:
refs/tags/v2.2.0 - Owner: https://github.com/aalonzolu
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c88a90f4f8224d428ba16b5524f910237b2fe0f0 -
Trigger Event:
push
-
Statement type: