SDK oficial de https://fiscalapi.com para Python, la API de facturación CFDI y otros servicios fiscales en México. Simplifica la integración con los servicios de facturación electrónica, eliminando las complejidades del SAT y facilitando la generación de facturas, notas de crédito, complementos de pago, nómina, carta porte, y más. ¡Facturar sin dolor ahora es posible!
- Soporte completo para CFDI 4.0 con todas las especificaciones oficiales
- Timbrado de facturas de ingreso con validación automática
- Timbrado de notas de crédito (facturas de egreso)
- Timbrado de complementos de pago en MXN, USD y EUR
- Timbrado de facturas de nómina
- Timbrado de facturas de carta porte
- Timbrado de facturas de comercio exterior
- Consulta del estatus de facturas en el SAT en tiempo real
- Cancelación de facturas
- Generación de archivos PDF de las facturas con formato profesional
- Personalización de logos y colores en los PDF generados
- Envío de facturas por correo electrónico automatizado
- Descarga de archivos XML con estructura completa
- Almacenamiento y recuperación de facturas por 5 años
- Dos modos de operación: Por valores o Por referencias
- Ejemplos en Python
- Acceso a catálogos de descarga masiva del SAT
- Descarga de CFDI y Metadatos en lotes grandes
- Descarga masiva XML con filtros personalizados
- Reglas de descarga automática por RFC
- Solicitudes de descarga via API y Dashboard.
- Automatización de solicitudes de descarga
- Administración de personas (emisores, receptores, clientes, usuarios, etc.)
- Gestión de certificados CSD y FIEL (subir archivos .cer y .key a FiscalAPI)
- Configuración de datos fiscales (RFC, domicilio fiscal, régimen fiscal)
- Datos de empleado (agrega/actualiza/elimina datos de empleado a una persona. CFDI Nómina)
- Datos de empleador (agrega/actualiza/elimina datos de empleador a una persona. CFDI Nómina)
- Firma de carta manifiesto (firma el manifiesto de una persona con su e.firma/FIEL)
- Gestión de productos y servicios con catálogo personalizable
- Administración de impuestos aplicables (IVA, ISR, IEPS)
- Consulta en catálogos oficiales del SAT actualizados
- Consulta en catálogos oficiales de Descarga masiva del SAT actualizados
- Búsqueda de información en catálogos del SAT con filtros avanzados
- Acceso y búsqueda en catálogos completos
- Gestión de folios fiscales: compra timbres a FiscalAPI y transfiere/retira a las personas de tu organización según tus reglas de negocio
- Listar transacciones del ledger con paginación
- Consultar transacciones por ID
- Transferir timbres entre personas
- Retirar timbres de una persona
- Transferir y retirar créditos de validación SAT con
credit_type=CreditType.VALIDATION
- Estructura del XML contra el Anexo 20 y los complementos declarados
- Vigencia del certificado del emisor a la fecha de emisión
- Sello del CFDI y sello del SAT en el Timbre Fiscal Digital
- Estatus del comprobante en el SAT (vigente, cancelado, no encontrado)
- Listas negras del artículo 69-B y 69-B Bis del CFF, por CFDI o por RFC
- Catálogo de tipos de validación y de los estatus que cada uno puede tomar
- Cientos de ejemplos de código disponibles en múltiples lenguajes de programación
- Documentación completa con guías paso a paso
- Ejemplos prácticos para casos de uso comunes
- Soporte técnico especializado
- Actualizaciones regulares conforme a cambios del SAT
pip:
pip install fiscalapipoetry:
poetry add fiscalapi- Crea un objeto de configuración con tus credenciales:
from fiscalapi import FiscalApiSettings
settings = FiscalApiSettings(
api_url="https://test.fiscalapi.com", # https://live.fiscalapi.com (producción)
api_key="<API_KEY>",
tenant="<TENANT_KEY>"
)- Crea la instancia del cliente:
from fiscalapi import FiscalApiClient
client = FiscalApiClient(settings=settings)FiscalAPI admite dos modos de operación:
-
Por Referencias: Envía solo IDs de objetos previamente creados en el dashboard de FiscalAPI.
Ideal para integraciones ligeras. -
Por Valores: Envía todos los campos requeridos en cada petición, con mayor control sobre los datos.
No se requiere configuración previa en el dashboard.
from fiscalapi import Person
person = Person(
legal_name="Empresa Python SA de CV",
email="mail7@gmail.com",
password="TestPassword1234!"
)
api_response = client.people.create(person)Descarga certificados de prueba
from fiscalapi import TaxFile
# Subir certificado (CER)
certificado_csd = TaxFile(
person_id="3f3478b4-60fd-459e-8bfc-f8239fc96257",
tin="FUNK671228PH6",
base64_file="MIIFgDCCA2igAwIBAgIUMzAwMDEwMDAwMDA1MDAwMDM0NDYwDQYJKo...",
file_type=0, # 0 para certificado, 1 para llave privada
password="12345678a"
)
# Subir llave privada (KEY)
clave_privada_csd = TaxFile(
person_id="3f3478b4-60fd-459e-8bfc-f8239fc96257",
tin="FUNK671228PH6",
base64_file="MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIAg...",
file_type=1,
password="12345678a"
)
api_response_cer = client.tax_files.create(certificado_csd)
api_response_key = client.tax_files.create(clave_privada_csd)from fiscalapi import Product
product = Product(
description="Producto python sin impuestos",
unit_price=Decimal("100.00")
)
api_response = client.products.create(product)from fiscalapi import Product, ProductTax
product = Product(
id="f4bf4df3-5a91-4a30-b137-52cb517d13c4",
description="Producto python sin impuestos",
unit_price=Decimal("100.00"),
product_taxes=[
ProductTax(
rate=Decimal("0.160000"),
taxId="002",
taxFlagId="T",
taxTypeId="Tasa"
),
ProductTax(
rate=Decimal("0.106667"),
taxId="002",
taxFlagId="R",
taxTypeId="Tasa"
),
ProductTax(
rate=Decimal("0.100000"),
taxId="001",
taxFlagId="R",
taxTypeId="Tasa"
)
]
)
api_response = client.products.update(product)from datetime import datetime
from decimal import Decimal
from fiscalapi import Invoice, InvoiceIssuer, InvoiceItem, InvoiceRecipient
invoice = Invoice(
version_code="4.0",
series="F",
date=datetime.now().strftime("%Y-%m-%dT%H:%M:%S"),
payment_form_code="01",
payment_conditions="Contado",
currency_code="MXN",
type_code="I",
expedition_zip_code="42501",
payment_method_code="PUE",
exchange_rate=1,
export_code="01",
issuer=InvoiceIssuer(
id="3f3478b4-60fd-459e-8bfc-f8239fc96257"
),
recipient=InvoiceRecipient(
id="96b46762-d246-4a67-a562-510a25dbafa9"
),
items=[
InvoiceItem(
id="114a4be5-fb65-40b2-a762-ff0c55c6ebfa",
quantity=Decimal("1.5"),
discount=Decimal("255.85")
)
]
)
api_response = client.invoices.create(invoice)from fiscalapi import Invoice, InvoiceIssuer, InvoiceItem, InvoiceRecipient, ItemTax, TaxCredential
invoice = Invoice(
version_code="4.0",
series="F",
date=datetime.now().strftime("%Y-%m-%dT%H:%M:%S"),
payment_form_code="01",
currency_code="MXN",
type_code="I",
expedition_zip_code="42501",
payment_method_code="PUE",
exchange_rate=1,
export_code="01",
issuer=InvoiceIssuer(
tin="FUNK671228PH6",
legal_name="KARLA FUENTE NOLASCO",
tax_regime_code="621",
tax_credentials=[
TaxCredential(
base64_file="MIIFgDCCA2igAwIBAgIUMzAwMDEwMDAwMDA1MDAwMDM0NDYwDQYJKo...",
file_type=0,
password="12345678a"
),
TaxCredential(
base64_file="MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIAg...",
file_type=1,
password="12345678a"
)
]
),
recipient=InvoiceRecipient(
tin="EKU9003173C9",
legal_name="ESCUELA KEMPER URGATE",
zip_code="42501",
tax_regime_code="601",
cfdi_use_code="G01",
email="mail@domain.com"
),
items=[
InvoiceItem(
item_code="84111506",
quantity=Decimal("9.5"),
unit_of_measurement_code="E48",
description="Invoicing software as a service",
unit_price=Decimal("3587.75"),
tax_object_code="02",
item_sku="7506022301697",
discount=Decimal("255.85"),
item_taxes=[
ItemTax(
tax_code="002",
tax_type_code="Tasa",
tax_rate=Decimal("0.160000"),
tax_flag_code="T"
)
]
)
]
)
api_response = client.invoices.create(invoice)# Visite https://docs.fiscalapi.com/catalogs
# Buscar registros que contengan 'Tarjeta' en el catalogo oficial Formas de pago 'SatPaymentForms' (página 1, tamaño página 10)
api_response = client.catalogs.search_catalog("SatPaymentForms", "Tarjeta", 1, 10)
if api_response.succeeded:
for item in api_response.data.items:
print(f"Unidad: {item.description}")
else:
print(api_response.message)Cada tipo de validación solicitado consume un crédito de validación. El cobro es todo o nada: si el saldo no alcanza para todos, no se ejecuta ninguno y la API responde 403.
import base64
from pathlib import Path
from fiscalapi import SatValidationRequest, SatValidationTypeIds
# Validar un CFDI timbrado: con xml puedes solicitar cualquier tipo de validación
request = SatValidationRequest(
xml=base64.b64encode(Path("factura.xml").read_bytes()).decode("ascii"),
validation_types=[
SatValidationTypeIds.XML_STRUCTURE,
SatValidationTypeIds.CFDI_SELLO,
SatValidationTypeIds.CFDI_STATUS,
SatValidationTypeIds.BLACKLIST_69B,
]
)
api_response = client.sat_validations.validate(request)
if api_response.succeeded:
for result in api_response.data:
veredicto = "PASSED" if result.passed else "FAILED"
print(f"[{veredicto}] {result.type.id.value} -> {result.status.id.value}")
if result.status.details:
print(f" {result.status.details}")
else:
print(api_response.details)Para consultar únicamente listas negras basta el RFC, sin enviar el CFDI:
request = SatValidationRequest(
tin="FUNK671228PH6",
validation_types=[
SatValidationTypeIds.BLACKLIST_69B,
SatValidationTypeIds.BLACKLIST_69B_BIS,
]
)
api_response = client.sat_validations.validate(request)Envía xml o tin, nunca ambos y nunca ninguno: con tin solo se pueden solicitar listas negras.
El catálogo de tipos y los estatus que cada tipo puede tomar se consultan sin consumir créditos:
api_response = client.sat_validations.get_types()
api_response = client.sat_validations.get_type_by_id(SatValidationTypeIds.CFDI_STATUS)
api_response = client.sat_validations.get_statuses(SatValidationTypeIds.CFDI_STATUS)Los saldos nunca se mezclan: CreditType.STAMP mueve timbres (available_balance) y CreditType.VALIDATION mueve créditos de validación (available_validation_balance).
from fiscalapi import CreditType, StampTransactionParams
params = StampTransactionParams(
from_person_id="3f3478b4-60fd-459e-8bfc-f8239fc96257",
to_person_id="96b46762-d246-4a67-a562-510a25dbafa9",
amount=10,
comments="Asignación de créditos de validación",
credit_type=CreditType.VALIDATION
)
api_response = client.stamps.transfer_stamps(params)El complemento se envía en Invoice.complement.comercio_exterior. Puede combinarse con carta porte en el mismo comprobante.
Escala decimal: el SAT valida la cantidad de decimales de varios campos. Construye siempre los montos con
Decimal("...")a partir de una cadena con la escala exacta (Decimal("0.160000"), noDecimal(0.16)): el SDK preserva los ceros finales al serializar. Una escala incorrecta produce rechazosCFDI40179(entaxRate) oCCE122(envalorDolares).
Tipo de cambio:
tipo_cambio_usddebe ser el publicado en el DOF para la fecha de emisión, y la fecha del comprobante no puede exceder 72 horas al momento del timbrado. Si no coinciden, el PAC respondeCCE121e indica el valor esperado.
from datetime import datetime
from decimal import Decimal
from fiscalapi.models import (
Invoice, InvoiceIssuer, InvoiceRecipient, InvoiceItem, ItemTax,
InvoiceComplement, TaxCredential,
ComercioExteriorComplement, ComercioExteriorEmisor, ComercioExteriorEmisorDomicilio,
ComercioExteriorReceptor, ComercioExteriorReceptorDomicilio, ComercioExteriorMercancia,
)
invoice = Invoice(
version_code="4.0",
payment_form_code="99",
payment_method_code="PPD",
currency_code="USD",
type_code="I",
expedition_zip_code="42501",
series="CCE",
date=datetime.now().replace(microsecond=0),
export_code="02",
issuer=InvoiceIssuer(
tin="EKU9003173C9",
legal_name="ESCUELA KEMPER URGATE",
tax_regime_code="601",
tax_credentials=[
TaxCredential(base64_file="<CER_BASE64>", file_type=0, password="<CSD_PASSWORD>"),
TaxCredential(base64_file="<KEY_BASE64>", file_type=1, password="<CSD_PASSWORD>"),
],
),
# Receptor extranjero: country_id (ResidenciaFiscal) es obligatorio si se envía foreign_tin.
recipient=InvoiceRecipient(
tin="XEXX010101000",
legal_name="U.S. 0026 SW",
zip_code="42501",
tax_regime_code="616",
cfdi_use_code="S01",
country_id="USA",
foreign_tin="123456789",
),
items=[
InvoiceItem(
item_code="50211503",
item_sku="131494-1055", # debe coincidir con mercancia.no_identificacion
quantity=Decimal("2"),
unit_of_measurement_code="H87",
description="Cigarros",
unit_price=Decimal("200.00"),
discount=Decimal("0"),
tax_object_code="02",
item_taxes=[
ItemTax(tax_code="002", tax_type_code="Tasa", tax_rate=Decimal("0.160000"), tax_flag_code="T"),
],
),
],
complement=InvoiceComplement(
comercio_exterior=ComercioExteriorComplement(
clave_de_pedimento_id="A1",
certificado_origen=0, # 0 = no funge como certificado de origen, 1 = sí
incoterm_id="FOB",
tipo_cambio_usd=Decimal("16.9722"),
# Domicilio del emisor: campos de catálogo SAT, con sufijo Id.
emisor=ComercioExteriorEmisor(
domicilio=ComercioExteriorEmisorDomicilio(
calle="CALLE DEL PAPEL",
colonia_id="0214",
localidad_id="01",
municipio_id="014",
estado_id="QUE",
pais_id="MEX",
codigo_postal_id="76199",
),
),
# Domicilio del receptor: texto libre, sin sufijo Id (salvo pais_id).
receptor=ComercioExteriorReceptor(
num_reg_id_trib="123456789",
domicilio=ComercioExteriorReceptorDomicilio(
calle="ST. A",
estado="TX",
pais_id="USA",
codigo_postal="00000",
),
),
mercancias=[
ComercioExteriorMercancia(
no_identificacion="131494-1055",
fraccion_arancelaria_id="2402200100",
cantidad_aduana=Decimal("117.64"),
unidad_aduana_id="01",
valor_unitario_aduana=Decimal("3.40"),
valor_dolares=Decimal("400.00"),
),
],
),
),
)
api_response = client.invoices.create(invoice)Firma la carta manifiesto de una persona y devuelve el PDF resultante en base64.
Requiere la e.firma (FIEL) del contribuyente, no el CSD de timbrado, y que exista una persona con ese RFC en el tenant.
from fiscalapi.models import SignManifestRequest
api_response = client.manifests.sign(SignManifestRequest(
base64_cer="<FIEL_CER_BASE64>",
base64_key="<FIEL_KEY_BASE64>",
password="<FIEL_PASSWORD>",
))
if api_response.succeeded:
print(api_response.data.file_name) # EKU9003173C9.pdf
print(api_response.data.file_extension) # .pdf
print(api_response.data.base64_file) # PDF firmado en base64Tras una firma exitosa, client.people.get_by_id(...) refleja el nuevo estado en manifest_status_id.
- Facturas (CFDI) Crear facturas de ingreso, notas de crédito, complementos de pago, cancelaciones, generación de PDF/XML.
- Personas (Clientes/Emisores) Alta y administración de personas, gestión de certificados (CSD).
- Productos y Servicios Administración de catálogos de productos, búsqueda en catálogos SAT.
- Timbres y créditos de validación Listar transacciones, transferir y retirar timbres o créditos de validación entre personas.
- Validaciones SAT Validar estructura, certificado, sellos, estatus en el SAT y listas negras 69-B de un CFDI o un RFC.
- Carta manifiesto Firmar la carta manifiesto de una persona con su e.firma (FIEL) y obtener el PDF resultante.
- Gestión de Timbres
- Validaciones SAT
- Complementos de Pago
- Facturas de Nómina
- Impuestos Locales (Por Valores)
- Impuestos Locales (Por Referencias)
- Comercio Exterior (Por Valores)
- Comercio Exterior (Por Referencias)
- Carta Porte (Por Valores)
- Carta Porte (Por Referencias)
- Firma de Cartas Manifiesto
- Haz un fork del repositorio.
- Crea una rama para tu feature:
git checkout -b feature/AmazingFeature - Realiza commits de tus cambios:
git commit -m 'Add some AmazingFeature' - Sube tu rama:
git push origin feature/AmazingFeature - Abre un Pull Request en GitHub.
- Asegúrate de usar la última versión del SDK.
- Verifica si el problema ya fue reportado.
- Proporciona un ejemplo mínimo reproducible.
- Incluye los mensajes de error completos.
Este proyecto está licenciado bajo la Licencia MPL. Consulta el archivo LICENSE para más detalles.
- Documentación Oficial
- Como obtener mis credenciales
- Portal de FiscalAPI
- Soporte técnico
- Certificados de prueba
- Postman Collection
- SDKs en otros lenguajes
Desarrollado con ❤️ por Fiscalapi