API Reference
Complete reference for the ReelFarm API. Create slideshows, manage automations, publish to TikTok, and more.
Authentication
All requests require a Bearer token. API access is available on the Growth, Scale, and Enterprise plans.
Include your API key in the Authorization header as a Bearer token. All keys start with rf_.
Generating an API Key
- Subscribe to the Growth, Scale, or Enterprise plan
- Go to your ReelFarm dashboard
- Click your user email at the bottom of the sidebar to open Settings
- Click the API Keys tab
- Generate a new API key
curl -X GET https://reel.farm/api/v1/account \
-H "Authorization: Bearer rf_your_api_key_here" \
-H "Content-Type: application/json"Slideshows
Generate slideshows from AI prompts or build them manually with full creative control.
Generate a slideshow
Create a new slideshow from a natural-language prompt. The AI generates text content, selects images, and rendering begins automatically. Returns immediately — the pipeline runs asynchronously (~45 seconds).
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| additional_context | string | Required | Natural-language prompt controlling topic, slide count, text items, font sizes, positions, styles, fonts, tone, and content direction. |
| images | string[] | Optional | 0-indexed array of image URLs for slide backgrounds. images[0] → slide 0, images[1] → slide 1, etc. Remaining slides use auto-selected images. |
{
"additional_context": "5 habits that changed my morning routine, casual first-person tone, 6 slides",
"images": [
"https://example.com/morning-coffee.jpg",
"https://example.com/cold-shower.jpg"
]
}{
"slideshow_id": 12345,
"status": "processing",
"message": "Slideshow generation started. Poll GET /api/v1/slideshows/12345/status to track progress."
}Create a slideshow (direct control)
Create a slideshow by specifying every slide's image, text, styling, and positioning directly. No AI generation — renders in ~10 seconds.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| slides | object[] | Required | Array of slide objects (1–20 slides). Each slide has image_url, text_items, and optional overrides. |
| title | string | Optional | Slideshow title (default "API Slideshow") |
| aspect_ratio | string | Optional | "4:5" (default), "9:16", "1:1", or "16:9" |
| text_position | string | Optional | "top", "center", or "bottom" (default "center") |
| export_as_video | boolean | Optional | Export the slideshow as a video (.mp4) in addition to the rendered slide images (default false) |
| duration | number | Optional | Seconds each slide is displayed in the video. Only applies when export_as_video is true (default 4) |
| is_bg_overlay_on | boolean | Optional | Dark overlay on body slides (default false) |
| is_bg_overlay_on_hook_image | boolean | Optional | Dark overlay on hook slide (default false) |
| background_opacity | number | Optional | Overlay opacity 0–100 (default 20) |
| keep_original_aspect_ratio | boolean | Optional | Use each image's native aspect ratio (default false) |
Slide Object
| Parameter | Type | Required | Description |
|---|---|---|---|
| image_url | string | Optional | Background image URL. Required for single-image slides. |
| image_urls | string[] | Optional | Array of image URLs for grid layouts. |
| image_layout | string | Optional | "single" (default), "1:2", "1:3", "2:1", or "2:2" |
| text_items | object[] | Optional | Text items to overlay. Empty array = image-only slide. |
| aspect_ratio | string | Optional | Override the global aspect ratio for this slide. Options: "4:5", "9:16", "1:1", "16:9" |
| text_position | string | Optional | Override text position for this slide |
TextItem Object
| Parameter | Type | Required | Description |
|---|---|---|---|
| text | string | Required | The text content to display |
| font_size | string | Optional | "extra_extra_small" (6px), "extra_small" (8px), "small" (10px), "medium" (12px), "large" (14px), "extra_large" (16px), or raw "14px" |
| text_style | string | Optional | "outline", "whiteText", "blackText", "yellowText", "white_background", "black_background", "white_50_background", "black_50_background" |
| font | string | Optional | "TikTokDisplay-Bold", "BebasNeue-Regular", "CormorantGaramond-Regular", "CormorantGaramond-Italic" |
| text_width | string | Optional | "100%", "80%", "50%", or "full" |
{
"slides": [
{
"image_url": "https://example.com/hook.jpg",
"text_items": [
{
"text": "5 habits that changed my morning routine",
"font_size": "extra_large",
"text_style": "outline"
}
]
},
{
"image_layout": "2:2",
"image_urls": [
"https://example.com/img1.jpg",
"https://example.com/img2.jpg",
"https://example.com/img3.jpg",
"https://example.com/img4.jpg"
],
"text_items": [
{
"text": "my 4 favorite outfits",
"font_size": "medium",
"text_style": "white_50_background"
}
]
}
],
"aspect_ratio": "4:5",
"text_position": "center",
"export_as_video": true,
"duration": 4,
"is_bg_overlay_on": true
}{
"slideshow_id": 12345,
"status": "processing",
"message": "Slideshow created and rendering started. Poll GET /api/v1/slideshows/12345/status to track progress."
}Check slideshow status
Lightweight polling endpoint for tracking slideshow generation progress. Poll every 5–10 seconds. Once the slideshow reaches the rendering stage, the response also includes video_id and video_status from the videos table for granular rendering progress.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| slideshow_id | integer | Required | The slideshow ID returned from generate or create |
Response Fields
| Parameter | Type | Required | Description |
|---|---|---|---|
| slideshow_id | integer | Optional | The slideshow ID |
| status | string | Optional | "draft", "generating", "rendering", "completed", or "failed" |
| video_id | string | Optional | Only present when a video export exists. The ID of the video record. A slideshow can have multiple exports — this returns the latest. |
| video_status | string | Optional | Only present when a video export exists. Granular rendering progress (e.g. "Downloading and processing images...", "Creating slideshow video...", "Finished"). |
video_id and video_status are omitted entirely when no video record is linked to the slideshow. A slideshow without a video export is just a draft.Status Flow
{
"slideshow_id": 12345,
"status": "rendering",
"video_id": "67890",
"video_status": "Creating slideshow video..."
}{
"slideshow_id": 12345,
"status": "completed",
"video_id": "67890",
"video_status": "Finished"
}{
"slideshow_id": 12345,
"status": "draft"
}Automations
Set up recurring schedules that generate and publish new slideshows to TikTok automatically.
Create an automation
Set up a recurring schedule that generates and publishes new slideshows automatically to your connected TikTok account.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| tiktok_account_id | string | Required | Target TikTok account ID (from GET /tiktok/accounts) |
| schedule | object[] | Required | Array of { cron: "cron-expression" } in Pacific time |
| title | string | Optional | Descriptive name for this automation |
| product_id | integer | Optional | Link to a product in the products table |
| slideshow_hooks | string[] | Optional | Hook/topic templates the AI rotates through |
| style | string | Optional | Natural-language prompt controlling text items, fonts, sizes, positions, tone, and writing style |
| language | string | Optional | Language for generated content (default "English") |
| tiktok_post_settings | object | Optional | TikTok posting config (see below) |
| image_settings | object | Optional | Image and display config (see below) |
tiktok_post_settings
| Parameter | Type | Required | Description |
|---|---|---|---|
| caption | object | Optional | { mode: "prompt"|"static", prompt_text, static_text } |
| description | object | Optional | { mode: "prompt"|"static", prompt_text, static_text } |
| auto_post | boolean | Optional | Auto-publish when ready (default true) |
| visibility | string | Optional | "PUBLIC_TO_EVERYONE", "SELF_ONLY", "MUTUAL_FOLLOW_FRIENDS", "FOLLOWER_OF_CREATOR" |
| auto_music | boolean | Optional | Let TikTok add trending music (default true) |
| post_mode | string | Optional | "MEDIA_UPLOAD" (draft) or "DIRECT_POST" |
| allow_comments | boolean | Optional | Default true |
| allow_duet | boolean | Optional | Default true |
| allow_stitch | boolean | Optional | Default true |
image_settings
| Parameter | Type | Required | Description |
|---|---|---|---|
| first_slide | object | Optional | { collection, mode, single_image } |
| all_slides | string | Optional | Collection ID for body slide images |
| aspect_ratio | string | Optional | "4:5" (default), "9:16", "1:1", etc. |
| is_bg_overlay_on | boolean | Optional | Overlay on body slides (default false) |
| is_bg_overlay_on_hook_image | boolean | Optional | Overlay on hook slide (default false) |
| background_opacity | integer | Optional | 0–100 (default 20) |
| keep_original_aspect_ratio | boolean | Optional | Use each image's native ratio (default false) |
| text_on_first_slide_only | boolean | Optional | Text on hook slide only (default false) |
| no_text_on_slides | boolean | Optional | Remove all text (default false) |
| auto_pull_images | boolean | Optional | Auto-pull from Pinterest (default false) |
| auto_images_no_text | boolean | Optional | No text on auto-pulled images (default false) |
| disable_auto_image_for_first_slide | boolean | Optional | Don't auto-pull for hook (default false) |
| hook_grid_format | string | Optional | "single", "1:2", "1:3", "2:1", "2:2" |
| body_grid_format | string | Optional | Grid layout for body slides |
| cta_slide | object | Optional | { check, cta_collection_check, cta_collection_id, image_id } |
{
"tiktok_account_id": "tt_abc123",
"schedule": [{ "cron": "0 14 * * *" }],
"title": "Fitness motivation posts",
"slideshow_hooks": [
"5 habits that changed my fitness journey",
"What I eat in a day for muscle building"
],
"style": "The first slide should have 1 large text item...",
"language": "English",
"tiktok_post_settings": {
"caption": { "mode": "prompt", "prompt_text": "Write an engaging caption" },
"auto_post": true,
"visibility": "PUBLIC_TO_EVERYONE",
"post_mode": "DIRECT_POST"
},
"image_settings": {
"aspect_ratio": "4:5",
"auto_pull_images": true,
"is_bg_overlay_on": true,
"background_opacity": 30
}
}{
"automation_id": "a1b2c3d4-e5f6-...",
"title": "Fitness motivation posts",
"status": "active",
"product_id": null,
"tiktok_account_id": "tt_abc123",
"schedule": [
{ "job_id": "f7g8h9i0-...", "cron": "0 14 * * *" }
],
"slideshow_hooks": ["5 habits that changed my fitness journey"],
"style": "The first slide should have 1 large text item...",
"language": "English",
"tiktok_post_settings": { ... },
"image_settings": { ... },
"created_at": "2026-03-24T10:00:00Z"
}List automations
Returns all automations belonging to the authenticated user.
{
"automations": [
{
"automation_id": "a1b2c3d4-...",
"title": "Fitness motivation posts",
"status": "active",
"schedule": [
{ "job_id": "f7g8h9i0-...", "cron": "0 14 * * *" }
],
"slideshow_hooks": ["5 habits that..."],
"created_at": "2026-03-24T10:00:00Z"
}
]
}Get a single automation
Returns the full details of one automation.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| automation_id | string | Required | The automation UUID |
// Same shape as a single item in the list responseUpdate an automation
Update any combination of automation settings. To pause/unpause, send { action: 'pause' } or { action: 'unpause' } instead of config fields.
Request Body (config update)
| Parameter | Type | Required | Description |
|---|---|---|---|
| title | string | Optional | Update the title |
| slideshow_hooks | string[] | Optional | Replace the hooks list |
| style | string | Optional | Update the style prompt |
| language | string | Optional | Update the language |
| tiktok_account_id | string | Optional | Change target TikTok account |
| tiktok_post_settings | object | Optional | Update TikTok posting settings |
| product_id | integer | Optional | Change linked product |
| image_settings | object | Optional | Update image/collection settings |
Request Body (pause/unpause)
| Parameter | Type | Required | Description |
|---|---|---|---|
| action | string | Required | "pause" or "unpause" |
{
"slideshow_hooks": ["New hook 1", "New hook 2"],
"style": "Each slide should have 2 text items..."
}{ "action": "pause" }// Returns the full updated automation objectDelete an automation
Permanently delete an automation and all its scheduled jobs.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| automation_id | string | Required | The automation UUID |
{ "message": "Automation deleted" }Run an automation (one-off)
Trigger a one-off slideshow generation using the automation's saved settings. Does not affect the cron schedule.
Request Body (all optional)
| Parameter | Type | Required | Description |
|---|---|---|---|
| hook | string | Optional | Override the hook/topic for this single run. If omitted, picks randomly from the automation's hooks list. |
| mode | string | Optional | "export" (default, generates video) or "draft_only" (slideshow draft only, no video) |
GET /videos?automation_id=...&created_after={time}&limit=1.{ "hook": "My top 5 productivity tools", "mode": "export" }{
"message": "Slideshow generation started",
"automation_id": "a1b2c3d4-...",
"status": "processing",
"mode": "export",
"hook": "My top 5 productivity tools"
}Automation Schedules
Manage cron schedule jobs on an automation. All cron expressions are in Pacific time.
Add a schedule job
Add a new cron schedule to an automation.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| cron | string | Required | Cron expression in Pacific time (e.g. "0 14 * * *" for 2pm daily) |
{ "cron": "0 14 * * *" }// Returns the full updated automation objectUpdate schedule jobs
Update one or more schedule jobs. Supports single update or batch operations.
Single Update
| Parameter | Type | Required | Description |
|---|---|---|---|
| job_id | string | Required | The job to update |
| cron | string | Required | New cron expression |
Batch Update
| Parameter | Type | Required | Description |
|---|---|---|---|
| actions | object[] | Required | Array of { type: "update"|"delete", job_id, cron } |
{ "job_id": "f7g8h9i0-...", "cron": "0 10 * * 1-5" }{
"actions": [
{ "type": "update", "job_id": "f7g8h9i0-...", "cron": "0 10 * * *" },
{ "type": "delete", "job_id": "k1l2m3n4-..." }
]
}Delete a schedule job
Remove a single schedule job from an automation.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| job_id | string | Required | The job to remove |
{ "job_id": "f7g8h9i0-..." }// Returns the full updated automation objectVideos
Access rendered videos, view TikTok analytics, and publish to TikTok.
List videos
Returns rendered videos with optional filtering by automation, video type, status, finished/failed state, and date range. Only safe, public fields are returned.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| automation_id | string | Optional | Filter by automation (created_by_cron_id) |
| video_type | string | Optional | "slideshow", "ugc", or "greenscreen" |
| status | string | Optional | "completed", "processing", or "failed" |
| finished | string | Optional | "true" or "false" |
| failed | string | Optional | "true" or "false" |
| created_after | string | Optional | ISO 8601 timestamp |
| created_before | string | Optional | ISO 8601 timestamp |
| limit | number | Optional | Max results (default 20, max 100) |
| offset | number | Optional | Pagination offset |
Response Fields
| Parameter | Type | Required | Description |
|---|---|---|---|
| created_at | string | Optional | When the video was created |
| status | string | Optional | Current status from the videos table |
| finished | boolean | Optional | Whether the video has finished rendering |
| failed | boolean | Optional | Whether the video failed during rendering |
| created_by_cron_id | string | Optional | Automation cron ID (null if created manually) |
| video_type | string | Optional | "slideshow", "ugc", or "greenscreen" |
| slideshow_images | array | Optional | Rendered slide images (only when video_type is "slideshow") |
| prompt | string | Optional | Generation prompt (only when video_type is "slideshow") |
| video_url | string | Optional | Direct .mp4 URL (only when video_type is "ugc" or "greenscreen") |
{
"videos": [
{
"created_at": "2026-03-24T14:00:00Z",
"status": "completed",
"finished": true,
"failed": false,
"created_by_cron_id": "a1b2c3d4-...",
"video_type": "slideshow",
"slideshow_images": [
{ "id": 0, "image_url": "https://slides.reel.farm/slide_0.png" },
{ "id": 1, "image_url": "https://slides.reel.farm/slide_1.png" }
],
"prompt": "5 habits that changed my morning routine"
}
],
"total": 42,
"limit": 20,
"offset": 0
}{
"videos": [
{
"created_at": "2026-03-25T10:30:00Z",
"status": "completed",
"finished": true,
"failed": false,
"created_by_cron_id": "e5f6g7h8-...",
"video_type": "ugc",
"video_url": "https://automated-content.s3.amazonaws.com/abc123.mp4"
}
],
"total": 5,
"limit": 20,
"offset": 0
}Get a single video
Returns full details for one video including slideshow images and TikTok publish status.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| video_id | string | Required | The video ID |
// Same shape as a single item in the list responseGet TikTok analytics
Returns TikTok post analytics. The video must have been published to TikTok (post_id must be present).
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| video_id | string | Required | The video ID |
{
"post_id": "7123456789",
"video_id": 12345,
"title": "5 habits that changed my morning",
"caption": "morning routine tips",
"view_count": 15400,
"like_count": 1230,
"comment_count": 89,
"share_count": 234,
"bookmark_count": 567,
"tiktok_account_id": "tt_abc123",
"account_username": "@yourusername",
"account_name": "Your Name",
"event": "post.publish.publicly_available",
"publish_type": "DIRECT_PUBLISH",
"published_at": "2026-03-20T14:00:00Z"
}Publish a video to TikTok
Publish a completed video using the automation's saved TikTok settings. The video must have status 'completed' and be linked to an automation.
Request Body (optional)
| Parameter | Type | Required | Description |
|---|---|---|---|
| post_mode | string | Optional | "DIRECT_POST" (publish) or "MEDIA_UPLOAD" (TikTok draft). Defaults to the automation's setting. |
{ "post_mode": "DIRECT_POST" }{
"message": "TikTok publish started",
"video_id": "uuid-1234",
"automation_id": "a1b2c3d4-...",
"status": "processing"
}TikTok
List connected TikTok accounts and view post analytics.
List connected accounts
Returns TikTok accounts connected via OAuth. Only safe fields are returned — no tokens.
{
"accounts": [
{
"tiktok_account_id": "tt_abc123",
"account_name": "Your Name",
"account_username": "@yourusername",
"account_image": "https://..."
}
]
}List TikTok posts with analytics
Returns all TikTok posts with engagement metrics and aggregated statistics.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| timeframe | number | string | Optional | 7, 30, 90, or "all" (default 30) |
| sort | string | Optional | "recent", "views", "likes", "shares", "comments", "bookmarks" |
| tiktok_account_id | string | Optional | Filter by account |
| limit | number | Optional | Default 20, max 200 |
| offset | number | Optional | Pagination offset |
{
"posts": [
{
"post_id": "7123456789",
"video_id": 12345,
"title": "5 habits that changed my morning",
"view_count": 15400,
"like_count": 1230,
"comment_count": 89,
"share_count": 234,
"bookmark_count": 567,
"account_username": "@yourusername",
"published_at": "2026-03-20T14:00:00Z"
}
],
"statistics": {
"total_posts": 28,
"total_views": 450000,
"total_likes": 34000,
"total_comments": 2100,
"total_shares": 5600,
"total_bookmarks": 12000
},
"timeframe": 30,
"sort": "recent",
"limit": 20,
"offset": 0
}Collections
Browse image collections. Collections are currently read-only via the API — create and upload endpoints are coming soon.
List image collections
Returns all image collections belonging to the authenticated user.
{
"collections": [
{
"collection_id": 5191,
"name": "Nature Photos",
"created_at": "2026-03-10T08:00:00Z"
}
]
}Get images in a collection
Returns images in a collection, paginated (max 100 per page).
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | number | Optional | Max results (default 100, max 100) |
| offset | number | Optional | Pagination offset |
{
"collection_id": 5191,
"images": [
{
"image_id": 215115,
"image_url": "https://...",
"added_at": "2026-03-15T12:00:00Z",
"created_at": "2026-03-14T09:00:00Z"
}
],
"total": 248,
"limit": 100,
"offset": 0
}Slideshow Library
Browse real TikTok slideshow profiles for inspiration and competitive research.
List available niches
Returns all unique niches with profile counts, sorted by most profiles.
{
"niches": [
{ "name": "fitness", "profile_count": 142 },
{ "name": "spirituality", "profile_count": 87 },
{ "name": "personal finance", "profile_count": 63 }
],
"total": 45
}Search the library
Search real TikTok slideshow profiles by niche, slide text, product medium, or audience region. At least one filter is required.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| q | string | Optional | Text search across slide content |
| niche | string | Optional | Filter by niche (e.g. "fitness") |
| product_medium | string | Optional | Filter by product type |
| region | string | Optional | Account region code (e.g. "US") |
| audience_region | string | Optional | Audience country code (e.g. "US") |
| sort | string | Optional | "followers" (default) or "recent" |
| limit | number | Optional | Default 3, max 3 |
| offset | number | Optional | Pagination offset |
{
"profiles": [
{
"profile_id": 8554,
"username": "everythingofzodiac",
"nickname": "All About Zodiac",
"bio": "Your daily zodiac feed",
"follower_count": "42.5K",
"niche": "spirituality",
"region": "NL",
"audience_regions": [
{
"count": 20,
"country": "United States",
"percentage": "20.83%",
"countryCode": "US"
}
],
"slideshows": [
{
"index": 0,
"views": "5.0M",
"likes": "233.1K",
"bookmarks": "39.4K",
"image_count": 13
}
]
}
],
"total": 87,
"limit": 3,
"offset": 0
}Get a single profile
Returns full profile details including all slideshow images, following count, and link in bio.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| profile_id | integer | Required | The profile ID |
Same fields as the search response, plus following_count and link_in_bio.
{
"profile_id": 8554,
"username": "everythingofzodiac",
"nickname": "All About Zodiac",
"follower_count": "42.5K",
"following_count": "120",
"link_in_bio": "https://example.com/shop",
"slideshows": [ ... ]
}Search Pinterest for high-resolution images to use in your slideshows.
Search Pinterest for images
Search Pinterest and return full-resolution image URLs. Paginated up to 5 pages per search query.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| q | string | Required | Search query (e.g. "aesthetic coffee") |
| cursor | string | Optional | Pagination cursor from a previous response |
{
"images": [
"https://i.pinimg.com/originals/ab/cd/ef/abcdef123456.jpg",
"https://i.pinimg.com/originals/12/34/56/123456abcdef.jpg"
],
"cursor": "eyJjIjoiYk...",
"has_more": true,
"page": 1,
"total_pages_allowed": 5
}Account
Retrieve account details, subscription tier, and remaining credits.
Get account info
Returns account details, subscription tier, and remaining credits. credits represents your monthly allotment (resets each billing cycle), while purchased_credits are one-time purchases that never reset.
Response Fields
| Parameter | Type | Required | Description |
|---|---|---|---|
| credits | number | Optional | Monthly credit allotment (resets each billing cycle) |
| purchased_credits | number | Optional | One-time purchased credits (never reset) |
| ai_credits | number | Optional | Remaining AI credits |
| subscription_tier | string | null | Optional | "Starter", "Growth", "Scale", or "Unlimited" |
| cancelled | boolean | Optional | Whether the subscription has been cancelled |
| next_reset_date | string | null | Optional | When credits next reset (billing cycle date) |
{
"name": "Jane Doe",
"email": "jane@example.com",
"user_id": "user_abc123",
"cancelled": false,
"next_reset_date": "2027-04-01",
"subscription_tier": "Growth",
"credits": 142,
"ai_credits": 50,
"purchased_credits": 0
}Error Codes
All errors follow a consistent JSON format with an error message and machine-readable code.
| Status | Code | Meaning |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid API key |
| 402 | INSUFFICIENT_CREDITS | Not enough credits |
| 403 | FORBIDDEN | Insufficient permissions |
| 404 | NOT_FOUND | Resource doesn't exist or doesn't belong to you |
| 422 | VALIDATION_ERROR | Invalid request body or parameters |
| 429 | RATE_LIMITED | Too many requests |
| 429 | CONCURRENT_LIMIT | Too many slideshows in progress (max 3) |
{
"error": "Human-readable error message",
"code": "ERROR_CODE"
}{
"error": "Missing or invalid API key",
"code": "UNAUTHORIZED"
}{
"error": "Too many concurrent slideshows. You have 3 in progress. Max is 3.",
"code": "CONCURRENT_LIMIT"
}Rate Limits
All endpoints share a single rate limit of 20 requests per 60-second sliding window, keyed per user.
| Response Header | Description |
|---|---|
| X-RateLimit-Limit | Max requests in the window |
| X-RateLimit-Remaining | Requests remaining |
| X-RateLimit-Reset | Unix timestamp (ms) when window resets |
| Retry-After | Seconds to wait before retrying |
{
"error": "Rate limit exceeded. Try again in 45 seconds.",
"code": "RATE_LIMITED"
}
// Response Headers:
// X-RateLimit-Limit: 20
// X-RateLimit-Remaining: 0
// X-RateLimit-Reset: 1711324800000
// Retry-After: 45