Reframefx
Developer docs

Render smoother video from your app.

The whole integration is four calls: upload a clip, start a render, poll until it finishes, download the output. Do it by hand below, or skip straight to the one-prompt path and let your AI tool wire it up.

or read the endpoints ↓

Get an API key

API users authenticate with a dashboard-generated key. Keep it server-side in production; these examples show the key inline so the replaceable value is obvious. Send it on every protected request as Authorization: Bearer <your_api_key>.

Verify the key works:

curl -s "https://reframefx-production.up.railway.app/me" \
  -H "Authorization: Bearer <your_api_key>"

Returns your profile and balance_xeconds. A 401 means the key is missing or wrong.

Endpoints

The full render flow, one endpoint at a time. Every protected request needs the Authorization header from above. The upload PUT is the only call that goes to storage instead of the API.

1Presign
POST/upload/presign

Create a direct upload URL for your file, then PUT the bytes to it.

# 1. Presign: returns { upload_url, key, project_id }
curl -s -X POST "https://reframefx-production.up.railway.app/upload/presign" \
  -H "Authorization: Bearer <your_api_key>" \
  -H "Content-Type: application/json" \
  -d '{"filename":"clip.mp4","content_type":"video/mp4"}'

# 2. Upload the file to the upload_url from that response.
#    Note: PUT, no auth header, Content-Type must match the presign.
curl -s -X PUT "<upload_url>" \
  -H "Content-Type: video/mp4" \
  --upload-file ./clip.mp4
Response
{
  "upload_url": "https://...storage.../clip.mp4?signature=...",
  "key": "uploads/ab12.../clip.mp4",
  "project_id": "9f3c..."
}
2Estimate
POST/pricing/estimate-video

Optional. Probe the uploaded file and see the charged xeconds for each target FPS before you render.

curl -s -X POST "https://reframefx-production.up.railway.app/pricing/estimate-video" \
  -H "Authorization: Bearer <your_api_key>" \
  -H "Content-Type: application/json" \
  -d '{"r2_key":"<key from presign>","target_fps_options":[60,120,240]}'
Response
{
  "metadata": { "fps": 24, "duration_sec": 12.4, "width": 1920, "height": 1080 },
  "estimates": [
    { "target_fps": 60,  "band": "smooth", "multiplier": 1, "charged_xeconds": 12 },
    { "target_fps": 120, "band": "slomo",  "multiplier": 2, "charged_xeconds": 24 },
    { "target_fps": 240, "band": "ultra",  "multiplier": 4, "charged_xeconds": 48 }
  ]
}
3Render
POST/jobs

Start the render. Credits are deducted when the job is accepted. target_fps must be greater than the source FPS.

curl -s -X POST "https://reframefx-production.up.railway.app/jobs" \
  -H "Authorization: Bearer <your_api_key>" \
  -H "Content-Type: application/json" \
  -d '{"r2_key":"<key>","project_id":"<project_id>","target_fps":60}'
Response
{
  "job_id": "job_7b2e...",
  "status": "queued"
}
4Poll
GET/jobs/{job_id}

Fetch the job and read live_status. Re-request roughly every 10 seconds until it's done or failed. On done, output_url is the rendered video.

# Loop/repeat this request about every 10s until live_status is done or failed.
curl -s "https://reframefx-production.up.railway.app/jobs/<job_id>" \
  -H "Authorization: Bearer <your_api_key>"
Response
{
  "job_id": "job_7b2e...",
  "live_status": "done",
  "target_fps": 60,
  "output_url": "https://...storage.../output.mp4?signature=...",
  "xeconds_charged": 12
}

Job status

Poll GET /jobs/{job_id} and read live_status. The API returns stable status values; your app owns the user-facing wording.

pendingJob created and waiting for render status.
queuedAccepted and waiting to start.
no_workersQueued while render capacity is full.
cold_startRender environment is preparing.
processingVideo is being processed.
doneRender succeeded. Use output_url to download.
failedRender failed. Deducted xeconds are refunded automatically.

Limits & pricing

Video size250MB maximum during launch. Larger files return 413.
Video length120 seconds maximum during launch.
Output FPS24-240fps, and target_fps must be greater than the source FPS.
Smooth bandUp to 60fps. charged_xeconds = max(1, floor(duration_sec)) x 1.
Slo-mo band61-120fps. charged_xeconds = max(1, floor(duration_sec)) x 2.
Ultra band (cinematic slo-mo)121-240fps. charged_xeconds = max(1, floor(duration_sec)) x 4.
Free creditsFree renders are watermarked. Paid-credit renders are clean by default.

Errors

Errors return a status code and JSON with a readable detail field.

{
  "detail": "Launch limit is 120s per video. Longer video chunking is coming."
}
400Invalid request body or unsupported params.
401Missing or invalid API key.
402Insufficient xeconds balance.
403The resource belongs to another account.
404Project, job, or file was not found.
413Upload exceeds launch size or duration limits.
500Unexpected server error. Retry reads; contact support if a write is unclear.

For AI agents

ReframeFX is built to be integrated by coding agents. The fastest path is the Integrate with one prompt button at the top — it copies a self-contained prompt for Cursor, Claude Code, or any AI tool. For programmatic discovery, these are always live:

  • /openapi.json — machine-readable OpenAPI 3 schema for Postman, client generators, and agents.
  • /llms-full.txt — the full docs as plain text, in one fetch.
  • /llms.txt — a short, structured index of the canonical pages and key facts.