Documentation Index Fetch the complete documentation index at: https://docs.sutro.sh/llms.txt
Use this file to discover all available pages before exploring further.
Using the API directly is not recommended for most users. Instead, we recommend using the
Python
SDK .
This endpoint creates (or reuses) one unified results artifact for a job in object storage (R2) and returns presigned URLs you can use to download it.
This route is built for large results and “real download tooling”:
Use urls.head to fetch metadata (Content-Length, ETag) without downloading the file.
Use urls.get to download the artifact, including HTTP Range requests for resumable downloads.
Once you have the presigned URLs, do not send your Sutro Authorization header to R2.
The presigned URL already contains the credentials.
Path Parameters
The job_id returned when you submitted the batch inference job.
Query Parameters
The artifact format. Currently supported values: Any other value returns 400 .
Whether to include the input prompts as columns in the unified artifact.
include_cumulative_logprobs
Whether to include cumulative log probabilities in the unified artifact.
TTL for the returned presigned URLs.
Minimum: 1
Maximum: 604800 (7 days)
Your Sutro API key using Key authentication scheme. Format: Key YOUR_API_KEY Example: Authorization: Key sk_live_abc123...
Response
Returns a JSON payload that describes the artifact and provides method-specific presigned URLs.
The job ID you requested.
The artifact format (currently parquet).
Echoes whether inputs were included in the artifact.
include_cumulative_logprobs
Echoes whether cumulative logprobs were included in the artifact.
TTL (in seconds) for the returned presigned URLs.
Metadata describing the stored object (bucket/key/filename/size).
Presigned URLs:
urls.get — use with GET (supports Range requests)
urls.head — use with HEAD (metadata only)
{
"job_id" : "batch_job_12345" ,
"format" : "parquet" ,
"include_inputs" : true ,
"include_cumulative_logprobs" : false ,
"expires_in_seconds" : 3600 ,
"artifact" : {
"bucket" : "sutro-data" ,
"key" : "jobs/user_abc/batch_job_12345/results/sutro-results~batch_job_12345~inputs=1~logprobs=0.parquet" ,
"filename" : "sutro-results~batch_job_12345~inputs=1~logprobs=0.parquet" ,
"size_bytes" : 987654321
},
"urls" : {
"get" : "https://<r2-endpoint>/<bucket>/<key>?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...&X-Amz-Signature=..." ,
"head" : "https://<r2-endpoint>/<bucket>/<key>?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...&X-Amz-Signature=..."
}
}
Download behavior
Use urls.head with the HEAD method to read headers like:
Content-Length — total bytes
ETag — object hash identifier (useful to detect changes)
GET (download)
Use urls.get with GET to download:
Supports Range: bytes=... for partial reads
Enables resumable downloads (append remaining bytes)
Treat presigned URLs like credentials. Anyone with the URL can download until it expires.
Code Examples
Python (requests) - Create URLs, HEAD metadata, resumable GET
Node.js (fetch) - Create URLs + download to disk
cURL - Create URLs, HEAD metadata, Range + resume
import os
import requests
API_KEY = os.environ[ "SUTRO_API_KEY" ]
JOB_ID = "batch_job_12345"
# 1) Ask Sutro for presigned URLs
meta = requests.get(
f "https://api.sutro.sh/jobs/ { JOB_ID } /results-url" ,
headers = { "Authorization" : f "Key { API_KEY } " },
params = {
"format" : "parquet" ,
"include_inputs" : True ,
"include_cumulative_logprobs" : False ,
"expires_in_seconds" : 3600 ,
},
).json()
get_url = meta[ "urls" ][ "get" ]
head_url = meta[ "urls" ][ "head" ]
filename = meta[ "artifact" ][ "filename" ]
# 2) HEAD for size + etag (no download)
head = requests.head(head_url)
head.raise_for_status()
size_bytes = int (head.headers[ "Content-Length" ])
etag = head.headers.get( "ETag" )
print ( "size_bytes:" , size_bytes)
print ( "etag:" , etag)
# 3) Resumable download using Range
out_path = filename
already = os.path.getsize(out_path) if os.path.exists(out_path) else 0
headers = {}
if already > 0 :
headers[ "Range" ] = f "bytes= { already } -"
with requests.get(get_url, headers = headers, stream = True ) as r:
r.raise_for_status()
mode = "ab" if already > 0 else "wb"
with open (out_path, mode) as f:
for chunk in r.iter_content( chunk_size = 8 * 1024 * 1024 ):
if chunk:
f.write(chunk)
print ( "downloaded:" , out_path)
Notes
Only format=parquet is supported on this route today.