FARPY Public API V1
FARPY Render API quickstart
Check service health, inspect current capabilities and pricing, upload a supported render file, submit the render, then download its result, receipt and proof.
1. Create an API key
Sign in, open Account Settings, and create an API key. The secret is shown once, so store it securely and never place it in browser code or public logs.
export FARPY_API_KEY="YOUR_FARPY_API_KEY"
Discover current service facts first
Call the public health endpoint before sending an API key or uploading a supported render file. If it does not return HTTP 200, stop and retry later.
curl -fsS https://farpy.com/v1/health
After health succeeds, inspect current renderer capabilities, public pricing, request limits, and the OpenAPI contract.
Production flow
health capabilities pricing upload submit status download receipt proof
API properties
- Public health preflight before authenticated work
- API-key authentication
- Public price: $0.01 per completed Blender Cycles frame
- Frame ranges from 1 to 10,000 frames per request
- 100 MiB upload limit
- Idempotent submission support
- 60 requests per minute plus burst capacity
- Cancellation before worker claim
- Signed terminal webhooks
- Receipts and proof artifacts
- Machine-readable OpenAPI 3.1 specification
Python client
Download the dependency-free Python 3 client and run the complete workflow with one high-level method.
import os
from farpy_client import FarpyClient, FarpyError
client = FarpyClient(os.environ["FARPY_API_KEY"])
capabilities = client.capabilities()
pricing = client.pricing()
limits = client.limits()
print(capabilities)
print(pricing)
print(limits)
try:
result = client.render(
"scene.blend",
output_path="render.zip",
poll_interval=2.0,
overall_timeout=3600.0,
)
print(result["job_id"])
print(result["receipt"])
print(result["proof"])
except FarpyError as exc:
print(exc.code)
print(exc.retryable)
print(exc.suggested_action)
print(exc.request_id)
raiseDownload farpy_client.py · Open the Python example · Verify SHA-256 checksums
Start here
set -euo pipefail
BASE="https://farpy.com/v1"
FILE="\${1:-scene.blend}"
: "\${FARPY_API_KEY:?Export FARPY_API_KEY first}"
command -v curl >/dev/null
command -v jq >/dev/null
command -v sha256sum >/dev/null
test -f "$FILE"
case "$FILE" in
*.blend) ;;
*) echo "ERROR: input must be a .blend file" >&2; exit 2 ;;
esac
AUTH="Authorization: Bearer $FARPY_API_KEY"
FILE_SHA256="$(sha256sum "$FILE" | awk '{print $1}')"
UPLOAD_KEY="upload-$FILE_SHA256"
SUBMIT_KEY="submit-$FILE_SHA256"
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT
echo "1/8 UPLOAD"
curl -fsS "$BASE/renders" \
-H "$AUTH" \
-H "Idempotency-Key: $UPLOAD_KEY" \
-F "file=@$FILE" \
-o "$TMP/upload.json"
jq . "$TMP/upload.json"
JOB_ID="$(jq -er '
.job_id //
.render.job_id //
.render.id //
.id //
empty
' "$TMP/upload.json")"
echo "JOB_ID=$JOB_ID"
echo
echo "2/8 PRICE"
curl -fsS "$BASE/pricing" \
-H "$AUTH" \
-o "$TMP/pricing.json"
jq . "$TMP/pricing.json"
echo
echo "3/8 PREFLIGHT"
jq -n \
--arg filename "$(basename "$FILE")" \
'{
filename: $filename,
renderer: "blender",
frame_start: 1,
frame_end: 1,
frame_count: 1
}' > "$TMP/preflight-request.json"
curl -fsS "$BASE/renders/preflight" \
-H "Content-Type: application/json" \
--data-binary @"$TMP/preflight-request.json" \
-o "$TMP/preflight.json"
jq . "$TMP/preflight.json"
echo
echo "4/8 SUBMIT"
curl -fsS -X POST "$BASE/renders/$JOB_ID/submit" \
-H "$AUTH" \
-H "Idempotency-Key: $SUBMIT_KEY" \
-o "$TMP/submit.json"
jq . "$TMP/submit.json"
echo
echo "5/8 STATUS"
while :; do
curl -fsS "$BASE/renders/$JOB_ID" \
-H "$AUTH" \
-o "$TMP/status.json"
jq . "$TMP/status.json"
STATE="$(jq -r '
.status //
.state //
.render.status //
.render.state //
empty
' "$TMP/status.json" | tr "[:lower:]" "[:upper:]")"
case "$STATE" in
DONE|COMPLETED|COMPLETE|SUCCEEDED|SUCCESS)
break
;;
FAILED|ERROR|CANCELLED|CANCELED)
echo "ERROR: terminal state $STATE" >&2
exit 3
;;
esac
sleep 5
done
echo
echo "6/8 RECEIPT"
curl -fsS "$BASE/renders/$JOB_ID/receipt" \
-H "$AUTH" \
-o "$TMP/receipt-response.json"
jq . "$TMP/receipt-response.json"
RECEIPT_URL="$(jq -er '
.receipt_url //
.receipt.url //
.render.receipt_url //
empty
' "$TMP/receipt-response.json")"
curl -fsSL "$RECEIPT_URL" -o "$TMP/receipt.json"
jq . "$TMP/receipt.json"
echo
echo "7/8 DOWNLOAD"
curl -fsS "$BASE/renders/$JOB_ID/download" \
-H "$AUTH" \
-o "$TMP/download-response.json"
jq . "$TMP/download-response.json"
DOWNLOAD_URL="$(jq -er '
.download_url //
.output_url //
.artifact_url //
.download.url //
.artifact.url //
.render.download_url //
.render.output_url //
empty
' "$TMP/download-response.json")"
OUTPUT="$PWD/$JOB_ID.output"
curl -fsSL "$DOWNLOAD_URL" -o "$OUTPUT"
echo "OUTPUT=$OUTPUT"
echo
echo "8/8 SHA256 VERIFY"
EXPECTED_SHA256="$(jq -er '
.output_sha256 //
.artifact_sha256 //
.sha256 //
.output.sha256 //
.artifact.sha256 //
.artifacts[0].sha256 //
empty
' "$TMP/receipt.json")"
ACTUAL_SHA256="$(sha256sum "$OUTPUT" | awk '{print $1}')"
echo "EXPECTED_SHA256=$EXPECTED_SHA256"
echo "ACTUAL_SHA256=$ACTUAL_SHA256"
test "$ACTUAL_SHA256" = "$EXPECTED_SHA256"
echo
echo "VERIFY_OK=true"
echo "JOB_ID=$JOB_ID"
echo "RECEIPT_URL=$RECEIPT_URL"
echo "DOWNLOAD_URL=$DOWNLOAD_URL"API overview · Agent integration guide · Error and retry guide · Webhook delivery guide · Quickstart · OpenAPI · Pricing · Status · Proof