> ## Documentation Index
> Fetch the complete documentation index at: https://docs-mx.taxo.ws/llms.txt
> Use this file to discover all available pages before exploring further.

# Quick Start

> Perform your first tax document extraction in under 5 minutes

<Note>
  This guide will help you perform your first SAT invoice extraction using the Taxo API.
</Note>

## Before you start

You need:

* A Taxo API key ([get it here](https://dashboard.taxo.co/api-keys))
* 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:

<CodeGroup>
  ```bash Terminal theme={null}
  export TAXO_API_KEY="your_api_key_here"
  ```

  ```javascript .env theme={null}
  TAXO_API_KEY=your_api_key_here
  TAXO_BASE_URL=https://api.taxo.co
  ```

  ```python .env theme={null}
  TAXO_API_KEY=your_api_key_here
  TAXO_BASE_URL=https://api.taxo.co
  ```
</CodeGroup>

## Step 2: Create your first extraction

Let's extract received invoices from 2024 for a specific RFC:

<CodeGroup>
  ```bash cURL theme={null}
  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"
      }
    }'
  ```

  ```javascript Node.js theme={null}
  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);
  ```

  ```python Python theme={null}
  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"]}')
  ```
</CodeGroup>

**Expected response:**

```json theme={null}
{
  "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
}
```

<Info>
  Save the `publicId` - you'll need it to check status and download documents.
</Info>

## Step 3: Monitor progress

Extraction is an asynchronous process. Check the status regularly:

<CodeGroup>
  ```bash cURL theme={null}
  # Replace JOB20250104123456789A with your publicId
  curl -X GET "https://api.taxo.co/v1/extractions/JOB20250104123456789A" \
    -H "Authorization: Bearer $TAXO_API_KEY"
  ```

  ```javascript Node.js theme={null}
  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');
  ```

  ```python Python theme={null}
  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')
  ```
</CodeGroup>

## Step 4: Download documents

Once the extraction is complete, you can download the documents:

<CodeGroup>
  ```bash cURL theme={null}
  # 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"
  ```

  ```javascript Node.js theme={null}
  // 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);
  ```

  ```python Python theme={null}
  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)
  ```
</CodeGroup>

## Complete example script

Here's a complete script that performs the entire process:

<CodeGroup>
  ```javascript complete-example.js theme={null}
  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();
  }
  ```

  ```python complete_example.py theme={null}
  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()
  ```
</CodeGroup>

## Next steps

Congratulations! You now know how to use the basic Taxo API. You can now:

<CardGroup cols={2}>
  <Card title="Set up webhooks" icon="webhook" href="/api-reference/taxo-b2b/webhooks/overview">
    Receive automatic notifications when extractions are ready
  </Card>

  <Card title="Explore endpoints" icon="code" href="/api-reference/taxo-b2b/endpoints/create-extraction">
    Discover all advanced filtering and configuration options
  </Card>

  <Card title="Best practices" icon="lightbulb" href="/api-reference/taxo-b2b/best-practices">
    Learn how to optimize performance and handle errors
  </Card>

  <Card title="Official SDKs" icon="code-simple" href="/api-reference/taxo-b2b/sdks">
    Use our official libraries for your favorite language
  </Card>
</CardGroup>

## Need help?

<Info>
  If you have problems following this guide, contact our support team:

  * Email: [api-support@taxo.co](mailto:api-support@taxo.co)
  * Live chat from the [dashboard](https://dashboard.taxo.co)
</Info>
