Skip to main content
Below describes an example workflow of API calls that can be made in order to process an SOV and retrieve results using the Ping.Extraction API. The following is a specific example, there are more options available to the user that are documented in the Parse SOVs pages under Ping.Extraction. The code blocks allow the user to select from Python or cURL (via terminal) use cases. The sample SOV and Python script are both available for download at the bottom of the page. Try filling in the blanks (e.g., {id}) to go through the workflow using cURL!

1. Start SOV Parsing Job

The first API call to make is Start SOV Parsing Job. This endpoint allows the user to submit an Excel file to Ping servers to be parsed as an SOV. Example code:
parse_sov.py
import time
from pathlib import Path
import requests
import os

# authentication token that allows you to make requests to the API
API_KEY=os.environ.get('SOVFIXER_AUTH_TOKEN')
headers = {"Authorization": f"Token {API_KEY}"}

# file being submitted
file_path = Path("parse_sov_testfile.xlsx")
files = {"file": ("parse_sov_testfile.xlsx", open(file_path, "rb"))}
# request options
payload = {
    # what kind of document is being processed? SOV
    "document_type": "SOV",
    # how should the file be outputted? json, auditor
    "output_formats": ["json", "auditor"],
    # what data integrations should be used? Ping Geocoding, Ping Hazard
    "integrations": ["PG", "PH"]
}

# 1. API URL for Start SOV Parsing Job: https://api.sovfixer.com/api/v1/sov
start_job_url = f"https://api.sovfixer.com/api/v1/sov"
# API response
start_job_response = requests.post(start_job_url, data=payload, files=files, headers=headers)
# check response status and record the SOV ID
if start_job_response.status_code in (200, 201):
    sovid = start_job_response.json()["id"]
else:
    raise RuntimeError

## ... 
Example response:
Python output
{'id': 's-pl-ping-21nyms3', 'message': 'OK'}

2. Check SOV Parsing Status

The second API call to make is Check SOV Parsing Job. This endpoint allows the user to check the status of the SOV parsing job by providing the SOV ID generated in step 1 into the API path parameter. E.g., https://api.sovfixer.com/api/v1/sov/s-pl-ping-21nyms3 Example code:
parse_sov.py
## ... 

# 2. API URL for Check SOV Parsing Job: https://api.sovfixer.com/api/v1/sov/{id}
check_job_url = f"https://api.sovfixer.com/api/v1/sov/{sovid}"
check_job_response = requests.get(check_job_url, headers=headers)
# ensure response is in a good state
assert check_job_response.status_code in (200, 201)
check_job_json = check_job_response.json()
# Poll every three seconds for job completion
while check_job_json["request"]["status"] not in ("COMPLETE", "FAILED"):
    print(check_job_json)
    print("Waiting 3 seconds to request again...")
    time.sleep(3)
    check_job_response = requests.get(check_job_url, headers=headers)
    # ensure response is in a good state
    assert check_job_response.status_code in (200, 201)
    check_job_json = check_job_response.json()

print(check_job_json)
assert check_job_json["request"]["status"] != "FAILED", "SOV parsing job failed"

# record filenames and urls of the processed SOV outputs
outputs = []
for output in check_job_json["result"]["outputs"]:
    outputs.append(
            {
                "filename": output["filename"], 
                "url": output["url"]
            }
        )

## ...
Example response:
Python output
{
   "request":{
      "completed_at":"2026-01-13T17: 00: 05.982Z",
      "last_health_check_time":"2026-01-13T17: 00: 06.498Z",
      "last_health_status":"Processing completed, storing internal artifacts.",
      "pct_complete":100,
      "progress_started_at":"2026-01-13T16: 59: 59.443Z",
      "requested_at":"2026-01-13T16: 59: 58.841Z",
      "status":"COMPLETE"
   },
   "result":{
      "inputs":[
         {
            "filename":"parse_sov_testfile.xlsx",
            "identified_document_type":"SOV",
            "status":"SUCCESS",
            "status_message":"Parsed.",
            "url":"https://api.sovfixer.com/api/v1/sov/s-pl-ping-21nyms3/input/parse_sov_testfile.xlsx"
         }
      ],
      "message":"Success.",
      "outputs":[
         {
            "description":"JSON",
            "filename":"parse_sov_testfile.json",
            "url":"https://api.sovfixer.com/api/v1/sov/s-pl-ping-21nyms3/output/parse_sov_testfile.json"
         },
         {
            "url":"https://api.sovfixer.com/api/v1/sov/s-pl-ping-21nyms3/output/parse_sov_testfile-Auditor.xlsx",
            "filename":"parse_sov_testfile-Auditor.xlsx",
            "description":"Ping SOV"
         }
      ],
      "status":"SUCCESS"
   }
}

3. Fetch Outputs of SOV Parsing Job

The third API call to make is Fetch Outputs of SOV Parsing Job. This endpoint allows the user to receive the contents of the completed SOV parsing job by providing the SOV ID generated in step 1 and the output filename generated in step 2 into the API path parameter. E.g., https://api.sovfixer.com/api/v1/sov/s-pl-ping-21nyms3/output/parse_sov_testfile.json Example code:
parse_sov.py
## ...
# 3. API URL for Fetch Outputs of SOV Parsing Job: https://api.sovfixer.com/api/v1/sov/{id}/output/{filename}
for output in outputs:
    fetch_outputs_response = requests.get(output["url"], headers=headers)
    # ensure response is in a good state
    assert fetch_outputs_response.status_code in (200, 201)
    print(f"saving {output['filename']}")
    with open(f"workflow_example_results/{output['filename']}", "wb") as outfile:
        outfile.write(fetch_outputs_response.content)    
    print(f"saved {output['filename']}")
Example response:
Python output
saving parse_sov_testfile.json
'id': 's-pl-ping-3yskss'
'source_filename': 'parse_sov_testfile.xlsx'
'num_buildings': 311
saved parse_sov_testfile.json
saving parse_sov_testfile-Auditor.xlsx
saved parse_sov_testfile-Auditor.xlsx

Python Demo

                     Download Python Script here ||| Download Example SOV here
parse_sov.py
import json
import time
from pathlib import Path
import requests
import os

# authentication token that allows you to make requests to the API
API_KEY=os.environ.get('SOVFIXER_AUTH_TOKEN')
headers = {"Authorization": f"Token {API_KEY}"}

# file being submitted
file_path = Path("parse_sov_testfile.xlsx")
files = {"file": ("parse_sov_testfile.xlsx", open(file_path, "rb"))}
# request options
payload = {
    # what kind of document is being processed? SOV
    "document_type": "SOV",
    # how should the file be outputted? json, auditor
    "output_formats": ["json", "auditor"],
    # what data integrations should be used? Ping Geocoding, Ping Hazard
    "integrations": ["PG", "PH"]
}

# 1. API URL for Start SOV Parsing Job: https://api.sovfixer.com/api/v1/sov
start_job_url = f"https://api.sovfixer.com/api/v1/sov"
# API response
start_job_response = requests.post(start_job_url, data=payload, files=files, headers=headers)
# check response status and record the SOV ID
if start_job_response.ok is True:
    sovid = start_job_response.json()["id"]
else:
    raise ValueError

## ... 

# 2. API URL for Check SOV Parsing Job: https://api.sovfixer.com/api/v1/sov/{id}
check_job_url = f"https://api.sovfixer.com/api/v1/sov/{sovid}"
check_job_response = requests.get(check_job_url, headers=headers).json()
# Poll every three seconds for job completion
while check_job_response["request"]["status"] not in ("COMPLETE", "FAILED"):
    print(check_job_response)
    print("Waiting 3 seconds to request again...")
    time.sleep(3)
    check_job_response = requests.get(check_job_url, headers=headers).json()

print(check_job_response)

if check_job_response["request"]["status"] == "FAILED":
    raise NotImplementedError

# record the outputteds filename of the processed SOV
output_filenames = []
for output in check_job_response["result"]["outputs"]:
    output_filenames.append(output["filename"])

## ...
# 3. API URL for Fetch Outputs of SOV Parsing Job: https://api.sovfixer.com/api/v1/sov/{id}/output/{filename}
for output_filename in output_filenames:
    fetch_outputs_url = f"https://api.sovfixer.com/api/v1/sov/{sovid}/output/{output_filename}"
    fetch_outputs_response = requests.get(fetch_outputs_url, headers=headers)

    print(f"saving {output_filename}")
    if output_filename.endswith("json"):
        with open(f"workflow_example_results/{output_filename}", "w") as outfile:
            json.dump(fetch_outputs_response.json(), outfile, indent=4)
        for k, v in list(fetch_outputs_response.json().items())[:4]:
            print(f"{k!r}: {v!r}")
    elif output_filename.endswith("xlsx"):
        with open(f"workflow_example_results/{output_filename}", "wb") as outfile:
            outfile.write(fetch_outputs_response.content)            
    else:
        NotImplementedError
    print(f"saved {output_filename}")