Skip to main content
Below describes an example workflow of API calls that can be made in order to retrieve an output for an SOV that has already been parsed. Reach for this workflow when one of the following is true:
  • You need an output format that was not requested at parse time. The original Start SOV Parsing Job call accepts an output_formats list. Any format you did not include there will not exist until you request it here.
  • You want to regenerate an existing output. Pass overwrite_existing as true to discard the cached file and produce a fresh one.
  • You want to fetch an output for a specific revision of the SOV. Pass revision to target a specific revision: 0 for the initial SOV, a positive integer for an intermediate revision, or -1 (the default) for the latest.
Use this workflow instead of re-submitting the source file to Start SOV Parsing Job: the SOV is already parsed, and this endpoint reuses that work. It returns a cached output immediately if one is available. Otherwise, it starts a new generation job and returns a request ID to poll. This workflow uses two distinct IDs in two different shapes. Keep them separate:
  • SOV ID — the ID of the parsed SOV (or SOV Update), returned by the original parsing job (e.g., s-no-ping-hggcsk). Pass this in the path of the POST.
  • Output request ID — a UUID returned in the POST response under request.id (e.g., 9f4d8c10-3b7e-4a52-bd91-c823f15e4d76). Pass this in the path of the GET when polling.
When the output is already cached, the request ID carries an immediately-done- prefix (for example, immediately-done-7b3c1a04-2d6e-4f95-bf18-a08e25c4d731). Treat the ID as opaque — use request.status to determine completion rather than parsing the prefix. The sample Python script is available for download at the bottom of the page. Try filling in the blanks (e.g., {sovid}, {request_id}) to go through the workflow using cURL!

1. Request the SOV Output

The first API call to make is Get Or Create SOV Output. Supply the SOV ID in the path and the desired output_format in the request body. If a matching output is already cached, the response returns request.status of COMPLETE and the result is available immediately — skip step 2 and go to step 3. Otherwise, the response returns a pending status and an id you will use to poll. output_format accepts either lowercase or uppercase. If the supplied format is not enabled for your organization, the response is a 400 with a message listing the formats you may request. To discover the allowed values up front, call List Available Output Formats. Set overwrite_existing to true to force regeneration even when a cached output exists. Set revision to select a specific SOV revision. -1 (the default) selects the latest revision and 0 selects the initial SOV. Revision is ignored when the path ID is a SUD ID. E.g., https://api.sovfixer.com/api/v1/sov/s-no-ping-hggcsk/get_or_create_output Example code:
get_or_create_output.py
import time
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}"}

# SOV ID from a previously completed parsing job
sovid = "s-no-ping-hggcsk"

# request options
payload = {
    # which output format do you want? e.g., json, auditor
    "output_format": "json",
    # latest revision (-1) or initial SOV (0)
    "revision": -1,
    # set to true to regenerate even if a cached output already exists
    "overwrite_existing": False,
}

# 1. API URL for Get Or Create SOV Output: https://api.sovfixer.com/api/v1/sov/{sovid}/get_or_create_output
start_url = f"https://api.sovfixer.com/api/v1/sov/{sovid}/get_or_create_output"
start_response = requests.post(start_url, json=payload, headers=headers)
assert start_response.status_code in (200, 201)
start_json = start_response.json()

# the request.id is the polling ID for step 2 — it is NOT the SOV ID
request_id = start_json["request"]["id"]
status = start_json["request"]["status"]

## ...
Example response (cached output already available):
Python output
{
    "request": {
        "id": "immediately-done-7b3c1a04-2d6e-4f95-bf18-a08e25c4d731",
        "status": "COMPLETE"
    },
    "result": {
        "label": "JSON",
        "output_format": "JSON",
        "scrubbed_filename": "parse_sov_testfile.json",
        "url": "https://api.sovfixer.com/api/v1/sov/s-no-ping-hggcsk/output/parse_sov_testfile.json"
    }
}
Example response (generation started):
Python output
{
    "request": {
        "id": "9f4d8c10-3b7e-4a52-bd91-c823f15e4d76",
        "status": "PENDING"
    }
}

2. Poll the SOV Output Request

The second API call to make is Get/Check SOV Output Result. Skip this step if step 1 already returned COMPLETE. Otherwise, pass the request.id from step 1 in the path and poll until request.status is COMPLETE or FAILED. When the status is COMPLETE, result contains label, output_format, scrubbed_filename, and url. When the status is FAILED, result.message contains a description of the error. E.g., https://api.sovfixer.com/api/v1/sov/get_or_create_output/9f4d8c10-3b7e-4a52-bd91-c823f15e4d76 Example code:
get_or_create_output.py
## ...

# 2. API URL for Get/Check SOV Output Result: https://api.sovfixer.com/api/v1/sov/get_or_create_output/{request_id}
check_url = f"https://api.sovfixer.com/api/v1/sov/get_or_create_output/{request_id}"
check_json = start_json

# Poll every three seconds for output generation
while check_json["request"]["status"] not in ("COMPLETE", "FAILED"):
    print(check_json)
    print("Waiting 3 seconds to request again...")
    time.sleep(3)
    check_response = requests.get(check_url, headers=headers)
    assert check_response.status_code in (200, 201)
    check_json = check_response.json()

print(check_json)
assert check_json["request"]["status"] != "FAILED", "Output generation failed"

result = check_json["result"]

## ...
Example response:
Python output
{
    "request": {
        "status": "COMPLETE"
    },
    "result": {
        "label": "JSON",
        "output_format": "JSON",
        "scrubbed_filename": "parse_sov_testfile.json",
        "url": "https://api.sovfixer.com/api/v1/sov/s-no-ping-hggcsk/output/parse_sov_testfile.json"
    }
}

3. Download the Output

Download the file directly from the result.url returned by the completed request. Save the response body as binary. The URL serves the file in its native format (e.g., JSON, XLSX). Example code:
get_or_create_output.py
## ...

# 3. Download the output from result.url
download_response = requests.get(result["url"], headers=headers)
assert download_response.status_code in (200, 201)

output_path = f"workflow_example_results/{result['scrubbed_filename']}"
os.makedirs(os.path.dirname(output_path), exist_ok=True)
with open(output_path, "wb") as outfile:
    outfile.write(download_response.content)
print(f"saved {result['scrubbed_filename']}")
Example response:
Python output
saved parse_sov_testfile.json

Python Demo

                                 Download Python Script here
get_or_create_sov_output.py
"""Demo: request and download an output for an existing SOV.

Two IDs are involved. Keep them separate:
  - SOVID: identifies the parsed SOV, passed in the POST path.
  - request_id: returned by the POST under request.id, used to poll the GET.
"""

import os
import time
import requests


BASE_URL = "https://api.sovfixer.com/api/v1"
SOVID = "s-no-ping-hggcsk"   # SOV ID from a previously completed parsing job
OUTPUT_FORMAT = "json"       # desired output format, e.g., json or auditor
POLL_SECONDS = 3

API_KEY = os.environ.get("SOVFIXER_AUTH_TOKEN")
headers = {"Authorization": f"Token {API_KEY}"}

# 1. Request the output. If a cached output is available the response
#    returns COMPLETE immediately. Otherwise, generation begins and we
#    receive a request.id to poll.

start_url = f"{BASE_URL}/sov/{SOVID}/get_or_create_output"
payload = {"output_format": OUTPUT_FORMAT, "revision": -1, "overwrite_existing": False}
start_json = requests.post(start_url, json=payload, headers=headers).json()
request_id = start_json["request"]["id"]


# 2. Poll until the output is ready. request_id is the polling ID — not SOVID.
check_url = f"{BASE_URL}/sov/get_or_create_output/{request_id}"
check_json = start_json
while check_json["request"]["status"] not in ("COMPLETE", "FAILED"):
    time.sleep(POLL_SECONDS)
    check_json = requests.get(check_url, headers=headers).json()

if check_json["request"]["status"] == "FAILED":
    raise RuntimeError(f"Output generation failed: {check_json}")

# 3. Download the file. result.url serves the output in its native format,
#    so write the response body as binary.
result = check_json["result"]
download = requests.get(result["url"], headers=headers)
os.makedirs("workflow_example_results", exist_ok=True)
with open(f"workflow_example_results/{result['scrubbed_filename']}", "wb") as f:
    f.write(download.content)
print(f"saved {result['scrubbed_filename']}")