Product guide
Turn natural language into executable spreadsheets. Same AI powering our web app, now in your code. Create an API key, submit jobs with simple prompts, and get back fully functional Excel files.
Get Started with AI
Use our pre-built prompt to build your first integration
Paste into Claude Code, Cursor, or your preferred AI agent
Send prompt + files, get instant run ID
Poll status or receive a signed webhook
Download the workbook and any artifacts
Submit a run and continue working while Shortcut processes it in the background.
Upload a primary workbook and supporting files for data-driven automation.
Not just text generation: creates fully functional Excel files. Production-ready output.
Choose whether Shortcut should modify a workbook or return read-only analysis.
Apply available Shortcut skills and collect additional files produced by the run.
Configure each run and retain its ID for status, support, and usage tracking.
import os
import time
import requests
API_KEY = os.environ["SHORTCUT_API_KEY"]
BASE_URL = "https://api.shortcut.ai/api/spreadsheets"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
def wait_for_run(run_id):
while True:
response = requests.get(f"{BASE_URL}/{run_id}", headers=HEADERS, timeout=30)
response.raise_for_status()
run = response.json()
print(f"Status: {run['status']}")
if run["status"] == "completed":
return run
if run["status"] in {"failed", "cancelled"}:
raise RuntimeError(run.get("error", f"Run {run['status']}"))
time.sleep(3)
response = requests.post(
BASE_URL,
headers=HEADERS,
json={
"prompt": "Create a sales report with summary statistics",
"mode": "action",
},
timeout=30,
)
response.raise_for_status()
run_id = response.json()["runId"]
print(f"Run submitted: {run_id}")
run = wait_for_run(run_id)
if run.get("downloadUrl"):
result = requests.get(f"{BASE_URL}/{run_id}/download", headers=HEADERS, timeout=120)
result.raise_for_status()
with open("result.xlsx", "wb") as output:
output.write(result.content)
print("Saved result.xlsx")
else:
print(run.get("summary", "Run completed without a workbook."))import os
import time
import requests
API_KEY = os.environ["SHORTCUT_API_KEY"]
BASE_URL = "https://api.shortcut.ai/api/spreadsheets"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
def wait_for_run(run_id):
while True:
response = requests.get(f"{BASE_URL}/{run_id}", headers=HEADERS, timeout=30)
response.raise_for_status()
run = response.json()
print(f"Status: {run['status']}")
if run["status"] == "completed":
return run
if run["status"] in {"failed", "cancelled"}:
raise RuntimeError(run.get("error", f"Run {run['status']}"))
time.sleep(3)
with open("data.xlsx", "rb") as workbook:
response = requests.post(
f"{BASE_URL}/upload",
headers=HEADERS,
files={"file": workbook},
timeout=120,
)
response.raise_for_status()
file_id = response.json()["fileId"]
response = requests.post(
BASE_URL,
headers=HEADERS,
json={
"prompt": "Create a pivot table summarizing sales by region",
"initFile": file_id,
"mode": "action",
},
timeout=30,
)
response.raise_for_status()
run_id = response.json()["runId"]
run = wait_for_run(run_id)
if run.get("downloadUrl"):
result = requests.get(f"{BASE_URL}/{run_id}/download", headers=HEADERS, timeout=120)
result.raise_for_status()
with open("result.xlsx", "wb") as output:
output.write(result.content)
else:
print(run.get("summary", "Run completed without a workbook."))import os
import time
import requests
API_KEY = os.environ["SHORTCUT_API_KEY"]
BASE_URL = "https://api.shortcut.ai/api/spreadsheets"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
def wait_for_run(run_id):
while True:
response = requests.get(f"{BASE_URL}/{run_id}", headers=HEADERS, timeout=30)
response.raise_for_status()
run = response.json()
print(f"Status: {run['status']}")
if run["status"] == "completed":
return run
if run["status"] in {"failed", "cancelled"}:
raise RuntimeError(run.get("error", f"Run {run['status']}"))
time.sleep(3)
with open("data.xlsx", "rb") as workbook:
response = requests.post(
f"{BASE_URL}/upload",
headers=HEADERS,
files={"file": workbook},
timeout=120,
)
response.raise_for_status()
file_id = response.json()["fileId"]
response = requests.post(
BASE_URL,
headers=HEADERS,
json={
"prompt": "Analyze this workbook and summarize the key trends",
"initFile": file_id,
"mode": "ask",
},
timeout=30,
)
response.raise_for_status()
run_id = response.json()["runId"]
run = wait_for_run(run_id)
print(run.get("summary", "No summary was produced."))import os
import time
import requests
API_KEY = os.environ["SHORTCUT_API_KEY"]
BASE_URL = "https://api.shortcut.ai/api/spreadsheets"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
def wait_for_run(run_id):
while True:
response = requests.get(f"{BASE_URL}/{run_id}", headers=HEADERS, timeout=30)
response.raise_for_status()
run = response.json()
print(f"Status: {run['status']}")
if run["status"] == "completed":
return run
if run["status"] in {"failed", "cancelled"}:
raise RuntimeError(run.get("error", f"Run {run['status']}"))
time.sleep(3)
# Always use the source-qualified id returned by the skills endpoint.
response = requests.get(f"{BASE_URL}/skills", headers=HEADERS, timeout=30)
response.raise_for_status()
skill = next((
item
for item in response.json()["skills"]
if item["folderName"] == "powerpoint_creation"
), None)
if skill is None:
raise RuntimeError("The powerpoint_creation skill is not available for this account")
response = requests.post(
BASE_URL,
headers=HEADERS,
json={
"prompt": "Create a Q1 sales workbook and a presentation summarizing it",
"mode": "action",
"skills": [skill["id"]],
},
timeout=30,
)
response.raise_for_status()
run_id = response.json()["runId"]
run = wait_for_run(run_id)
if run.get("downloadUrl"):
response = requests.get(f"{BASE_URL}/{run_id}/download", headers=HEADERS, timeout=120)
response.raise_for_status()
with open("result.xlsx", "wb") as output:
output.write(response.content)
for filename in run.get("artifacts", []):
response = requests.get(
f"{BASE_URL}/{run_id}/artifacts/{filename}",
headers=HEADERS,
timeout=120,
)
response.raise_for_status()
with open(filename, "wb") as output:
output.write(response.content)
print(f"Saved {filename}")# Sign in once; your OAuth session authenticates every call
shortcut login
# Submit a job, wait for it, and download the result in one command
shortcut api run "Combine these regional sheets and add a summary pivot" \
--input q3-sales.xlsx \
--output q3-report.xlsx
# Or drive each step yourself
shortcut api upload q3-sales.xlsx
shortcut api submit "Combine these regional sheets and add a summary pivot" \
--init-file-id <file-id> --mode action
shortcut api status <run-id>
shortcut api wait <run-id>
shortcut api download <run-id> -o q3-report.xlsx
# Webhooks need an API key
shortcut api submit "..." --auth api-key --webhook-url https://example.com/shortcut
import os
import time
import requests
API_KEY = os.environ["SHORTCUT_API_KEY"]
BASE_URL = "https://api.shortcut.ai/api/spreadsheets"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
def wait_for_run(run_id):
while True:
response = requests.get(f"{BASE_URL}/{run_id}", headers=HEADERS, timeout=30)
response.raise_for_status()
run = response.json()
print(f"Status: {run['status']}")
if run["status"] == "completed":
return run
if run["status"] in {"failed", "cancelled"}:
raise RuntimeError(run.get("error", f"Run {run['status']}"))
time.sleep(3)
response = requests.post(
BASE_URL,
headers=HEADERS,
json={
"prompt": "Create a sales report with summary statistics",
"mode": "action",
},
timeout=30,
)
response.raise_for_status()
run_id = response.json()["runId"]
print(f"Run submitted: {run_id}")
run = wait_for_run(run_id)
if run.get("downloadUrl"):
result = requests.get(f"{BASE_URL}/{run_id}/download", headers=HEADERS, timeout=120)
result.raise_for_status()
with open("result.xlsx", "wb") as output:
output.write(result.content)
print("Saved result.xlsx")
else:
print(run.get("summary", "Run completed without a workbook."))The Shortcut CLI drives this API from the terminal. The full list, as printed by shortcut api --help:
shortcut api run <prompt> [options] # upload, submit, wait, and download in one command
shortcut api upload <file...> # upload files, returns a file id
shortcut api submit <prompt> [options] # submit a task, returns a run id
shortcut api status <run-id> # check on a run
shortcut api wait <run-id> # wait until a run finishes
shortcut api download <run-id> [-o f] # download the result workbook
shortcut api artifacts <run-id> # list extra output files
shortcut api artifact <run-id> <name> # download one extra output file
shortcut api cancel <run-id> # cancel a run
shortcut api runs # list recent runs
shortcut api skills # list the skills the user can use
shortcut api verify # check the connection"Generate weekly sales report with YoY comparison charts"
"Extract tables from invoice PDFs into structured spreadsheet"
"Combine 5 regional sheets, standardize dates, add summary pivot"
"Clean user-uploaded data and generate analysis dashboard"
A cloud job does not inherit your users’ desktop sessions, local files, signed-in cloud storage, VPN, credentials, or local connectors. The calling system normally fetches private source data and submits it as job context.
Jobs cannot run VBA, macros, or desktop Excel add-ins. Validate complex workbooks before relying on exact native Excel behavior.
Quotas and timeout ceilings can change by deployment or contract. The live OpenAPI specification is the source of truth for current fields and limits.
No. Jobs run entirely in Shortcut’s cloud and return standard .xlsx files.
No. Your application uploads files with each job, or mounts an authorized Shortcut project read-only as context.
For server integrations and webhooks, yes. Signed-in users can also call the API with OAuth; the CLI does this through its shortcut api commands after shortcut login.
Yes. This is the product designed for fully unattended, asynchronous cloud execution.
Live endpoints, fields, limits, and errors.
OpenAPI specification →Machine-readable contract for client generation and testing.
Run Shortcut from your own agent →Call the API through the shortcut command after one OAuth sign-in.
Support →Help with API keys, quotas, and integration questions.
Operational limits evolve independently of this guide. Check the live OpenAPI reference below before relying on a numeric limit in an integration.
Use our auto-generated spec for type-safe client generation, testing tools, or API exploration.
https://api.shortcut.ai/api/openapi.jsonNext step
Explore the interactive API reference with live examples, or jump straight to building with our AI-powered quick start.