FrameSurferAPI
Contents

API reference · v1

FrameSurfer API

Make AI images, short video clips and music tracks from your own code, with the same models and prices as the FrameSurfer app. Everything you make is saved in your FrameSurfer library.

Overview

Introduction

A JSON REST API. Send requests to the base URL with your API key. Pictures and clips are made in the background: you get an id at once and ask for the result. Music comes back finished in the same request.

Base URL
https://framesurfer.com/api/v1
Auth
Authorization: Bearer fs_…
Format
JSON in, JSON out
Access
A paid FrameSurfer plan. See plans.

Overview

Authentication

Create a key on the API keys page in your account. Keys start with fs_ and are shown once, when you create them. Send the key in the Authorization header of every request.

Authorization: Bearer fs_your_key

A key can spend your credits. Keep it on your server and out of browser code and public repositories. If a key leaks, delete it on the same page; it stops working at once.

Guide

Quick start

Make a picture and wait until it is ready. Put your key in the FRAMESURFER_API_KEY environment variable first.

Quick start
# 1. Start the picture. Save the id from the answer.
curl -X POST https://framesurfer.com/api/v1/images \
  -H "Authorization: Bearer $FRAMESURFER_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: lighthouse-001" \
  -d '{"prompt": "A lighthouse on a cliff at sunset"}'

# 2. Ask until status is completed, then open url.
curl https://framesurfer.com/api/v1/images/IMAGE_ID \
  -H "Authorization: Bearer $FRAMESURFER_API_KEY"

Guide

Credits and pricing

Requests use the credits of your FrameSurfer plan, at the same price as in the app. A create request is charged when it is accepted. To know the price first, send the same fields to POST /v1/quote. It is free and makes nothing.

Prices per model are in GET /v1/models. A clip costs its credits per second times its length, for example 5 credits for 5 seconds on the default model at 480p.

Price check
curl -X POST https://framesurfer.com/api/v1/quote \
  -H "Authorization: Bearer $FRAMESURFER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "image"
  }'

Guide

Getting results

A picture or clip answers with status: "processing" and an id. Ask for that id until the status is completed, then download url.

statusMeaning
processingStill being made. Ask again in a few seconds.
completedDone. url is the finished file.
failedThe model could not make it. error says why; try a different prompt.

Ask about every 3 seconds for a picture and every 5 to 10 seconds for a clip. A finished file can be looked up by its id later, and it is also in your library on framesurfer.com.

Guide

Safe retries

Networks fail. To retry a create request without paying twice, send an Idempotency-Key header with any unique string, such as an id from your own database. The same key with the same body returns the same picture, clip or track and is charged once. Without the header, every request is a new one.

Idempotency-Key: order-4821-thumbnail

Guide

Rate limits

Each key can send up to 60 requests a minute, status checks included. Over that, requests answer 429 with the code rate_limited. Wait a moment and try again. For more throughput, spread work over time rather than over several keys.

Guide

Errors

Errors use standard HTTP status codes and always have the same body: a stable code to check in your code and a message written for people.

Error402 Payment Required

{
  "error": {
    "code": "insufficient_credits",
    "message": "Not enough credits: this needs 20 and the account has 12.",
    "requiredCredits": 20,
    "availableCredits": 12
  }
}
StatuscodeMeaning
400invalid_requestA field is missing or not valid. The message names it, for example "durationSec: Number must be less than or equal to 30".
400invalid_jsonThe body is not JSON.
400bad_requestThe request cannot be made as asked, for example an unknown model id.
401missing_api_keyNo key was sent.
401invalid_api_keyThe key is wrong or was deleted.
402paid_plan_requiredThe account has no paid plan.
402insufficient_creditsNot enough credits. The error also carries requiredCredits and availableCredits.
404not_foundNo picture or clip with that id on this account.
429rate_limitedMore than 60 requests in a minute with this key. Wait and try again.
500server_errorSomething failed on our side. Retry with the same Idempotency-Key.

Endpoints

Images

Create an image

POST/v1/images

Makes one AI picture from a prompt, or changes an existing picture. The request is charged when it is accepted. It answers at once with an id; get the finished picture with GET /v1/images/{id}.

Body

promptstringrequired
What the picture shows.
modelstring= gpt-image-2.5
An image model id from GET /v1/models.
aspectRatiostring= 9:16
9:16 vertical, 16:9 wide or 1:1 square.9:1616:91:1
imageUrlhttps URL
A picture to change instead of starting from nothing. The model must edit pictures (editsPictures in GET /v1/models).

Headers

Idempotency-Keystring
Optional. Any unique string, so a retry cannot charge twice. See Safe retries.

Response

idstring
Use it with the GET endpoint. Pictures and clips only.
kindstring
image, video or music.
statusstring
processing, completed or failed.
urlstring
The finished file, once status is completed.
creditsnumber
What this request was charged.
errorstring
Why it failed, when status is failed.
Request
curl -X POST https://framesurfer.com/api/v1/images \
  -H "Authorization: Bearer $FRAMESURFER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "A lighthouse on a cliff at sunset, warm light",
    "aspectRatio": "16:9"
  }'

Response202 Accepted

{
  "id": "project-image-7c1e0a9b4d2f",
  "kind": "image",
  "status": "processing",
  "credits": 1,
  "model": "gpt-image-2.5"
}

Retrieve an image

GET/v1/images/{id}

Where a picture stands. Ask every few seconds until status is completed or failed; most pictures take 10 to 30 seconds.

Path parameters

idstringrequired
The id POST /v1/images answered with.

Response

idstring
Use it with the GET endpoint. Pictures and clips only.
kindstring
image, video or music.
statusstring
processing, completed or failed.
urlstring
The finished file, once status is completed.
creditsnumber
What this request was charged.
errorstring
Why it failed, when status is failed.
Request
curl https://framesurfer.com/api/v1/images/project-image-7c1e0a9b4d2f \
  -H "Authorization: Bearer $FRAMESURFER_API_KEY"

Response200 OK

{
  "id": "project-image-7c1e0a9b4d2f",
  "kind": "image",
  "status": "completed",
  "url": "https://media.framesurfer.com/media/ai-images/lighthouse.png"
}

Endpoints

Video clips

Create a video clip

POST/v1/videos

Makes one short AI video clip: a single continuous shot, from a prompt or starting from a picture. The request is charged when it is accepted. It answers at once with an id; get the finished clip with GET /v1/videos/{id}.

Body

promptstringrequired
The one shot: what is seen, what moves, and any sound or line.
modelstring= p-video-2-pro
A video model id from GET /v1/models.
durationSecinteger= 5
Length in seconds. It is rounded to a length the model can make (allowedDurations in GET /v1/models), and the response gives the real length.
aspectRatiostring= 9:16
9:16 vertical, 16:9 wide or 1:1 square.9:1616:91:1
resolutionstring
One of the model's resolutions, for example 480p or 768p. Defaults to the model's own (480p on p-video-2-pro).
soundboolean= true
Generate sound with the picture.
startImageUrlhttps URL
A picture the clip starts from.
endImageUrlhttps URL
A picture the clip ends on, when the model takes one (endsOnPicture in GET /v1/models).

Headers

Idempotency-Keystring
Optional. Any unique string, so a retry cannot charge twice. See Safe retries.

Response

idstring
Use it with the GET endpoint. Pictures and clips only.
kindstring
image, video or music.
statusstring
processing, completed or failed.
urlstring
The finished file, once status is completed.
creditsnumber
What this request was charged.
errorstring
Why it failed, when status is failed.
durationSecinteger
The real length, after rounding to what the model makes.
resolutionstring
The resolution it is made at.
Request
curl -X POST https://framesurfer.com/api/v1/videos \
  -H "Authorization: Bearer $FRAMESURFER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Waves crashing on dark rocks, slow motion, sea spray",
    "durationSec": 5,
    "aspectRatio": "9:16"
  }'

Response202 Accepted

{
  "id": "tool-video-credit-4b8d2e61f0aa",
  "kind": "video",
  "status": "processing",
  "credits": 5,
  "durationSec": 5,
  "resolution": "480p",
  "aspectRatio": "9:16",
  "withAudio": true,
  "model": "p-video-2-pro"
}

Retrieve a video clip

GET/v1/videos/{id}

Where a clip stands. Ask every 5 to 10 seconds until status is completed or failed; most clips take 30 seconds to 3 minutes.

Path parameters

idstringrequired
The id POST /v1/videos answered with.

Response

idstring
Use it with the GET endpoint. Pictures and clips only.
kindstring
image, video or music.
statusstring
processing, completed or failed.
urlstring
The finished file, once status is completed.
creditsnumber
What this request was charged.
errorstring
Why it failed, when status is failed.
Request
curl https://framesurfer.com/api/v1/videos/tool-video-credit-4b8d2e61f0aa \
  -H "Authorization: Bearer $FRAMESURFER_API_KEY"

Response200 OK

{
  "id": "tool-video-credit-4b8d2e61f0aa",
  "kind": "video",
  "status": "completed",
  "url": "https://media.framesurfer.com/media/videos/waves.mp4"
}

Endpoints

Music

Create a music track

POST/v1/music

Makes one instrumental music track and answers with its link in the same request, usually within a minute. If the same request is still being made it answers 202; send it again with the same Idempotency-Key to collect the track.

Body

promptstringrequired
What the music sounds like: genre, mood, instruments, tempo.
durationSecinteger= 30
Length in seconds, 1 to 180.

Headers

Idempotency-Keystring
Optional. Any unique string, so a retry cannot charge twice. See Safe retries.

Response

kindstring
image, video or music.
statusstring
processing, completed or failed.
urlstring
The finished file, once status is completed.
creditsnumber
What this request was charged.
Request
curl -X POST https://framesurfer.com/api/v1/music \
  -H "Authorization: Bearer $FRAMESURFER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Calm solo piano, slow tempo, warm and hopeful",
    "durationSec": 30
  }'

Response200 OK

{
  "kind": "music",
  "status": "completed",
  "url": "https://media.framesurfer.com/media/music/calm-piano.mp3",
  "credits": 10,
  "durationSec": 30
}

Endpoints

Pricing

Get a price

POST/v1/quote

The price of a request, without making anything or charging. Send kind plus the same fields you would send to create it; prompt is optional here.

Body

kindstringrequired
What to price.
...
The fields of that kind's create endpoint.

Response

creditsnumber
What the request would be charged.
availableCreditsnumber
Credits left on the account.
canAffordboolean
Whether the account has enough.
Request
curl -X POST https://framesurfer.com/api/v1/quote \
  -H "Authorization: Bearer $FRAMESURFER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "video",
    "durationSec": 10,
    "resolution": "768p"
  }'

Response200 OK

{
  "kind": "video",
  "credits": 20,
  "durationSec": 10,
  "resolution": "768p",
  "aspectRatio": "9:16",
  "withAudio": true,
  "model": "p-video-2-pro",
  "availableCredits": 1240,
  "canAfford": true
}

Endpoints

Account

List models

GET/v1/models

Every image, video and music option with its credits and the defaults. Video models list the lengths they make, their resolutions and credits per second, and whether they start or end on a picture.

Request
curl https://framesurfer.com/api/v1/models \
  -H "Authorization: Bearer $FRAMESURFER_API_KEY"

Response200 OK

{
  "image": {
    "defaultModel": "gpt-image-2.5",
    "models": [
      {
        "id": "gpt-image-2.5",
        "label": "GPT Image 2.5",
        "credits": 1,
        "editsPictures": true
      }
    ]
  },
  "video": {
    "defaultModel": "p-video-2-pro",
    "models": [
      {
        "id": "p-video-2-pro",
        "label": "Pruna P-Video-2 Pro",
        "allowedDurations": [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
        "defaultDuration": 5,
        "startsFromPicture": true,
        "endsOnPicture": true,
        "creditsPerSecond": [
          {
            "resolution": "480p",
            "silent": 1,
            "withSound": 1
          },
          {
            "resolution": "768p",
            "silent": 2,
            "withSound": 2
          }
        ]
      }
    ]
  },
  "music": {
    "creditsPerTrack": 10,
    "maxDurationSec": 180,
    "defaultDurationSec": 30
  }
}

Shortened here to one model of each kind.

Get the account

GET/v1/account

Credits left and the plan.

Response

creditsnumber
Credits left.
planstring
The plan's name.
Request
curl https://framesurfer.com/api/v1/account \
  -H "Authorization: Bearer $FRAMESURFER_API_KEY"

Response200 OK

{
  "credits": 1240,
  "plan": "Standard"
}