Quickstart
1. Create an account and key
Sign up, confirm your email, then open API keys and create an admin key. Your trial gives you 14 days, 1 model and 1,000 scenarios, no card.
Keep the key on your server. Set WORKBOOKAPI_KEY to it in your terminal. Never put a key in a browser, a URL or source control. Use a calculate-only key for production apps; the admin key below is for setup.
These commands need curl and Python 3, with no Python packages. Start by setting the API base URL below. Every request below uses a timeout of 90 seconds.
export WBAPI_BASE_URL=https://api.workbookapi.com
2. Upload the sample
The browser check is private. This next step deliberately uploads the sample loan model to the hosted API.
curl --fail-with-body --silent --show-error --max-time 90 \
https://workbookapi.com/sample/loan-model.xlsx --output loan-model.xlsx
curl --fail-with-body --silent --show-error --max-time 90 \
"$WBAPI_BASE_URL/v1/models" \
-H "Authorization: Bearer $WORKBOOKAPI_KEY" \
-F file=@loan-model.xlsx --output uploaded.json --write-out 'HTTP %{http_code}\n'
export MODEL_ID=$(python3 -I -c 'import json; print(json.load(open("uploaded.json"))["model_id"])')
A successful upload returns HTTP 201 with model_id, version_id, report and request_id. The command saves the response and sets MODEL_ID. IDs vary for each upload.
3. Declare inputs and outputs
Give each input and output an API name. This sample uses cells; for your own workbooks, prefer defined names in ref, so a new version survives moved cells.
curl --fail-with-body --silent --show-error --max-time 90 \
-X PUT "$WBAPI_BASE_URL/v1/models/$MODEL_ID/interface" \
-H "Authorization: Bearer $WORKBOOKAPI_KEY" \
-H "Content-Type: application/json" \
--data '{"inputs":[{"name":"principal","ref":"Inputs!B2","type":"number","min":1000,"max":10000000},{"name":"rate","ref":"Inputs!B3","type":"number","min":0,"max":1},{"name":"term_months","ref":"Inputs!B4","type":"number","allowed":[120,180,240,360]}],"outputs":[{"name":"monthly_payment","ref":"Summary!B2"}]}' \
--output interface.json --write-out 'HTTP %{http_code}\n'
HTTP 200 returns interface with the declared inputs and outputs, plus model_id, version_id and request_id.
4. Calculate with curl
curl --fail-with-body --silent --show-error --max-time 90 \
"$WBAPI_BASE_URL/v1/models/$MODEL_ID/calculate" \
-H "Authorization: Bearer $WORKBOOKAPI_KEY" \
-H "Content-Type: application/json" \
--data '{"inputs":{"principal":250000,"rate":0.045,"term_months":360}}' \
--output calculated.json --write-out 'HTTP %{http_code}\n'
python3 -I -c 'import json; r=json.load(open("calculated.json")); assert type(r["outputs"]["monthly_payment"]) in (int, float); print("Calculation returned a monthly payment.")'
Expected terminal output:
HTTP 200
Calculation returned a monthly payment.
HTTP 200 includes outputs.monthly_payment as a number, billed_scenarios: 1, warnings, request_id, model_id, version_id and engine_build. The full-precision amount is in calculated.json. Each calculation starts from the saved inputs; omitted inputs keep their workbook values.
5. Calculate with Python
Keep MODEL_ID, WORKBOOKAPI_KEY and WBAPI_BASE_URL set. Save this as calculate.py and run python3 -I calculate.py. It calls the same model and prints the same line.
import json
import os
import urllib.request
url = os.environ["WBAPI_BASE_URL"].rstrip("/") + "/v1/models/" + os.environ["MODEL_ID"] + "/calculate"
body = json.dumps({"inputs": {"principal": 250000, "rate": 0.045, "term_months": 360}}).encode()
request = urllib.request.Request(url, data=body, headers={
"Authorization": "Bearer " + os.environ["WORKBOOKAPI_KEY"],
"Content-Type": "application/json",
})
with urllib.request.urlopen(request, timeout=90) as response:
assert response.status == 200
result = json.load(response)
assert type(result["outputs"]["monthly_payment"]) in (int, float)
print("Calculation returned a monthly payment.")
Next steps
Use scenarios for batches, upload a new version under the same model ID, or choose output names on each call. Read the API reference and limits. A client timeout can cut off an already billed response; a retry counts again.