This guide will help you perform your first SAT invoice extraction using the Taxo API.
Before you start
You need:- A Taxo API key (get it here)
- CIEC or FIEL credentials from a taxpayer
- A tool to make HTTP requests (cURL, Postman, or code)
Step 1: Set up authentication
Store your API key securely:export TAXO_API_KEY="your_api_key_here"
TAXO_API_KEY=your_api_key_here
TAXO_BASE_URL=https://api.taxo.co
TAXO_API_KEY=your_api_key_here
TAXO_BASE_URL=https://api.taxo.co
Step 2: Create your first extraction
Let’s extract received invoices from 2024 for a specific RFC:curl -X POST "https://api.taxo.co/v1/extractions" \
-H "Authorization: Bearer $TAXO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"subject": {
"identifier": "ABC010101ABC",
"name": "Mi Empresa S.A. de C.V."
},
"credentials": {
"type": "CIEC",
"password": "'"$(echo -n 'my_sat_password' | base64)"'"
},
"options": {
"informationType": "INVOICE",
"period": {
"from": "2024-01-01",
"to": "2024-12-31"
},
"direction": "RECEIVED"
}
}'
const response = await fetch('https://api.taxo.co/v1/extractions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.TAXO_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
subject: {
identifier: 'ABC010101ABC',
name: 'Mi Empresa S.A. de C.V.'
},
credentials: {
type: 'CIEC',
password: Buffer.from('my_sat_password').toString('base64')
},
options: {
informationType: 'INVOICE',
period: {
from: '2024-01-01',
to: '2024-12-31'
},
direction: 'RECEIVED'
}
})
});
const extraction = await response.json();
console.log('Extraction created:', extraction.publicId);
import requests
import base64
import os
# Codificar contraseña
password_b64 = base64.b64encode('my_sat_password'.encode()).decode()
response = requests.post(
'https://api.taxo.co/v1/extractions',
json={
'subject': {
'identifier': 'ABC010101ABC',
'name': 'Mi Empresa S.A. de C.V.'
},
'credentials': {
'type': 'CIEC',
'password': password_b64
},
'options': {
'informationType': 'INVOICE',
'period': {
'from': '2024-01-01',
'to': '2024-12-31'
},
'direction': 'RECEIVED'
}
},
headers={
'Authorization': f'Bearer {os.environ["TAXO_API_KEY"]}',
'Content-Type': 'application/json'
}
)
extraction = response.json()
print(f'Extraction created: {extraction["publicId"]}')
{
"publicId": "JOB20250104123456789A",
"status": "PENDING",
"createdAt": "2025-01-04T12:34:56.789Z",
"subject": {
"identification": "ABC010101ABC",
"fullName": "Mi Empresa S.A. de C.V.",
"personType": "MORAL"
},
"discoveryCount": 0,
"completedCount": 0,
"failedCount": 0
}
Save the
publicId - you’ll need it to check status and download documents.Step 3: Monitor progress
Extraction is an asynchronous process. Check the status regularly:# Replace JOB20250104123456789A with your publicId
curl -X GET "https://api.taxo.co/v1/extractions/JOB20250104123456789A" \
-H "Authorization: Bearer $TAXO_API_KEY"
async function waitForCompletion(extractionId) {
console.log('Esperando que se complete la extracción...');
while (true) {
const response = await fetch(
`https://api.taxo.co/v1/extractions/${extractionId}`,
{
headers: {
'Authorization': `Bearer ${process.env.TAXO_API_KEY}`
}
}
);
const status = await response.json();
console.log(`Estado: ${status.status} - Progreso: ${status.progress?.percentage || 0}%`);
if (status.status === 'COMPLETED') {
console.log(`✅ ¡Completado! ${status.progress.completedCount} documentos extraídos`);
return status;
}
if (status.status === 'FAILED') {
throw new Error(`❌ Extracción falló: ${status.error?.message}`);
}
// Esperar 30 segundos antes del siguiente check
await new Promise(resolve => setTimeout(resolve, 30000));
}
}
// Usar la función
const result = await waitForCompletion('JOB20250104123456789A');
import time
def wait_for_completion(extraction_id):
print('Esperando que se complete la extracción...')
while True:
response = requests.get(
f'https://api.taxo.co/v1/extractions/{extraction_id}',
headers={'Authorization': f'Bearer {os.environ["TAXO_API_KEY"]}'}
)
status = response.json()
progress = status.get('progress', {})
print(f'Estado: {status["status"]} - Progreso: {progress.get("percentage", 0)}%')
if status['status'] == 'COMPLETED':
print(f'✅ ¡Completado! {progress["completedCount"]} documentos extraídos')
return status
if status['status'] == 'FAILED':
error_msg = status.get('error', {}).get('message', 'Error desconocido')
raise Exception(f'❌ Extracción falló: {error_msg}')
# Esperar 30 segundos antes del siguiente check
time.sleep(30)
# Usar la función
result = wait_for_completion('JOB20250104123456789A')
Step 4: Download documents
Once the extraction is complete, you can download the documents:# Download the first document in XML format
curl -X GET "https://api.taxo.co/v1/extractions/JOB20250104123456789A/documents/DOCUMENT_ID/download?type=XML" \
-H "Authorization: Bearer $TAXO_API_KEY" \
-o "factura.xml"
// Descargar el primer documento disponible
async function downloadFirstDocument(extractionStatus) {
if (extractionStatus.documents.length === 0) {
console.log('No hay documentos para descargar');
return;
}
const firstDoc = extractionStatus.documents[0];
const format = firstDoc.availableFormats[0]; // XML o PDF
const response = await fetch(
`https://api.taxo.co/v1/extractions/${extractionStatus.publicId}/documents/${firstDoc.id}/download?type=${format}`,
{
headers: {
'Authorization': `Bearer ${process.env.TAXO_API_KEY}`
}
}
);
if (!response.ok) {
throw new Error(`Error descargando: ${response.status}`);
}
const buffer = Buffer.from(await response.arrayBuffer());
const filename = `documento_${firstDoc.uuid}.${format.toLowerCase()}`;
require('fs').writeFileSync(filename, buffer);
console.log(`✅ Documento descargado: ${filename}`);
}
// Usar después de que la extracción esté completa
await downloadFirstDocument(result);
def download_first_document(extraction_status):
if not extraction_status.get('documents'):
print('No hay documentos para descargar')
return
first_doc = extraction_status['documents'][0]
format_type = first_doc['availableFormats'][0] # XML o PDF
response = requests.get(
f'https://api.taxo.co/v1/extractions/{extraction_status["publicId"]}/documents/{first_doc["id"]}/download',
params={'type': format_type},
headers={'Authorization': f'Bearer {os.environ["TAXO_API_KEY"]}'}
)
response.raise_for_status()
filename = f'documento_{first_doc["uuid"]}.{format_type.lower()}'
with open(filename, 'wb') as f:
f.write(response.content)
print(f'✅ Documento descargado: {filename}')
# Usar después de que la extracción esté completa
download_first_document(result)
Complete example script
Here’s a complete script that performs the entire process:const fetch = require('node-fetch');
const fs = require('fs');
const API_KEY = process.env.TAXO_API_KEY;
const BASE_URL = 'https://api.taxo.co';
async function main() {
try {
console.log('🚀 Iniciando extracción de facturas...');
// 1. Crear extracción
const extraction = await createExtraction();
console.log(`✅ Extracción creada: ${extraction.publicId}`);
// 2. Esperar a que complete
const result = await waitForCompletion(extraction.publicId);
// 3. Descargar primeros 5 documentos
await downloadDocuments(result, 5);
console.log('🎉 ¡Proceso completado exitosamente!');
} catch (error) {
console.error('❌ Error:', error.message);
process.exit(1);
}
}
async function createExtraction() {
const response = await fetch(`${BASE_URL}/v1/extractions`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
subject: {
identifier: 'ABC010101ABC', // Reemplaza con el RFC real
name: 'Mi Empresa S.A. de C.V.'
},
credentials: {
type: 'CIEC',
password: Buffer.from('my_sat_password').toString('base64') // Reemplaza
},
options: {
informationType: 'INVOICE',
period: {
from: '2024-01-01',
to: '2024-03-31' // Solo Q1 para el ejemplo
},
direction: 'RECEIVED'
}
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Error creando extracción: ${error.error?.message}`);
}
return await response.json();
}
async function waitForCompletion(extractionId) {
console.log('⏳ Esperando que se complete la extracción...');
const delays = [5, 10, 20, 30, 60]; // Backoff exponencial
let delayIndex = 0;
while (true) {
const response = await fetch(`${BASE_URL}/v1/extractions/${extractionId}`, {
headers: { 'Authorization': `Bearer ${API_KEY}` }
});
const status = await response.json();
const progress = status.progress || {};
console.log(` Estado: ${status.status} - Progreso: ${progress.percentage || 0}%`);
if (status.status === 'COMPLETED') {
console.log(`✅ ¡Completado! ${progress.completedCount} documentos extraídos`);
return status;
}
if (status.status === 'FAILED') {
throw new Error(`Extracción falló: ${status.error?.message}`);
}
// Esperar con backoff exponencial
const delay = delays[Math.min(delayIndex++, delays.length - 1)] * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
async function downloadDocuments(extractionStatus, maxDocuments = 5) {
const documents = extractionStatus.documents.slice(0, maxDocuments);
console.log(`📥 Descargando ${documents.length} documentos...`);
for (let i = 0; i < documents.length; i++) {
const doc = documents[i];
const format = doc.availableFormats[0];
console.log(` Descargando ${i + 1}/${documents.length}: ${doc.uuid}`);
const response = await fetch(
`${BASE_URL}/v1/extractions/${extractionStatus.publicId}/documents/${doc.id}/download?type=${format}`,
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
if (!response.ok) {
console.warn(` ⚠️ Error descargando ${doc.uuid}: ${response.status}`);
continue;
}
const buffer = Buffer.from(await response.arrayBuffer());
const filename = `documento_${i + 1}_${doc.uuid}.${format.toLowerCase()}`;
fs.writeFileSync(filename, buffer);
console.log(` ✅ Guardado: ${filename}`);
}
}
// Ejecutar si es llamado directamente
if (require.main === module) {
main();
}
import requests
import base64
import time
import os
API_KEY = os.environ['TAXO_API_KEY']
BASE_URL = 'https://api.taxo.co'
def main():
try:
print('🚀 Iniciando extracción de facturas...')
# 1. Crear extracción
extraction = create_extraction()
print(f'✅ Extracción creada: {extraction["publicId"]}')
# 2. Esperar a que complete
result = wait_for_completion(extraction['publicId'])
# 3. Descargar primeros 5 documentos
download_documents(result, max_documents=5)
print('🎉 ¡Proceso completado exitosamente!')
except Exception as error:
print(f'❌ Error: {error}')
exit(1)
def create_extraction():
# Codificar contraseña en base64
password_b64 = base64.b64encode('my_sat_password'.encode()).decode()
response = requests.post(
f'{BASE_URL}/v1/extractions',
json={
'subject': {
'identifier': 'ABC010101ABC', # Reemplaza con el RFC real
'name': 'Mi Empresa S.A. de C.V.'
},
'credentials': {
'type': 'CIEC',
'password': password_b64 # Reemplaza con la contraseña real
},
'options': {
'informationType': 'INVOICE',
'period': {
'from': '2024-01-01',
'to': '2024-03-31' # Solo Q1 para el ejemplo
},
'direction': 'RECEIVED'
}
},
headers={
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'
}
)
if not response.ok:
error = response.json()
raise Exception(f'Error creando extracción: {error.get("error", {}).get("message")}')
return response.json()
def wait_for_completion(extraction_id):
print('⏳ Esperando que se complete la extracción...')
delays = [5, 10, 20, 30, 60] # Backoff exponencial
delay_index = 0
while True:
response = requests.get(
f'{BASE_URL}/v1/extractions/{extraction_id}',
headers={'Authorization': f'Bearer {API_KEY}'}
)
status = response.json()
progress = status.get('progress', {})
print(f' Estado: {status["status"]} - Progreso: {progress.get("percentage", 0)}%')
if status['status'] == 'COMPLETED':
print(f'✅ ¡Completado! {progress["completedCount"]} documentos extraídos')
return status
if status['status'] == 'FAILED':
error_msg = status.get('error', {}).get('message', 'Error desconocido')
raise Exception(f'Extracción falló: {error_msg}')
# Esperar con backoff exponencial
delay = delays[min(delay_index, len(delays) - 1)]
delay_index += 1
time.sleep(delay)
def download_documents(extraction_status, max_documents=5):
documents = extraction_status['documents'][:max_documents]
print(f'📥 Descargando {len(documents)} documentos...')
for i, doc in enumerate(documents):
format_type = doc['availableFormats'][0]
print(f' Descargando {i + 1}/{len(documents)}: {doc["uuid"]}')
response = requests.get(
f'{BASE_URL}/v1/extractions/{extraction_status["publicId"]}/documents/{doc["id"]}/download',
params={'type': format_type},
headers={'Authorization': f'Bearer {API_KEY}'}
)
if not response.ok:
print(f' ⚠️ Error descargando {doc["uuid"]}: {response.status_code}')
continue
filename = f'documento_{i + 1}_{doc["uuid"]}.{format_type.lower()}'
with open(filename, 'wb') as f:
f.write(response.content)
print(f' ✅ Guardado: {filename}')
if __name__ == '__main__':
main()
Next steps
Congratulations! You now know how to use the basic Taxo API. You can now:Set up webhooks
Receive automatic notifications when extractions are ready
Explore endpoints
Discover all advanced filtering and configuration options
Best practices
Learn how to optimize performance and handle errors
Official SDKs
Use our official libraries for your favorite language
Need help?
If you have problems following this guide, contact our support team:
- Email: api-support@taxo.co
- Live chat from the dashboard