Skip to main content
GET
https://api.sutro.sh
/
jobs
/
{job_id}
/
results-url
Getting a Results Download URL
curl --request GET \
  --url https://api.sutro.sh/jobs/{job_id}/results-url \
  --header 'Authorization: <authorization>'
{
  "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=..."
  }
}
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

job_id
string
required
The job_id returned when you submitted the batch inference job.

Query Parameters

format
enum
default:"parquet"
The artifact format.Currently supported values:
  • parquet (only)
Any other value returns 400.
include_inputs
boolean
default:"false"
Whether to include the input prompts as columns in the unified artifact.
include_cumulative_logprobs
boolean
default:"false"
Whether to include cumulative log probabilities in the unified artifact.
expires_in_seconds
integer
default:"3600"
TTL for the returned presigned URLs.
  • Minimum: 1
  • Maximum: 604800 (7 days)

Headers

Authorization
string
required
Your Sutro API key using Key authentication scheme.Format: Key YOUR_API_KEYExample: Authorization: Key sk_live_abc123...

Response

Returns a JSON payload that describes the artifact and provides method-specific presigned URLs.
job_id
string
The job ID you requested.
format
string
The artifact format (currently parquet).
include_inputs
boolean
Echoes whether inputs were included in the artifact.
include_cumulative_logprobs
boolean
Echoes whether cumulative logprobs were included in the artifact.
expires_in_seconds
integer
TTL (in seconds) for the returned presigned URLs.
artifact
object
Metadata describing the stored object (bucket/key/filename/size).
urls
object
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

HEAD (metadata)

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

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.