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. Reach for this workflow when one of the following is true:
  • You have a new Excel SOV that needs to be parsed for the first time.
  • You need structured building data — addresses, limits, construction details, custom columns — extracted from the workbook.
  • You want to request one or more output formats up front (e.g., json, auditor) and have them generated in a single job.
  • You want third-party data integrations (e.g., Ping Geocoding PG, Ping Hazard PH) applied at parse time so they appear in the output.
If the SOV is already parsed and you only need a different output format or a regenerated copy, use Get or Create an SOV Output instead — it reuses the parsed result rather than re-parsing the source file. If the SOV is already parsed and you need to update building attributes or regenerate outputs with revised data, see Update an SOV. 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. If you have already parsed an SOV and need to apply corrections or refresh enrichments, see Update an SOV instead. 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. To see which output_formats your organization can request, call List Available Output Formats. The integrations field accepts datasource codes (e.g., PG, PH) from List Datasources, which returns the datasources your account has access to. Supplying a format or integration not enabled for your organization causes the request to fail. 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']}")
    output_path = f"workflow_example_results/{output['filename']}"
    os.makedirs(os.path.dirname(output_path), exist_ok=True)
    with open(output_path, "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}")