> ## 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.

# Get Job Status

> Retrieves the current status and details of an extraction job using its publicId

## Description

This endpoint allows you to query the status and progress of a previously initiated extraction job. Use the job's `publicId` to track progress until completion.

<Note>
  We recommend using webhooks to receive automatic notifications instead of constant polling.
</Note>

## Parameters

<ParamField path="publicId" type="string" required>
  Unique public ID of the job obtained when creating the extraction (e.g., "JOB20250104123456789A")
</ParamField>

## Response

<ResponseField name="publicId" type="string">
  Unique extraction ID
</ResponseField>

<ResponseField name="status" type="string">
  Current extraction status:

  * `PENDING`: In queue, waiting to be processed
  * `PROCESSING`: Extracting documents from SAT
  * `COMPLETED`: Successfully completed
  * `FAILED`: Failed due to unrecoverable error
</ResponseField>

<ResponseField name="createdAt" type="string">
  Creation timestamp in ISO 8601 format
</ResponseField>

<ResponseField name="updatedAt" type="string">
  Last update timestamp
</ResponseField>

<ResponseField name="completedAt" type="string">
  Completion timestamp (only when status is COMPLETED or FAILED)
</ResponseField>

<ResponseField name="options" type="object">
  Original extraction options

  <Expandable title="Options properties">
    <ResponseField name="informationType" type="string">
      Type of information extracted
    </ResponseField>

    <ResponseField name="period" type="object">
      Time period queried
    </ResponseField>

    <ResponseField name="direction" type="string">
      Document direction (for invoices)
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="subject" type="object">
  Taxpayer information

  <Expandable title="Subject properties">
    <ResponseField name="identification" type="string">
      Taxpayer RFC
    </ResponseField>

    <ResponseField name="fullName" type="string">
      Full name obtained from SAT
    </ResponseField>

    <ResponseField name="personType" type="string">
      Person type: `MORAL` or `FISICA`
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="progress" type="object">
  Extraction progress information

  <Expandable title="Progress properties">
    <ResponseField name="discoveryCount" type="number">
      Total documents found
    </ResponseField>

    <ResponseField name="completedCount" type="number">
      Documents processed successfully
    </ResponseField>

    <ResponseField name="failedCount" type="number">
      Documents that failed to process
    </ResponseField>

    <ResponseField name="percentage" type="number">
      Progress percentage (0-100)
    </ResponseField>

    <ResponseField name="estimatedTimeRemaining" type="string">
      Estimated remaining time in ISO 8601 duration format
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="documents" type="array">
  List of extracted documents (only when status is COMPLETED)

  <Expandable title="Properties of each document">
    <ResponseField name="id" type="string">
      Unique document ID
    </ResponseField>

    <ResponseField name="uuid" type="string">
      Fiscal UUID of the document
    </ResponseField>

    <ResponseField name="type" type="string">
      Document type
    </ResponseField>

    <ResponseField name="issueDate" type="string">
      Issue date
    </ResponseField>

    <ResponseField name="amount" type="number">
      Document amount
    </ResponseField>

    <ResponseField name="currency" type="string">
      Currency (default MXN)
    </ResponseField>

    <ResponseField name="emitter" type="object">
      Emitter information
    </ResponseField>

    <ResponseField name="receiver" type="object">
      Receiver information
    </ResponseField>

    <ResponseField name="availableFormats" type="array">
      Available formats for download: \["XML", "PDF"]
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="error" type="object">
  Error information (only when status is FAILED)

  <Expandable title="Error properties">
    <ResponseField name="code" type="string">
      Error code
    </ResponseField>

    <ResponseField name="message" type="string">
      Error description
    </ResponseField>

    <ResponseField name="details" type="object">
      Additional error details
    </ResponseField>
  </Expandable>
</ResponseField>

## Examples

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://api.taxo.co/v1/extractions/JOB20250104123456789A" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```javascript Node.js theme={null}
  async function getExtractionStatus(extractionId) {
    const response = await fetch(
      `https://api.taxo.co/v1/extractions/${extractionId}`,
      {
        headers: {
          'Authorization': `Bearer ${process.env.TAXO_API_KEY}`
        }
      }
    );
    
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    
    return await response.json();
  }

  // Usage
  const status = await getExtractionStatus('JOB20250104123456789A');
  console.log(`Status: ${status.status}, Progress: ${status.progress.percentage}%`);
  ```

  ```python Python theme={null}
  import requests

  def get_extraction_status(extraction_id):
      response = requests.get(
          f'https://api.taxo.co/v1/extractions/{extraction_id}',
          headers={
              'Authorization': f'Bearer {os.environ["TAXO_API_KEY"]}'
          }
      )
      response.raise_for_status()
      return response.json()

  # Usage
  status = get_extraction_status('JOB20250104123456789A')
  print(f'Status: {status["status"]}, Progress: {status["progress"]["percentage"]}%')
  ```

  ```java Java theme={null}
  public ExtractionStatus getExtractionStatus(String extractionId) {
      HttpHeaders headers = new HttpHeaders();
      headers.set("Authorization", "Bearer " + System.getenv("TAXO_API_KEY"));
      
      HttpEntity<String> entity = new HttpEntity<>(headers);
      
      ResponseEntity<ExtractionStatus> response = restTemplate.exchange(
          "https://api.taxo.co/v1/extractions/" + extractionId,
          HttpMethod.GET,
          entity,
          ExtractionStatus.class
      );
      
      return response.getBody();
  }

  // Usage
  ExtractionStatus status = getExtractionStatus("JOB20250104123456789A");
  System.out.println("Status: " + status.getStatus() + 
                    ", Progress: " + status.getProgress().getPercentage() + "%");
  ```
</CodeGroup>

## Example responses

<CodeGroup>
  ```json Extraction in progress theme={null}
  {
    "publicId": "JOB20250104123456789A",
    "status": "PROCESSING",
    "createdAt": "2025-01-04T12:34:56.789Z",
    "updatedAt": "2025-01-04T12:45:23.456Z",
    "options": {
      "informationType": "INVOICE",
      "period": {
        "from": "2024-01-01",
        "to": "2024-12-31"
      },
      "direction": "RECEIVED"
    },
    "subject": {
      "identification": "ABC010101ABC",
      "fullName": "Empresa ABC S.A. de C.V.",
      "personType": "MORAL"
    },
    "progress": {
      "discoveryCount": 1500,
      "completedCount": 750,
      "failedCount": 2,
      "percentage": 50,
      "estimatedTimeRemaining": "PT15M"
    }
  }
  ```

  ```json Extraction completed theme={null}
  {
    "publicId": "JOB20250104123456789A",
    "status": "COMPLETED",
    "createdAt": "2025-01-04T12:34:56.789Z",
    "updatedAt": "2025-01-04T13:15:42.123Z",
    "completedAt": "2025-01-04T13:15:42.123Z",
    "options": {
      "informationType": "INVOICE",
      "period": {
        "from": "2024-01-01",
        "to": "2024-12-31"
      },
      "direction": "RECEIVED"
    },
    "subject": {
      "identification": "ABC010101ABC",
      "fullName": "Empresa ABC S.A. de C.V.",
      "personType": "MORAL"
    },
    "progress": {
      "discoveryCount": 1500,
      "completedCount": 1498,
      "failedCount": 2,
      "percentage": 100
    },
    "documents": [
      {
        "id": "doc_550e8400-e29b-41d4-a716-446655440000",
        "uuid": "12345678-1234-1234-1234-123456789012",
        "type": "INVOICE",
        "issueDate": "2024-03-15T10:30:00Z",
        "amount": 1160.00,
        "currency": "MXN",
        "emitter": {
          "rfc": "XYZ020202XYZ",
          "name": "Proveedor XYZ S.A. de C.V."
        },
        "receiver": {
          "rfc": "ABC010101ABC",
          "name": "Empresa ABC S.A. de C.V."
        },
        "availableFormats": ["XML", "PDF"]
      }
    ]
  }
  ```

  ```json Extraction failed theme={null}
  {
    "publicId": "JOB20250104123456789A",
    "status": "FAILED",
    "createdAt": "2025-01-04T12:34:56.789Z",
    "updatedAt": "2025-01-04T12:38:12.456Z",
    "completedAt": "2025-01-04T12:38:12.456Z",
    "options": {
      "informationType": "INVOICE",
      "period": {
        "from": "2024-01-01",
        "to": "2024-12-31"
      },
      "direction": "RECEIVED"
    },
    "subject": {
      "identification": "ABC010101ABC",
      "fullName": "Empresa ABC S.A. de C.V.",
      "personType": "MORAL"
    },
    "progress": {
      "discoveryCount": 0,
      "completedCount": 0,
      "failedCount": 0,
      "percentage": 0
    },
    "error": {
      "code": "INVALID_CREDENTIALS",
      "message": "The provided CIEC credentials are incorrect",
      "details": {
        "satResponse": "Incorrect username or password",
        "suggestion": "Verify that the password is correctly encoded in base64"
      }
    }
  }
  ```
</CodeGroup>

## Efficient polling implementation

<CodeGroup>
  ```javascript Polling with exponential backoff theme={null}
  async function pollUntilComplete(extractionId, maxAttempts = 120) {
    const delays = [5, 10, 20, 30, 60]; // seconds
    let attempt = 0;
    
    while (attempt < maxAttempts) {
      try {
        const status = await getExtractionStatus(extractionId);
        
        console.log(`Attempt ${attempt + 1}: ${status.status} - ${status.progress.percentage}%`);
        
        if (status.status === 'COMPLETED') {
          console.log(`✅ Extraction completed: ${status.progress.completedCount} documents`);
          return status;
        }
        
        if (status.status === 'FAILED') {
          throw new Error(`❌ Extraction failed: ${status.error.message}`);
        }
        
        // Calculate delay with exponential backoff
        const delayIndex = Math.min(attempt, delays.length - 1);
        const delay = delays[delayIndex] * 1000;
        
        console.log(`⏳ Waiting ${delays[delayIndex]} seconds...`);
        await new Promise(resolve => setTimeout(resolve, delay));
        
        attempt++;
        
      } catch (error) {
        console.error('Error querying status:', error);
        throw error;
      }
    }
    
    throw new Error('Timeout: The extraction did not complete within the expected time');
  }

  // Usage
  try {
    const result = await pollUntilComplete('JOB20250104123456789A');
    console.log('Available documents:', result.documents.length);
  } catch (error) {
    console.error('Polling error:', error.message);
  }
  ```

  ```python Polling with retries theme={null}
  import time
  import requests
  from typing import Dict, Any

  def poll_until_complete(extraction_id: str, max_attempts: int = 120) -> Dict[str, Any]:
      delays = [5, 10, 20, 30, 60]  # seconds
      attempt = 0
      
      while attempt < max_attempts:
          try:
              status = get_extraction_status(extraction_id)
              
              print(f"Attempt {attempt + 1}: {status['status']} - {status['progress']['percentage']}%")
              
              if status['status'] == 'COMPLETED':
                  print(f"✅ Extraction completed: {status['progress']['completedCount']} documents")
                  return status
              
              if status['status'] == 'FAILED':
                  raise Exception(f"❌ Extraction failed: {status['error']['message']}")
              
              # Calculate delay with exponential backoff
              delay_index = min(attempt, len(delays) - 1)
              delay = delays[delay_index]
              
              print(f"⏳ Waiting {delay} seconds...")
              time.sleep(delay)
              
              attempt += 1
              
          except requests.RequestException as e:
              print(f"Error querying status: {e}")
              raise
      
      raise TimeoutError("The extraction did not complete within the expected time")

  # Usage
  try:
      result = poll_until_complete('JOB20250104123456789A')
      print(f'Available documents: {len(result["documents"])}')
  except Exception as e:
      print(f'Polling error: {e}')
  ```
</CodeGroup>

## Extraction states

<AccordionGroup>
  <Accordion title="PENDING" icon="clock">
    The extraction is in queue waiting to be processed. This may take a few minutes during peak hours.
  </Accordion>

  <Accordion title="PROCESSING" icon="gear">
    The extraction is in progress. The `progress.percentage` field shows the current progress.
  </Accordion>

  <Accordion title="COMPLETED" icon="check">
    The extraction completed successfully. Documents are available for download.
  </Accordion>

  <Accordion title="FAILED" icon="xmark">
    The extraction failed due to an unrecoverable error. Check the `error` field for more details.
  </Accordion>
</AccordionGroup>

## Error codes

<ResponseExample>
  ```json Error 404 - Extraction not found theme={null}
  {
    "error": {
      "code": "EXTRACTION_NOT_FOUND",
      "message": "No extraction found with the provided ID",
      "details": {
        "extractionId": "JOB20250104123456789A"
      }
    }
  }
  ```
</ResponseExample>

<Tip>
  **Optimization:** Instead of frequent polling, configure webhooks to receive automatic notifications when the extraction completes.
</Tip>
