Get Job Status
curl --request GET \
--url https://api.taxo.co/v1/extractions/{publicId}import requests
url = "https://api.taxo.co/v1/extractions/{publicId}"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://api.taxo.co/v1/extractions/{publicId}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.taxo.co/v1/extractions/{publicId}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.taxo.co/v1/extractions/{publicId}"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.taxo.co/v1/extractions/{publicId}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.taxo.co/v1/extractions/{publicId}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_body{
"error": {
"code": "EXTRACTION_NOT_FOUND",
"message": "No extraction found with the provided ID",
"details": {
"extractionId": "JOB20250104123456789A"
}
}
}
SAT API Reference
Get Job Status
Retrieves the current status and details of an extraction job using its publicId
GET
/
v1
/
extractions
/
{publicId}
Get Job Status
curl --request GET \
--url https://api.taxo.co/v1/extractions/{publicId}import requests
url = "https://api.taxo.co/v1/extractions/{publicId}"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://api.taxo.co/v1/extractions/{publicId}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.taxo.co/v1/extractions/{publicId}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.taxo.co/v1/extractions/{publicId}"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.taxo.co/v1/extractions/{publicId}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.taxo.co/v1/extractions/{publicId}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_body{
"error": {
"code": "EXTRACTION_NOT_FOUND",
"message": "No extraction found with the provided ID",
"details": {
"extractionId": "JOB20250104123456789A"
}
}
}
Description
This endpoint allows you to query the status and progress of a previously initiated extraction job. Use the job’spublicId to track progress until completion.
We recommend using webhooks to receive automatic notifications instead of constant polling.
Parameters
string
required
Unique public ID of the job obtained when creating the extraction (e.g., “JOB20250104123456789A”)
Response
string
Unique extraction ID
string
Current extraction status:
PENDING: In queue, waiting to be processedPROCESSING: Extracting documents from SATCOMPLETED: Successfully completedFAILED: Failed due to unrecoverable error
string
Creation timestamp in ISO 8601 format
string
Last update timestamp
string
Completion timestamp (only when status is COMPLETED or FAILED)
object
object
object
array
List of extracted documents (only when status is COMPLETED)
object
Examples
curl -X GET "https://api.taxo.co/v1/extractions/JOB20250104123456789A" \
-H "Authorization: Bearer YOUR_API_KEY"
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}%`);
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"]}%')
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() + "%");
Example responses
{
"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"
}
}
{
"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"]
}
]
}
{
"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"
}
}
}
Efficient polling implementation
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);
}
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}')
Extraction states
PENDING
PENDING
The extraction is in queue waiting to be processed. This may take a few minutes during peak hours.
PROCESSING
PROCESSING
The extraction is in progress. The
progress.percentage field shows the current progress.COMPLETED
COMPLETED
The extraction completed successfully. Documents are available for download.
FAILED
FAILED
The extraction failed due to an unrecoverable error. Check the
error field for more details.Error codes
{
"error": {
"code": "EXTRACTION_NOT_FOUND",
"message": "No extraction found with the provided ID",
"details": {
"extractionId": "JOB20250104123456789A"
}
}
}
Optimization: Instead of frequent polling, configure webhooks to receive automatic notifications when the extraction completes.
⌘I