REST API
Generate App Store screenshots programmatically over HTTPS.
Base URL
https://api.framefast.appAuthentication
All /api/* endpoints require a Bearer token with the ff_live_ prefix.
Authorization: Bearer ff_live_your_key_hereStudio Rendering
Pro API keys automatically route to the Studio Rendering pipeline. You get pixel-perfect output, real iPhone frame photography, multi-stop gradients without seams, and better Unicode handling. No code changes needed. Same endpoint, same payload, noticeably better PNG.
/api/generate
Generate a single App Store screenshot. Returns a PNG image.
Request body (JSON)
screenshotstringrequiredBase64 data URL or public URL of the raw screenshot.
templatestringdefault: "dark-gradient"Template ID. Options: dark-gradient, light-clean, colorful, minimal-black, neon.
devicestringdefault: "iphone-16-pro"Device frame. Options: iphone-16-pro, iphone-16, ipad-pro.
captionTopstringMain caption text displayed above or below the device frame.
captionBottomstringSubtitle text.
fontSizenumberdefault: 36Caption font size in pixels.
fontWeightnumberdefault: 700Caption font weight.
textAlignstringdefault: "center"Caption alignment. Options: left, center, right.
badgestringdefault: "none"Badge overlay. Options: none, new, free, rating, pro.
deviceScalenumberdefault: 0.65Scale of the device frame relative to the canvas.
blurBackgroundbooleandefault: falseUse the screenshot as a blurred background behind the device frame.
gradientTextbooleandefault: falseApply a gradient effect to caption text.
textShadowbooleandefault: falseAdd a drop shadow to caption text.
backgroundOverridestringCustom CSS background value (overrides template).
widthnumberdefault: 1290Output width in pixels.
heightnumberdefault: 2796Output height in pixels.
Response
200 OK with Content-Type: image/png. The response body is the raw PNG bytes.
Example: curl
curl -X POST https://api.framefast.app/api/generate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ff_live_your_key" \
-d '{
"screenshot": "https://example.com/my-screenshot.png",
"template": "dark-gradient",
"device": "iphone-16-pro",
"captionTop": "Track your habits",
"captionBottom": "Simple and beautiful",
"badge": "new"
}' \
--output screenshot.pngExample: JavaScript
const res = await fetch(
"https://api.framefast.app/api/generate",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer ff_live_your_key",
},
body: JSON.stringify({
screenshot: "https://example.com/my-screenshot.png",
template: "colorful",
device: "iphone-16-pro",
captionTop: "Track your habits",
}),
}
);
const png = await res.arrayBuffer();
// Save or display the PNG/api/generate-batch
Generate screenshots for multiple App Store sizes in one request. Accepts up to 3 sizes per call. Requesting more returns 400. Chunk your request into multiple calls.
Additional parameter
sizesstring[]default: ["6.7"]Array of size IDs (max 3). Options: 6.9, 6.7, 6.5, 6.1, 5.5, ipad-13, ipad-12.9. See /api/generate-all-sizes below for the full catalog.
Response
Single size: returns raw PNG. Multiple sizes: returns JSON with base64-encoded PNGs.
// Multi-size response
{
"files": [
{ "name": "screenshot-6.7.png", "size": 245891, "data": "iVBOR..." },
{ "name": "screenshot-5.5.png", "size": 198432, "data": "iVBOR..." }
]
}/api/generate-all-sizes
Batch-render the same design across App Store export sizes. Accepts the same body as /api/generate plus a required sizes array.
Size catalog (7 total)
| ID | Label | Dimensions |
|---|---|---|
| 6.9 | iPhone 16 Pro Max | 1320 x 2868 |
| 6.7 | iPhone 15/14 Pro Max | 1290 x 2796 |
| 6.5 | iPhone 11 Pro Max | 1284 x 2778 |
| 6.1 | iPhone 15/14 | 1179 x 2556 |
| 5.5 | iPhone 8 Plus | 1242 x 2208 |
| ipad-13 | iPad Pro 13" | 2064 x 2752 |
| ipad-12.9 | iPad Pro 12.9" | 2048 x 2732 |
Additional parameter
sizesstring[]requiredArray of size IDs from the catalog above. Minimum 1, maximum 3 per call. If omitted or empty, the endpoint returns a 400 with the catalog and chunking instructions so clients can discover it at runtime.
Discovery response (empty `sizes`)
POST /api/generate-all-sizes { "screenshot": "..." }
400 Bad Request
{
"error": "Provide a `sizes` array (max 3 per call). Rendering all 7 in one request exceeds Worker CPU limits.",
"availableSizes": [
{ "id": "6.9", "width": 1320, "height": 2868, "label": "6.9\" Display" },
...
],
"maxPerCall": 3,
"hint": "Call this endpoint 3 times with different `sizes` subsets to cover all 7 App Store device sizes."
}Success response
200 OK
{
"files": [
{ "name": "screenshot-6.9.png", "size": 321044, "width": 1320, "height": 2868, "data": "iVBOR..." },
{ "name": "screenshot-6.7.png", "size": 298112, "width": 1290, "height": 2796, "data": "iVBOR..." },
{ "name": "screenshot-6.5.png", "size": 291008, "width": 1284, "height": 2778, "data": "iVBOR..." }
]
}Example: chunked client (JavaScript)
Render all 7 App Store sizes by issuing 3 sequential calls, then merging the files arrays.
const API = "https://api.framefast.app";
const KEY = "ff_live_your_key";
// Full App Store size set is 7; endpoint caps at 3 sizes/call.
const CHUNKS = [
["6.9", "6.7", "6.5"],
["6.1", "5.5", "ipad-13"],
["ipad-12.9"],
];
const body = {
screenshot: "https://example.com/my-screenshot.png",
template: "dark-gradient",
captionTop: "Track your habits",
};
const allFiles = [];
for (let i = 0; i < CHUNKS.length; i++) {
console.log(`Rendering chunk ${i + 1}/${CHUNKS.length}…`);
const res = await fetch(`${API}/api/generate-all-sizes`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${KEY}`,
},
body: JSON.stringify({ ...body, sizes: CHUNKS[i] }),
});
if (!res.ok) throw new Error(`Chunk ${i + 1} failed: ${res.status}`);
const { files } = await res.json();
allFiles.push(...files);
}
// allFiles now has all 7 App Store sizes as base64-encoded PNGs.
console.log(`Got ${allFiles.length} files`);Example: chunked client (Python)
import requests
API = "https://api.framefast.app"
KEY = "ff_live_your_key"
CHUNKS = [
["6.9", "6.7", "6.5"],
["6.1", "5.5", "ipad-13"],
["ipad-12.9"],
]
body = {
"screenshot": "https://example.com/my-screenshot.png",
"template": "dark-gradient",
"captionTop": "Track your habits",
}
all_files = []
for i, chunk in enumerate(CHUNKS, 1):
print(f"Rendering chunk {i}/{len(CHUNKS)}…")
r = requests.post(
f"{API}/api/generate-all-sizes",
json={**body, "sizes": chunk},
headers={"Authorization": f"Bearer {KEY}"},
timeout=60,
)
r.raise_for_status()
all_files.extend(r.json()["files"])
print(f"Got {len(all_files)} files")/api/templates
List all available templates with their styling defaults.
{
"templates": [
{
"id": "dark-gradient",
"name": "Dark Gradient",
"background": "linear-gradient(135deg, #1a1a2e, #16213e)",
"textColor": "#ffffff",
"captionPosition": "top",
"deviceScale": 0.65,
"fontWeight": 700
}
]
}/api/devices
List all available device frames with their dimensions.
{
"devices": [
{ "id": "iphone-16-pro", "width": 280, "height": 607 },
{ "id": "iphone-16", "width": 270, "height": 586 },
{ "id": "ipad-pro", "width": 340, "height": 453 }
]
}Errors
401: Missing or invalid API key.
400: Missing required screenshot field.
413: Screenshot exceeds 5 MB limit.
500: Server error. Response includes a detail field.