# AI Voice Cloner Source: https://docs.magichour.ai/api-reference/audio-projects/ai-voice-cloner /api-reference/openapi.json post /v1/ai-voice-cloner Clone a voice from an audio sample and generate speech. * Each character costs 0.05 credits. * The cost is rounded up to the nearest whole number # AI Voice Generator Source: https://docs.magichour.ai/api-reference/audio-projects/ai-voice-generator /api-reference/openapi.json post /v1/ai-voice-generator Generate speech from text. Each character costs 0.05 credits. The cost is rounded up to the nearest whole number. # Delete audio Source: https://docs.magichour.ai/api-reference/audio-projects/delete-audio /api-reference/openapi.json delete /v1/audio-projects/{id} Permanently delete the rendered audio file(s). This action is not reversible, please be sure before deleting. # Get audio details Source: https://docs.magichour.ai/api-reference/audio-projects/get-audio-details /api-reference/openapi.json get /v1/audio-projects/{id} Check the progress of a audio project. The `downloads` field is populated after a successful render. **Statuses** - `queued` — waiting to start - `rendering` — in progress - `complete` — ready; see `downloads` - `error` — a failure occurred (see `error`) - `canceled` — user canceled - `draft` — not used # Face Detection Source: https://docs.magichour.ai/api-reference/files/face-detection /api-reference/openapi.json post /v1/face-detection Detect faces in an image or video. Use this API to get the list of faces detected in the image or video to use in the [face swap photo](https://docs.magichour.ai/api-reference/image-projects/face-swap-photo) or [face swap video](https://docs.magichour.ai/api-reference/video-projects/face-swap-video) API calls for multi-face swaps. Note: Face detection is free to use for the near future. Pricing may change in the future. # Generate asset upload urls Source: https://docs.magichour.ai/api-reference/files/generate-asset-upload-urls /api-reference/openapi.json post /v1/files/upload-urls Generates a list of pre-signed upload URLs for the assets required. This API is only necessary if you want to upload to Magic Hour's storage. Refer to the [Input Files Guide](/integration/input-files) for more details. The response array will match the order of items in the request body. **Valid file extensions per asset type**: - video: mp4, m4v, mov, webm - audio: mp3, wav, aac, flac, webm, weba, m4a, opus, ogg, oga, aiff, amr - image: png, jpg, jpeg, jfif, heic, heif, webp, avif, jp2, tiff, tif, bmp - gif: gif, webp, webm > Note: `gif` is only supported for face swap API `video_file_path` field. Once you receive an upload URL, send a `PUT` request to upload the file directly. Example: ``` curl -X PUT --data '@/path/to/file/video.mp4' \ https://videos.magichour.ai/api-assets/id/video.mp4? ``` # Get face detection details Source: https://docs.magichour.ai/api-reference/files/get-face-detection-details /api-reference/openapi.json get /v1/face-detection/{id} Get the details of a face detection task. Use this API to get the list of faces detected in the image or video to use in the [face swap photo](https://docs.magichour.ai/api-reference/image-projects/face-swap-photo) or [face swap video](https://docs.magichour.ai/api-reference/video-projects/face-swap-video) API calls for multi-face swaps. # AI Clothes Changer Source: https://docs.magichour.ai/api-reference/image-projects/ai-clothes-changer /api-reference/openapi.json post /v1/ai-clothes-changer Change outfits in photos in seconds with just a photo reference. Each photo costs 25 credits. # AI Face Editor Source: https://docs.magichour.ai/api-reference/image-projects/ai-face-editor /api-reference/openapi.json post /v1/ai-face-editor Edit facial features of an image using AI. Each edit costs 1 frame. The height/width of the output image depends on your subscription. Please refer to our [pricing](https://magichour.ai/pricing) page for more details # AI GIF Generator Source: https://docs.magichour.ai/api-reference/image-projects/ai-gif-generator /api-reference/openapi.json post /v1/ai-gif-generator Create an AI GIF. Each GIF costs 50 credits. # AI Headshot Generator Source: https://docs.magichour.ai/api-reference/image-projects/ai-headshot-generator /api-reference/openapi.json post /v1/ai-headshot-generator Create an AI headshot. Each headshot costs 50 credits. # AI Image Editor Source: https://docs.magichour.ai/api-reference/image-projects/ai-image-editor /api-reference/openapi.json post /v1/ai-image-editor Edit images with AI. # AI Image Generator Source: https://docs.magichour.ai/api-reference/image-projects/ai-image-generator /api-reference/openapi.json post /v1/ai-image-generator Create an AI image with advanced model selection and quality controls. # AI Image Upscaler Source: https://docs.magichour.ai/api-reference/image-projects/ai-image-upscaler /api-reference/openapi.json post /v1/ai-image-upscaler Upscale your image using AI. Each 2x upscale costs 50 credits for balanced/creative modes, and 25 credits for preserve. 4x upscale costs 200 and 100 credits respectively. # AI Meme Generator Source: https://docs.magichour.ai/api-reference/image-projects/ai-meme-generator /api-reference/openapi.json post /v1/ai-meme-generator Create an AI generated meme. Each meme costs 10 credits. # AI QR Code Generator Source: https://docs.magichour.ai/api-reference/image-projects/ai-qr-code-generator /api-reference/openapi.json post /v1/ai-qr-code-generator Create an AI QR code. Each QR code costs 0 credits. # Body Swap Source: https://docs.magichour.ai/api-reference/image-projects/body-swap /api-reference/openapi.json post /v1/body-swap Swap a person into a scene image using Nano Banana 2 Lite (640px/1k) or Nano Banana 2 (2k/4k). Credits depend on `resolution` (from 50 credits at 640px upward). # Delete image Source: https://docs.magichour.ai/api-reference/image-projects/delete-image /api-reference/openapi.json delete /v1/image-projects/{id} Permanently delete the rendered image(s). This action is not reversible, please be sure before deleting. # Face Swap Photo Source: https://docs.magichour.ai/api-reference/image-projects/face-swap-photo /api-reference/openapi.json post /v1/face-swap-photo Create a face swap photo. Each photo costs 10 credits. The height/width of the output image depends on your subscription. Please refer to our [pricing](https://magichour.ai/pricing) page for more details # Get image details Source: https://docs.magichour.ai/api-reference/image-projects/get-image-details /api-reference/openapi.json get /v1/image-projects/{id} Check the progress of a image project. The `downloads` field is populated after a successful render. **Statuses** - `queued` — waiting to start - `rendering` — in progress - `complete` — ready; see `downloads` - `error` — a failure occurred (see `error`) - `canceled` — user canceled - `draft` — not used # Head Swap Source: https://docs.magichour.ai/api-reference/image-projects/head-swap /api-reference/openapi.json post /v1/head-swap Swap a head onto a body image. Each image costs 10 credits. Output resolution depends on your subscription; you may set `max_resolution` lower than your plan maximum if desired. # Image Background Remover Source: https://docs.magichour.ai/api-reference/image-projects/image-background-remover /api-reference/openapi.json post /v1/image-background-remover Remove background from image. Each image costs 5 credits. # Photo Colorizer Source: https://docs.magichour.ai/api-reference/image-projects/photo-colorizer /api-reference/openapi.json post /v1/photo-colorizer Colorize image. Each image costs 10 credits. # API Reference Source: https://docs.magichour.ai/api-reference/overview Browse every Magic Hour API endpoint by media type. Magic Hour APIs create and manage video, image, and audio projects. Most creation endpoints return a project ID immediately; use the matching details endpoint or a webhook to track completion. See observed median end-to-end times. Learn how to submit, monitor, and download projects. ## Files * [`POST /v1/files/generate-upload-urls`](/api-reference/files/generate-asset-upload-urls) — Generate asset upload URLs * [`GET /v1/files/:id/face-detection`](/api-reference/files/get-face-detection-details) — Get face detection details * [`POST /v1/files/face-detection`](/api-reference/files/face-detection) — Detect faces in an asset ## Video projects ### Manage projects * [`GET /v1/video-projects/:id`](/api-reference/video-projects/get-video-details) — Get video details * [`DELETE /v1/video-projects/:id`](/api-reference/video-projects/delete-video) — Delete a video ### Create projects * [`POST /v1/ai-talking-photo`](/api-reference/video-projects/ai-talking-photo) — AI Talking Photo * [`POST /v1/ai-video-editor`](/api-reference/video-projects/ai-video-editor) — AI Video Editor * [`POST /v1/animation`](/api-reference/video-projects/animation) — Animation * [`POST /v1/audio-to-video`](/api-reference/video-projects/audio-to-video) — Audio to Video * [`POST /v1/auto-subtitle-generator`](/api-reference/video-projects/auto-subtitle-generator) — Auto Subtitle Generator * [`POST /v1/character-replace`](/api-reference/video-projects/character-replace) — Character Replace * [`POST /v1/face-swap`](/api-reference/video-projects/face-swap-video) — Face Swap Video * [`POST /v1/image-to-video`](/api-reference/video-projects/image-to-video) — Image to Video * [`POST /v1/lip-sync`](/api-reference/video-projects/lip-sync) — Lip Sync * [`POST /v1/text-to-video`](/api-reference/video-projects/text-to-video) — Text to Video * [`POST /v1/video-to-video`](/api-reference/video-projects/video-to-video) — Video to Video ## Image projects ### Manage projects * [`GET /v1/image-projects/:id`](/api-reference/image-projects/get-image-details) — Get image details * [`DELETE /v1/image-projects/:id`](/api-reference/image-projects/delete-image) — Delete an image ### Create projects * [`POST /v1/ai-clothes-changer`](/api-reference/image-projects/ai-clothes-changer) — AI Clothes Changer * [`POST /v1/ai-face-editor`](/api-reference/image-projects/ai-face-editor) — AI Face Editor * [`POST /v1/ai-gif-generator`](/api-reference/image-projects/ai-gif-generator) — AI GIF Generator * [`POST /v1/ai-headshot-generator`](/api-reference/image-projects/ai-headshot-generator) — AI Headshot Generator * [`POST /v1/ai-image-editor`](/api-reference/image-projects/ai-image-editor) — AI Image Editor * [`POST /v1/ai-image-generator`](/api-reference/image-projects/ai-image-generator) — AI Image Generator * [`POST /v1/ai-image-upscaler`](/api-reference/image-projects/ai-image-upscaler) — AI Image Upscaler * [`POST /v1/ai-meme-generator`](/api-reference/image-projects/ai-meme-generator) — AI Meme Generator * [`POST /v1/ai-qr-code-generator`](/api-reference/image-projects/ai-qr-code-generator) — AI QR Code Generator * [`POST /v1/body-swap`](/api-reference/image-projects/body-swap) — Body Swap * [`POST /v1/face-swap-photo`](/api-reference/image-projects/face-swap-photo) — Face Swap Photo * [`POST /v1/head-swap`](/api-reference/image-projects/head-swap) — Head Swap * [`POST /v1/image-background-remover`](/api-reference/image-projects/image-background-remover) — Image Background Remover * [`POST /v1/photo-colorizer`](/api-reference/image-projects/photo-colorizer) — Photo Colorizer ## Audio projects ### Manage projects * [`GET /v1/audio-projects/:id`](/api-reference/audio-projects/get-audio-details) — Get audio details * [`DELETE /v1/audio-projects/:id`](/api-reference/audio-projects/delete-audio) — Delete audio ### Create projects * [`POST /v1/ai-voice-generator`](/api-reference/audio-projects/ai-voice-generator) — AI Voice Generator * [`POST /v1/ai-voice-cloner`](/api-reference/audio-projects/ai-voice-cloner) — AI Voice Cloner # Processing Times Source: https://docs.magichour.ai/api-reference/processing-times Observed median end-to-end processing times for Magic Hour API endpoints. These values cover the **rolling 30 days ending July 20, 2026**. Customer-observed end-to-end processing time, including queueing. Processing time varies with queue load, input duration, resolution, and model complexity. These values describe observed customer traffic and are not an SLA. Build timeout handling into every integration. **p50 (median)** means half of completed jobs finished within this time. ## Video endpoints | Endpoint | Median (p50) | | ------------------------------------------------------------------------------------------- | -----------: | | [`POST /v1/ai-talking-photo`](/api-reference/video-projects/ai-talking-photo) | 2m 54s | | [`POST /v1/ai-video-editor`](/api-reference/video-projects/ai-video-editor) | 1m 25s | | [`POST /v1/animation`](/api-reference/video-projects/animation) | 9m 56s | | [`POST /v1/audio-to-video`](/api-reference/video-projects/audio-to-video) | 1m 57s | | [`POST /v1/auto-subtitle-generator`](/api-reference/video-projects/auto-subtitle-generator) | 45s | | [`POST /v1/character-replace`](/api-reference/video-projects/character-replace) | 7m 26s | | [`POST /v1/face-swap`](/api-reference/video-projects/face-swap-video) | 1m 36s | | [`POST /v1/image-to-video`](/api-reference/video-projects/image-to-video) | 1m 14s | | [`POST /v1/lip-sync`](/api-reference/video-projects/lip-sync) | 3m 49s | | [`POST /v1/text-to-video`](/api-reference/video-projects/text-to-video) | 51s | | [`POST /v1/video-to-video`](/api-reference/video-projects/video-to-video) | 12m 52s | ## Image endpoints | Endpoint | Median (p50) | | --------------------------------------------------------------------------------------------- | -----------: | | [`POST /v1/ai-clothes-changer`](/api-reference/image-projects/ai-clothes-changer) | 13s | | [`POST /v1/ai-face-editor`](/api-reference/image-projects/ai-face-editor) | 3s | | [`POST /v1/ai-gif-generator`](/api-reference/image-projects/ai-gif-generator) | 23s | | [`POST /v1/ai-headshot-generator`](/api-reference/image-projects/ai-headshot-generator) | 14s | | [`POST /v1/ai-image-editor`](/api-reference/image-projects/ai-image-editor) | 9s | | [`POST /v1/ai-image-generator`](/api-reference/image-projects/ai-image-generator) | 5s | | [`POST /v1/ai-image-upscaler`](/api-reference/image-projects/ai-image-upscaler) | 22s | | [`POST /v1/ai-meme-generator`](/api-reference/image-projects/ai-meme-generator) | 9s | | [`POST /v1/ai-qr-code-generator`](/api-reference/image-projects/ai-qr-code-generator) | 7s | | [`POST /v1/body-swap`](/api-reference/image-projects/body-swap) | 38s | | [`POST /v1/face-swap-photo`](/api-reference/image-projects/face-swap-photo) | 4s | | [`POST /v1/head-swap`](/api-reference/image-projects/head-swap) | 18s | | [`POST /v1/image-background-remover`](/api-reference/image-projects/image-background-remover) | 4s | | [`POST /v1/photo-colorizer`](/api-reference/image-projects/photo-colorizer) | 9s | ## Audio endpoints | Endpoint | Median (p50) | | --------------------------------------------------------------------------------- | -----------: | | [`POST /v1/ai-voice-generator`](/api-reference/audio-projects/ai-voice-generator) | 21s | | [`POST /v1/ai-voice-cloner`](/api-reference/audio-projects/ai-voice-cloner) | 21s | ## Integration guidance * Use webhooks for long-running jobs instead of aggressive polling. * If polling, use the interval recommended in the integration guide and apply exponential backoff. * Set timeouts based on your product's tolerance for delayed jobs. * Treat every latency figure as directional, not guaranteed. [Learn how to monitor jobs and handle timeouts →](/integration/overview) # AI Talking Photo Source: https://docs.magichour.ai/api-reference/video-projects/ai-talking-photo /api-reference/openapi.json post /v1/ai-talking-photo Create a talking photo from an image and audio or text input. # AI Video Editor Source: https://docs.magichour.ai/api-reference/video-projects/ai-video-editor /api-reference/openapi.json post /v1/ai-video-editor **What this API does** Create the same Video Editor you can make in the browser, but programmatically, so you can automate it, run it at scale, or connect it to your own app or workflow. **Good for** - Automation and batch processing - Adding video editor into apps, pipelines, or tools **How it works (3 steps)** 1) Upload your inputs (video, image, or audio) with [Generate Upload URLs](https://docs.magichour.ai/api-reference/files/generate-asset-upload-urls) and copy the `file_path`. 2) Send a request to create a video editor job with the basic fields. 3) Check the job status until it's `complete`, then download the result from `downloads`. **Key options** - Inputs: usually a file, sometimes a YouTube link, depending on project type - Resolution: free users are limited to 576px; higher plans unlock HD and larger sizes - Extra fields: e.g. `face_swap_mode`, `start_seconds`/`end_seconds`, or a text prompt **Cost** Credits are only charged for the frames that actually render. You'll see an estimate when the job is queued, and the final total after it's done. For detailed examples, see the [product page](https://magichour.ai/products/ai-video-editor). # Animation Source: https://docs.magichour.ai/api-reference/video-projects/animation /api-reference/openapi.json post /v1/animation Create a Animation video. The estimated frame cost is calculated based on the `fps` and `end_seconds` input. # Audio-to-Video Source: https://docs.magichour.ai/api-reference/video-projects/audio-to-video /api-reference/openapi.json post /v1/audio-to-video **What this API does** Create the same Audio To Video you can make in the browser, but programmatically, so you can automate it, run it at scale, or connect it to your own app or workflow. **Good for** - Automation and batch processing - Adding audio to video into apps, pipelines, or tools **How it works (3 steps)** 1) Upload your inputs (video, image, or audio) with [Generate Upload URLs](https://docs.magichour.ai/api-reference/files/generate-asset-upload-urls) and copy the `file_path`. 2) Send a request to create a audio to video job with the basic fields. 3) Check the job status until it's `complete`, then download the result from `downloads`. **Key options** - Inputs: usually a file, sometimes a YouTube link, depending on project type - Resolution: free users are limited to 576px; higher plans unlock HD and larger sizes - Extra fields: e.g. `face_swap_mode`, `start_seconds`/`end_seconds`, or a text prompt **Cost** Credits are only charged for the frames that actually render. You'll see an estimate when the job is queued, and the final total after it's done. For detailed examples, see the [product page](https://magichour.ai/products/audio-to-video). # Auto Subtitle Generator Source: https://docs.magichour.ai/api-reference/video-projects/auto-subtitle-generator /api-reference/openapi.json post /v1/auto-subtitle-generator Automatically generate subtitles for your video in multiple languages. # Character Replace Source: https://docs.magichour.ai/api-reference/video-projects/character-replace /api-reference/openapi.json post /v1/character-replace **What this API does** Create the same Character Replace you can make in the browser, but programmatically, so you can automate it, run it at scale, or connect it to your own app or workflow. **Good for** - Automation and batch processing - Adding character replace into apps, pipelines, or tools **How it works (3 steps)** 1) Upload your inputs (video, image, or audio) with [Generate Upload URLs](https://docs.magichour.ai/api-reference/files/generate-asset-upload-urls) and copy the `file_path`. 2) Send a request to create a character replace job with the basic fields. 3) Check the job status until it's `complete`, then download the result from `downloads`. **Key options** - Inputs: usually a file, sometimes a YouTube link, depending on project type - Resolution: free users are limited to 576px; higher plans unlock HD and larger sizes - Extra fields: e.g. `face_swap_mode`, `start_seconds`/`end_seconds`, or a text prompt **Cost** Credits are only charged for the frames that actually render. You'll see an estimate when the job is queued, and the final total after it's done. For detailed examples, see the [product page](https://magichour.ai/products/character-replace). # Delete video Source: https://docs.magichour.ai/api-reference/video-projects/delete-video /api-reference/openapi.json delete /v1/video-projects/{id} Permanently delete the rendered video. This action is not reversible, please be sure before deleting. # Face Swap Video Source: https://docs.magichour.ai/api-reference/video-projects/face-swap-video /api-reference/openapi.json post /v1/face-swap **What this API does** Create the same Face Swap you can make in the browser, but programmatically, so you can automate it, run it at scale, or connect it to your own app or workflow. **Good for** - Automation and batch processing - Adding face swap into apps, pipelines, or tools **How it works (3 steps)** 1) Upload your inputs (video, image, or audio) with [Generate Upload URLs](https://docs.magichour.ai/api-reference/files/generate-asset-upload-urls) and copy the `file_path`. 2) Send a request to create a face swap job with the basic fields. 3) Check the job status until it's `complete`, then download the result from `downloads`. **Key options** - Inputs: usually a file, sometimes a YouTube link, depending on project type - Resolution: free users are limited to 576px; higher plans unlock HD and larger sizes - Extra fields: e.g. `face_swap_mode`, `start_seconds`/`end_seconds`, or a text prompt **Cost** Credits are only charged for the frames that actually render. You'll see an estimate when the job is queued, and the final total after it's done. For detailed examples, see the [product page](https://magichour.ai/products/face-swap). # Get video details Source: https://docs.magichour.ai/api-reference/video-projects/get-video-details /api-reference/openapi.json get /v1/video-projects/{id} Check the progress of a video project. The `downloads` field is populated after a successful render. **Statuses** - `queued` — waiting to start - `rendering` — in progress - `complete` — ready; see `downloads` - `error` — a failure occurred (see `error`) - `canceled` — user canceled - `draft` — not used # Image-to-Video Source: https://docs.magichour.ai/api-reference/video-projects/image-to-video /api-reference/openapi.json post /v1/image-to-video **What this API does** Create the same Image To Video you can make in the browser, but programmatically, so you can automate it, run it at scale, or connect it to your own app or workflow. **Good for** - Automation and batch processing - Adding image to video into apps, pipelines, or tools **How it works (3 steps)** 1) Upload your inputs (video, image, or audio) with [Generate Upload URLs](https://docs.magichour.ai/api-reference/files/generate-asset-upload-urls) and copy the `file_path`. 2) Send a request to create a image to video job with the basic fields. 3) Check the job status until it's `complete`, then download the result from `downloads`. **Key options** - Inputs: usually a file, sometimes a YouTube link, depending on project type - Resolution: free users are limited to 576px; higher plans unlock HD and larger sizes - Extra fields: e.g. `face_swap_mode`, `start_seconds`/`end_seconds`, or a text prompt **Cost** Credits are only charged for the frames that actually render. You'll see an estimate when the job is queued, and the final total after it's done. For detailed examples, see the [product page](https://magichour.ai/products/image-to-video). # Lip Sync Source: https://docs.magichour.ai/api-reference/video-projects/lip-sync /api-reference/openapi.json post /v1/lip-sync **What this API does** Create the same Lip Sync you can make in the browser, but programmatically, so you can automate it, run it at scale, or connect it to your own app or workflow. **Good for** - Automation and batch processing - Adding lip sync into apps, pipelines, or tools **How it works (3 steps)** 1) Upload your inputs (video, image, or audio) with [Generate Upload URLs](https://docs.magichour.ai/api-reference/files/generate-asset-upload-urls) and copy the `file_path`. 2) Send a request to create a lip sync job with the basic fields. 3) Check the job status until it's `complete`, then download the result from `downloads`. **Key options** - Inputs: usually a file, sometimes a YouTube link, depending on project type - Resolution: free users are limited to 576px; higher plans unlock HD and larger sizes - Extra fields: e.g. `face_swap_mode`, `start_seconds`/`end_seconds`, or a text prompt **Cost** Credits are only charged for the frames that actually render. You'll see an estimate when the job is queued, and the final total after it's done. For detailed examples, see the [product page](https://magichour.ai/products/lip-sync). # Text-to-Video Source: https://docs.magichour.ai/api-reference/video-projects/text-to-video /api-reference/openapi.json post /v1/text-to-video **What this API does** Create the same Text To Video you can make in the browser, but programmatically, so you can automate it, run it at scale, or connect it to your own app or workflow. **Good for** - Automation and batch processing - Adding text to video into apps, pipelines, or tools **How it works (3 steps)** 1) Upload your inputs (video, image, or audio) with [Generate Upload URLs](https://docs.magichour.ai/api-reference/files/generate-asset-upload-urls) and copy the `file_path`. 2) Send a request to create a text to video job with the basic fields. 3) Check the job status until it's `complete`, then download the result from `downloads`. **Key options** - Inputs: usually a file, sometimes a YouTube link, depending on project type - Resolution: free users are limited to 576px; higher plans unlock HD and larger sizes - Extra fields: e.g. `face_swap_mode`, `start_seconds`/`end_seconds`, or a text prompt **Cost** Credits are only charged for the frames that actually render. You'll see an estimate when the job is queued, and the final total after it's done. For detailed examples, see the [product page](https://magichour.ai/products/text-to-video). # Video-to-Video Source: https://docs.magichour.ai/api-reference/video-projects/video-to-video /api-reference/openapi.json post /v1/video-to-video **What this API does** Create the same Video To Video you can make in the browser, but programmatically, so you can automate it, run it at scale, or connect it to your own app or workflow. **Good for** - Automation and batch processing - Adding video to video into apps, pipelines, or tools **How it works (3 steps)** 1) Upload your inputs (video, image, or audio) with [Generate Upload URLs](https://docs.magichour.ai/api-reference/files/generate-asset-upload-urls) and copy the `file_path`. 2) Send a request to create a video to video job with the basic fields. 3) Check the job status until it's `complete`, then download the result from `downloads`. **Key options** - Inputs: usually a file, sometimes a YouTube link, depending on project type - Resolution: free users are limited to 576px; higher plans unlock HD and larger sizes - Extra fields: e.g. `face_swap_mode`, `start_seconds`/`end_seconds`, or a text prompt **Cost** Credits are only charged for the frames that actually render. You'll see an estimate when the job is queued, and the final total after it's done. For detailed examples, see the [product page](https://magichour.ai/products/video-to-video). # Pricing Overview Source: https://docs.magichour.ai/billing/overview Choose the right pricing model for your needs - subscription or usage-based billing. ## Which Pricing Model is Right for You? Magic Hour offers two flexible pricing approaches to fit different usage patterns and business needs. Choose the model that best matches how you plan to use our API. ### **Quick Decision Guide** **Choose Subscription if you:** * Have predictable, steady usage patterns * Want cost certainty and budget planning * Prefer paying upfront for credits * Need immediate access to all your credits * Want to build up credit reserves over time **Choose Usage-Based if you:** * Have variable or unpredictable usage * Want to pay only for what you actually use * Prefer monthly billing after usage * Need volume discounts for high usage * Want to avoid upfront costs ## Side-by-Side Comparison | Feature | Subscription | Usage-Based | | :---------------------- | :------------------- | :----------------------- | | **Payment timing** | Pay upfront | Pay after usage | | **Cost predictability** | Fixed monthly cost | Variable monthly cost | | **Credit expiration** | Never expire | Credits consumed as used | | **Volume discounts** | No | Yes (up to 50% off) | | **Billing frequency** | Monthly or yearly | Monthly only | | **Best for** | Predictable usage | Variable usage | | **Minimum commitment** | Monthly subscription | No minimum | ## Pricing Models Explained ### **Subscription Pricing** Pay upfront for credits that never expire. Perfect for steady, predictable usage. **Key Benefits:** * **Predictable costs** - Know exactly what you'll pay * **Credits upfront** - Get all credits immediately * **Yearly savings** - Get 33% off with annual billing * **No usage tracking** - Use credits at your own pace **Plans Available:** * **Creator**: \$15/mo or \$10/mo (yearly) - 10,000 credits, 1024px resolution * **Pro**: \$45/mo or \$30/mo (yearly) - 30,000 credits, 1472px resolution * **Business**: \$99/mo or \$66/mo (yearly) - 70,000 credits, 4K resolution View subscription plans and features ### **Usage-Based Pricing** Pay only for what you use, with automatic volume discounts as you scale. **Key Benefits:** * **Pay as you go** - No upfront costs or commitments * **Volume discounts** - Up to 50% off at high volumes * **Automatic billing** - Monthly charges based on usage * **Perfect for growth** - Scales with your business **Tier Examples:** * **Starter**: \$0.90 per 1000 credits (576px resolution) * **Creator**: \$1.20 per 1000 credits (1024px resolution) * **Pro**: \$1.95 per 1000 credits (1472px resolution) * **Business**: \$2.50 per 1000 credits (4K resolution) View usage-based pricing and discounts ## Resolution Limits Each subscription tier has different maximum resolution limits for image and video generation. Understanding these limits helps you choose the right plan for your needs. View detailed resolution limits for each subscription tier ## Getting Started ### **Step 1: Estimate Your Usage** * Review your expected monthly API calls * Consider seasonal variations in usage * Factor in growth projections ### **Step 2: Compare Costs** * **Low usage (\< 10,000 credits/month)**: Consider Free tier or Creator subscription * **Medium usage (10,000-50,000 credits/month)**: Compare Creator/Pro subscription vs usage-based * **High usage (> 50,000 credits/month)**: Usage-based often provides better value with volume discounts ### **Step 3: Choose Your Model** * **Steady usage**: Subscription for predictable costs * **Variable usage**: Usage-based for flexibility * **Uncertain usage**: Start with usage-based, switch to subscription later **Not sure?** Start with our Free tier (400 credits + 100/day) to test the API, then choose the model that fits your actual usage patterns. # Resolution Limits Source: https://docs.magichour.ai/billing/resolution-limits Maximum resolution limits for each subscription tier and tool across all Magic Hour APIs. ## Resolution Limits by Subscription Tier Your subscription tier determines the maximum width and height available for image and video generation across all Magic Hour APIs. | Subscription Tier | Maximum Dimension | Example Dimensions | | :---------------- | :---------------- | :------------------------------ | | **Free** | 576px | 576x576, 576x324, 324x576 | | **Creator** | 1024px | 1024x1024, 1024x576, 576x1024 | | **Pro** | 1472px | 1472x1472, 1472x828, 828x1472 | | **Business** | 4096px | 4096x4096, 4096x2304, 2304x4096 | ## How Resolution Limits Work ### **Tier-Limited APIs** For most APIs that respect subscription limits: * **Automatic sizing** determines optimal resolution based on your tier * **Manual sizing** must stay within your tier's maximum dimension * **Both width and height** must be ≤ your tier limit * Invalid dimensions will return an error with suggested alternatives ### **Hard-Capped APIs** For compute-intensive video APIs: * **Maximum resolution enforced** regardless of subscription tier * **Performance optimization** - caps ensure reasonable processing times * **Specific limits:** * AI Headshot Generator: 1344px maximum dimension * AI Clothes Changer: 1472px maximum dimension * AI Talking Photo: 720p (1280x720) * Text to Video: up to 4k (model-dependent; e.g. `kling-3.0` supports 4k) * Image to Video: up to 4k (model-dependent; e.g. `kling-3.0` supports 4k) * Video to Video: 1472px maximum dimension * Animation: 1472px maximum dimension ### **Special Requirements** Some video generation APIs have additional constraints: #### **64-Pixel Increment Requirement** These APIs require both width and height to be divisible by 64: * **Video-to-Video API** - Both dimensions must be multiples of 64 * Other video APIs may have similar requirements (check individual API docs) **Examples of valid 64-divisible dimensions:** * 512x512, 576x576, 640x640, 704x704, 768x768 * 1024x576, 1024x640, 1024x704, 1024x768 * 1472x832, 1472x896, 1472x960 ### **Examples by Tier** **Valid dimensions:** * 576x576 (square) * 576x324 (16:9 ratio) * 576x432 (4:3 ratio) **Invalid dimensions:** * 1024x256 ❌ (width exceeds 576px limit) * 640x640 ❌ (both dimensions exceed 576px limit) **Valid dimensions:** * 1024x1024 (square) * 1024x576 (16:9 ratio) * 576x1024 (portrait) **Invalid dimensions:** * 1920x1080 ❌ (width exceeds 1024px limit) * 1024x1200 ❌ (height exceeds 1024px limit) **Valid dimensions:** * 1472x1472 (square) * 1472x828 (16:9 ratio) * 828x1472 (portrait) **Invalid dimensions:** * 1920x1080 ❌ (width exceeds 1472px limit) * 1472x2000 ❌ (height exceeds 1472px limit) **Valid dimensions:** * 4096x4096 (square) * 4096x2304 (16:9 ratio) * 2304x4096 (portrait) * Any combination where both width ≤ 4096px and height ≤ 4096px **Note:** Business tier supports true 4K resolution for tier-limited APIs only. Hard-capped APIs have their own maximum limits regardless of subscription tier. ## API-Specific Resolution Behavior Different APIs handle resolution limits in different ways: ### **Tier-Limited APIs** These APIs respect your subscription's resolution limits: **Image APIs:** * AI Image Generator * AI Image Upscaler * Face Swap Photo **Video APIs:** * Face Swap Video * Lip Sync ### **Hard-Capped APIs** These APIs have maximum resolution limits regardless of subscription tier: **Image APIs:** * **AI Headshot Generator** - Maximum 1344px across all tiers * **AI Clothes Changer** - Maximum 1472px across all tiers **Video APIs:** * **AI Talking Photo** - Maximum 720p (1280x720) across all tiers * **Text to Video (T2V)** - Up to 4k depending on the model (`kling-3.0` supports 4k; most models cap at 1080p) * **Image to Video (I2V)** - Up to 4k depending on the model (`kling-3.0` supports 4k; most models cap at 1080p) * **Video to Video** - Maximum 1472px across all tiers * **Animation** - Maximum 1472px across all tiers **Hard Caps Override Tiers**: Even Business tier subscribers cannot exceed the hard caps listed above. These limits are set for performance and computational efficiency. ## Upgrading for Higher Resolution Need higher resolution? [Upgrade your subscription](https://magichour.ai/pricing) to unlock: * **Creator**: 2x larger dimensions (1024px vs 576px) * **Pro**: 2.5x larger dimensions (1472px vs 576px) * **Business**: 7x larger dimensions (4096px vs 576px), supports true 4K Compare all subscription tiers and their benefits # Subscription Pricing Source: https://docs.magichour.ai/billing/subscription-pricing Get credits upfront with predictable monthly or yearly billing and bonus credits for annual plans. ## How Subscription Pricing Works **Simple concept:** Pay upfront for a set amount of credits that you can use throughout your subscription period. Perfect for predictable usage patterns. ### **The Basics** 1. **Choose your plan** based on resolution needs and monthly credit requirements 2. **Select billing frequency** - monthly or yearly (yearly saves 33%) 3. **Pay upfront** and receive all credits immediately 4. **Use credits anytime** - credits never expire, even after subscription ends 5. **Auto-renewal** ensures continuous access (can be cancelled anytime) ### **Key Benefits** ✅ **Predictable costs** - Know exactly what you'll pay each month/year\ ✅ **Credits upfront** - Get all your credits immediately to use anytime\ ✅ **Yearly savings** - Get 33% off with annual billing\ ✅ **No usage tracking** - Use credits at your own pace\ ✅ **Higher resolution** - Access to better quality based on your tier ### **How It Differs from Usage-Based** | Subscription | Usage-Based | | :---------------------------- | :------------------------ | | Pay upfront | Pay after you use | | Fixed monthly cost | Variable monthly cost | | Credits never expire | Credits consumed as used | | Immediate credit access | Pay only for what you use | | Perfect for predictable usage | Good for variable usage | ## Choose Your Plan Each plan offers different credit amounts and resolution limits. Higher plans unlock better resolution and more credits per month. ## Need More Credits? If you use more credits than your plan includes, you can purchase additional credit packs anytime. ### **Credit Packs** * **1,000 credits per pack** * **\$3.00 per pack** (\$0.003 per credit) * **Instant delivery** - credits added immediately to your account * **No expiration** - credits never expire, even after subscription ends ### **When to Buy Credit Packs** * **Seasonal spikes** in usage * **One-time projects** requiring extra credits * **Testing higher usage** before upgrading plans * **End-of-month** credit shortfall **Tip:** If you consistently need credit packs, consider upgrading to the next plan tier for better value. ## Practical Examples ### **Content Creator (5,000 credits/month)** * **Free Plan**: 400 + (100 × 30 days) = 3,400 credits/month - **Not enough** * **Creator Plan**: 10,000 credits/month from \$10/mo (billed annually) - **Perfect fit** *Creator plan gives you 2x your needs with room to grow* ### **Marketing Agency (35,000 credits/month)** * **Creator Plan**: 10,000 credits + 25 credit packs = \$10 + \$75 = **\$85/month** * **Pro Plan**: 30,000 credits + 5 credit packs = \$30 + \$15 = **\$45/month** - **Better value** *Pro plan saves \$40/month and provides buffer for growth* ### **Enterprise (200,000 credits/month)** * **Pro Plan**: 30,000 + 170 credit packs = \$30 + \$510 = **\$540/month** * **Business Plan**: 70,000 credits + 130 credit packs = \$66 + \$390 = **\$456/month** - **Much better value** *Business plan saves \$84/month and includes 4K resolution* ### **Yearly vs Monthly Savings** **Pro Plan Example:** * **Monthly**: \$45 × 12 = **\$540/year** * **Yearly**: **\$360/year** (33% off - equivalent to \$30/month) ## Getting Started Ready to subscribe? Here's how to set up your subscription in minutes. Go to [magichour.ai/pricing](https://magichour.ai/pricing) to see all available plans and current pricing. Pricing Page **Monthly**: Pay monthly, cancel anytime **Yearly**: Pay upfront, get 33% off Pricing
    Page Choose based on your monthly credit needs and resolution requirements: * **Creator**: 10,000 credits/month, 1024px resolution * **Pro**: 30,000 credits/month, 1472px resolution * **Business**: 70,000 credits/month, 4096px resolution Pricing Page Click **"Get Plan"** to proceed to checkout. Enter your payment information using any of these secure methods: - **Credit/Debit Cards**: Visa, Mastercard, American Express, Discover - **Digital Wallets**: Apple Pay, Google Pay - **Regional Options**: AliPay and other methods depending on your location All payments are processed securely through Stripe. Review your plan details and click **"Subscribe"**. After successful payment: * You'll be redirected to [My Plan](https://magichour.ai/dashboard/my-plan) * Credits are added to your account within minutes * Start using the API immediately Payment Successful ## Managing Your Subscription ### **Tracking Credit Usage** Monitor your remaining credits in multiple ways: 1. **My Plan Dashboard**: Visit [My Plan](https://magichour.ai/dashboard/my-plan) to see your monthly credit allocation and next renewal date 2. **Any signed-in page**: Check credit balance in the top navigation Create Page ### **Subscription Management** **Upgrade Options**: * **Immediate upgrade**: New plan starts right away, billing cycle resets * **End-of-cycle upgrade**: Change takes effect at next billing date **Downgrade Process**: * Cancel current subscription at [My Plan](https://magichour.ai/dashboard/my-plan) * Downgrade takes effect at end of current billing cycle * All unused credits carry over permanently **Other Management**: * **Cancel anytime**: Access continues until period ends * **Billing frequency**: Switch between monthly/yearly anytime * **Auto-renewal**: Automatically renews unless cancelled ### **What Happens When You Downgrade or Cancel** When your subscription ends or you downgrade to Free: **✅ You Keep:** * **All unused credits** - they carry over permanently * **API access** - continue making requests with remaining credits * **Account history** - all your generated content remains accessible **❌ You Lose (at end of billing cycle):** * **Higher resolution limits** - revert to Free tier (576px max) * **No watermark** - outputs will include watermarks again * **Monthly credit allocation** - revert to Free tier (400 credits + 100/day) * **Premium features** - lose priority support, larger uploads, etc. **💡 Upgrade Anytime:** * **Immediate upgrade**: New benefits start right away, billing resets * **End-of-cycle upgrade**: Changes take effect at next billing date * Your existing credits work at higher resolutions again ### **Important Notes** **Insufficient Credits**: API calls will be rejected if your account doesn't have enough credits for the requested operation. **Credits Never Expire**: Your credits remain in your account permanently, even after your subscription ends. However, you'll lose access to subscription benefits like higher resolution limits and no watermarks. **Usage Monitoring**: Set up alerts in your dashboard to get notified when credits are running low. # Usage-Based Pricing Source: https://docs.magichour.ai/billing/usage-based-pricing Pay only for what you use with automatic monthly billing and volume discounts. ## How Usage-Based Pricing Works **Simple concept:** You pay only for the credits you actually use each month. No upfront payments, no unused credits going to waste. ### **The Basics** 1. **Add a payment method** to your account 2. **Choose your tier** based on resolution needs and expected usage 3. **Use the API** - credits are consumed as you generate content 4. **Get billed monthly** based on your actual usage 5. **Automatic volume discounts** apply as you use more credits ### **Key Benefits** ✅ **No upfront costs** - Start using immediately without buying credits in advance\ ✅ **Pay for actual usage** - Only pay for credits you actually consume\ ✅ **Automatic billing** - Monthly invoices based on previous month's usage\ ✅ **Volume discounts** - Up to 50% savings as you scale\ ✅ **No waste** - Never lose unused credits ### **How It Differs from Subscriptions** | Usage-Based | Subscription | | :------------------------- | :--------------------------- | | Pay after you use | Pay upfront | | Automatic monthly billing | Manual credit purchases | | Volume discounts included | Fixed credit amounts | | No unused credits | Prepaid credits never expire | | Perfect for variable usage | Good for predictable usage | ## Choose Your Tier Each tier offers different resolution limits and credit pricing. Higher tiers unlock better resolution but cost more per credit. ## Volume Discounts (Automatic) The more credits you use each month, the cheaper each credit becomes. Discounts apply automatically to different usage ranges within the same month. **Example:** If you use 150,000 credits on Starter tier: * First 100,000 credits: \$0.90 per 1000 credits * Next 50,000 credits: \$0.81 per 1000 credits (10% discount) * **Total cost:** \$90 + \$40.50 = \$130.50 ### **Discount Tiers by Plan** | Credit usage per month | Discount on credits in this range | Cost per 1000 credits | | ----------------------- | --------------------------------- | --------------------- | | 0 - 100,000 | 0% | \$0.900 | | 100,001 - 1,000,000 | 10% | \$0.810 | | 1,000,001 - 5,000,000 | 20% | \$0.720 | | 5,000,001 - 10,000,000 | 30% | \$0.630 | | 10,000,001 - 50,000,000 | 40% | \$0.540 | | 50,000,000+ | 50% | \$0.450 | | Credit usage per month | Discount on credits in this range | Cost per 1000 credits | | ----------------------- | --------------------------------- | --------------------- | | 0 - 100,000 | 0% | \$1.200 | | 100,001 - 1,000,000 | 10% | \$1.080 | | 1,000,001 - 5,000,000 | 20% | \$0.960 | | 5,000,001 - 10,000,000 | 30% | \$0.840 | | 10,000,001 - 50,000,000 | 40% | \$0.720 | | 50,000,000+ | 50% | \$0.600 | | Credit usage per month | Discount on credits in this range | Cost per 1000 credits | | ----------------------- | --------------------------------- | --------------------- | | 0 - 100,000 | 0% | \$1.950 | | 100,001 - 1,000,000 | 10% | \$1.755 | | 1,000,001 - 5,000,000 | 20% | \$1.560 | | 5,000,001 - 10,000,000 | 30% | \$1.365 | | 10,000,001 - 50,000,000 | 40% | \$1.170 | | 50,000,000+ | 50% | \$0.975 | | Credit usage per month | Discount on credits in this range | Cost per 1000 credits | | ----------------------- | --------------------------------- | --------------------- | | 0 - 100,000 | 0% | \$2.500 | | 100,001 - 1,000,000 | 10% | \$2.250 | | 1,000,001 - 5,000,000 | 20% | \$2.000 | | 5,000,001 - 10,000,000 | 30% | \$1.750 | | 10,000,001 - 50,000,000 | 40% | \$1.500 | | 50,000,000+ | 50% | \$1.250 | Usage resets at midnight on the first of each month PST time ## Practical Examples ### **Small Developer (5,000 credits/month)** * **Starter Tier**: 5,000 × \$0.0009 = **\$4.50/month** * **Creator Tier**: 5,000 × \$0.0012 = **\$6.00/month** *Choose Starter for basic resolution, Creator for higher quality* ### **Growing Business (75,000 credits/month)** * **Starter Tier**: \$67.50/month (all at base rate) * **Creator Tier**: \$90/month (all at base rate) * **Pro Tier**: \$146.25/month (all at base rate) *Volume discounts kick in at 100,000+ credits* ### **High Volume (500,000 credits/month)** **Starter Tier with volume discounts:** * First 100,000: \$90 (base rate) * Next 400,000: \$324 (10% discount) * **Total: \$414/month** (vs \$450 without discounts) **Use our [cost calculator](https://magichour.ai/api#api-cost-calculator) for precise estimates with volume discounts included.** ## Getting Started **Currently requires setup assistance** - Usage-based pricing is not yet self-serve. We're working on making this automatic. **To get started:** 1. **Contact our team** to set up usage-based billing: * Email: [support@magichour.ai](mailto:support@magichour.ai) * Use the Intercom chat widget on our website 2. **Choose your tier** based on resolution needs and expected usage 3. **Add payment method** - We'll help you set this up securely 4. **Start using the API** - Credits are tracked automatically ### **Billing Process** * **Monthly billing** on the same date each month * **High usage accounts** may be billed earlier (e.g., at \$500 threshold) * **Detailed invoices** show credit usage breakdown by tier * **Volume discounts** applied automatically ## Tracking usage In the [Developer Hub](https://magichour.ai/developer), you can see your usage analytics for specific time ranges. Usage Analytics Also, you can view your upcoming invoice amount and total credit usage broken down by tier. Developer Hub # 2026 Source: https://docs.magichour.ai/changelog Latest feature enhancements in Magic Hour ## Added Kling 2.6 for Text-to-Video and Image-to-Video Kling 2.6 is now available for text-to-video and image-to-video generation. It replaces Kling 2.5 as the lower-cost Kling option, and API requests for Kling 2.5 are routed to Kling 2.6. ## New Photo, Face, and Skin Enhancer Tools You can now enhance photos, faces, and skin directly from the creation dashboard. Each tool includes three transformation strengths, giving you more control over the final look. Try it out now: * [Photo Enhancer](https://magichour.ai/create/photo-enhancer) * [Face Enhancer](https://magichour.ai/create/face-enhancer) * [Skin Enhancer](https://magichour.ai/create/skin-enhancer) ## Added Seedance 2.5 for Video Generation Seedance 2.5 is now available for text-to-video, image-to-video, and reference-to-video generation. It delivers high-quality results with improved realism, detail, and motion at 24 FPS. The model supports 480p and 720p output. Generation costs 288 credits per second at 480p and 576 credits per second at 720p. ## Create AI Music Videos You can now turn an audio track, optional reference images, and creative direction into a complete music video. Choose story mode or lip sync mode, with support for 9:16, 16:9, and 1:1 aspect ratios. Try it out now: [Create an AI music video](https://magichour.ai/create/ai-music-video-generator) ## Start Character and Moodboard Creation with Drag and Drop You can now drag and drop an image directly into the Characters or Moodboards view to start the creation process. After dropping an image, you’ll continue in the dedicated creation page. ## Build Photorealistic AI Influencers from Traits Create a photorealistic AI influencer with a game-style character builder instead of writing a prompt. Select traits like character type, appearance, occupation, and accessories from the left panel, then generate the result in the center preview. Customize skin and eye colors with presets or HEX values, expand Advanced options for additional details, and keep your selections intact while generating or retrying. Try it out now: [https://magichour.ai/create/ai-influencer-generator](https://magichour.ai/create/ai-influencer-generator) ## New AI Avatar Generator Product Page Explore the upcoming AI Avatar Generator, designed to turn a Character or photo into a lifelike talking video with a matching voice, custom scene, and script. The new product page covers preset and custom avatars, voice selection, scene creation, first-frame previews, script-based generation, and use cases for marketing, education, social content, and more. The tool is coming soon. Try it out now: [https://magichour.ai/products/ai-avatar-generator](https://magichour.ai/products/ai-avatar-generator) ## Use Photo, Face, and Skin Enhancers from Their Product Pages You can now enhance images directly from the Photo Enhancer, Face Enhancer, and Skin Enhancer product pages. Try it out now: * [https://magichour.ai/products/photo-enhancer](https://magichour.ai/products/photo-enhancer) * [https://magichour.ai/products/face-enhancer](https://magichour.ai/products/face-enhancer) * [https://magichour.ai/products/skin-enhancer](https://magichour.ai/products/skin-enhancer) ## Improved LTX 2.3 in Audio-to-Video The improved LTX 2.3 model is now available in Audio-to-Video. Provide audio, a text prompt, and an aspect ratio to create video whose timing is driven by the uploaded audio. Check it out at [https://magichour.ai/create/audio-to-video](https://magichour.ai/create/audio-to-video) ## New Free Video Colorizer You can now colorize videos with a simple drag-and-drop workflow. The free tool supports videos up to 5 seconds at 480p, with 3 uses available per day. Start colorizing without configuring a color strength setting—just drop in your video and get started. ## Create Videos with Video Colorizer You can now create colorized videos directly from the dashboard. Choose a video, select a resolution from 480p, 720p, or 1080p, and adjust the color strength between low, balance, and high. Try it out now: [https://magichour.ai/create/video-colorizer](https://magichour.ai/create/video-colorizer) ## Improved LTX 2.3 Talking Photo Expressive mode. Talking Photo Expressive now uses an improved LTX 2.3 model. Provide an image, audio, and text prompt to anchor the subject’s identity and composition while the audio drives timing and lip sync. ## Create Moodboards for Consistent Styles You can now create Moodboards to capture and replicate an art style, mood, or visual vibe. Moodboards use a collage of reference images and are available alongside Characters, with presets to help you get started quickly. Try it out now: [https://magichour.ai/create/moodboards](https://magichour.ai/create/moodboards) ## New Free Character Replace Tool You can now replace a character in a video using a simple two-input workflow: upload a video and an image. The tool automatically selects the character and uses the first 3 seconds of your video—no trimming required. The free tool supports up to three generations per day at 480p. Explore the new experience at [https://magichour.ai/products/character-replace](https://magichour.ai/products/character-replace) ## Add Image References to Google Omni Video Editor Google Omni Video Editor now supports up to 10 additional image references alongside the primary video. Upload images through the compact **Add media** box, then review or remove them as compact thumbnails before generating. The primary video remains unchanged, with inline validation for unsupported files, duration, and size limits. ## Save Cloned Voices for Reuse You can now save a cloned voice after uploading or recording audio in AI Voice Cloner. Saved voices are available in AI Voice Generator, AI Voice Changer, and in-app text-to-speech tools. Newly saved voices appear first, followed by voices ordered by recent use, so your most relevant voices are easy to find. ## Try the Free AI Video Editor You can now use the AI Video Editor as a free tool with LTX 2.3. Generations are limited to 3 seconds and 3 uses per day, using the first 3 seconds of your video automatically. Try it out now: [https://magichour.ai/products/ai-video-editor](https://magichour.ai/products/ai-video-editor) ## Manually Select Characters for Character Replacement Character replacement now supports manually selecting the character you want to replace, giving you more control over the editing process. ## Chain Character Outputs Into New Creations You can now continue creating directly from outputs that use a character. Choose **Create with character** when a single character is used, or **Create with characters** when multiple characters are included. Image outputs continue to AI Image Generation, while video outputs continue to Text-to-Video. ## New LTX Video Editor Mode You can now transform existing videos with an LTX-based video editor mode. Upload a video, describe the changes you want, and choose an output resolution up to 1080p. ## Star Your Favorite Voices You can now star voices using the heart icon on voice cards. Starred voices appear first in the voice selector across voice tools, as well as in your library and creation sidebar. ## Use Characters in Headshot and Selfie Generator Headshot and Selfie Generator now supports Character references. Select **Add Character** from the model dropdown to create a character, or choose an existing character to use its reference image and generation settings in your result. Selecting **Add Character** takes you to `/create/characters`, making it easier to create and reuse consistent characters across your generations. ## Bring Characters to the AI Image Generator Users can now discover Characters directly from the AI Image Generator, including a prompt hint to “Add characters with @” and a popup introducing the Characters experience. Characters still use the existing image workflow, directing users to the Characters entrypoint for image creation. ## Character Replace and Animate API You can now use Character Replace through the API, with the option to switch to Animate. The mode defaults to Replace when no option is provided. You can also provide character selection points in the format `x,y,f`, where `x` and `y` are image coordinates and `f` is the frame number. Pass multiple points separated by semicolons, or omit them to let Magic Hour select characters automatically. ## Added Seedance 5 Pro to AI Image Tools Seedance 5 Pro is now available in AI Image Editor and AI Image Generator for sharp, pro-level detail. Choose from 640px, 1K, or 2K output sizes. 640px and 1K images cost 75 credits per image, while 2K images cost 150 credits. 4K output is not supported. ## Create and Reuse Characters Across Magic Hour Characters lets you save a person once and reuse them across Magic Hour tools without repeatedly uploading the same reference images. Create a Character with 1–12 photos, choose a primary image, rename or update it, and use it in supported workflows. Characters are now available in Text to Video, Image to Video, AI Image Editor, and AI Image Generator. Text to Video and Image to Video use the primary image, while the image tools use a collage of the Character’s reference images. ## Added End-Frame Support for Veo 3.1 Veo 3.1 and Veo 3.1 Lite now support an end-frame option in the web app, giving you more control over how generated videos conclude. ## AI Video Editor Now Available via the API You can now use the AI Video Editor through the API. The initial release supports the Google Omni model, with additional models planned for a future update. ## Updated Library Layout for Folders The Library UI has been updated to prepare for folders, making it easier to organize image, video, and audio outputs into collections such as projects or favorites. ## Added Preset Characters to Character Replace and Animate Character Replace and Animate now include preset characters. Preset names are based on their filenames and use the same interface as other presets, making them easy to find and select. ## Choose a Person During Character Replace and Animate Uploads Video uploads in Character Replace and Animate now include the Auto/Select Person field, giving you control over how the person is identified in your video. ## Explore 200+ New Free AI Tools We added more than 200 free, no-signup AI tool pages under `/tools/`. Browse new image editing, image generation, and hybrid tools with ready-to-use workflows for transforming photos or creating images from prompts. Try it out now: [Explore the free AI tools](https://magichour.ai/tools/photo-to-cartoon) ## Use Magic Hour from MCP Clients Magic Hour’s MCP endpoint is now available at [`https://magichour.ai/mcp/`](https://magichour.ai/mcp/). Claude Code, Codex, and other MCP clients can connect with a Magic Hour API key to call Magic Hour tools, including generating presigned URLs and creating and waiting for image projects. Authentication is passed through securely, invalid keys return a clean authentication failure, and bearer tokens are not logged. Try it out now: [Connect to the Magic Hour MCP endpoint](https://magichour.ai/mcp/) ## Shared History for Swap Workflows Face Swap, Head Swap, and Body Swap now share a history by default, making it easier to manage related projects together in one workflow. ## Added a Switch Feature to Body Swap Body Swap now includes a switch feature, giving you more control when working with body swap inputs. ## Characters Explore Characters, a new way to upload 1–12 reference photos of a person once and reuse them consistently across Magic Hour tools. Select saved Characters in Text to Video, Image to Video, AI Image Editor, and AI Image Generator without re-uploading or re-describing the same person. Create and manage your Characters at [https://magichour.ai/create/characters](https://magichour.ai/create/characters). ## Added Nano Banana 2 Lite to AI Image Editor and Generator APIs Nano Banana 2 Lite is now available in the AI Image Editor and AI Image Generator APIs, giving developers another model option for image generation and editing workflows. ## Explore 54 Language-Specific AI Video Translator Pages You can now browse dedicated AI Video Translator pages for 54 supported languages, including Spanish, French, Japanese, Arabic, Hindi, and more. Each page includes language-specific market context, FAQs, supported-language links, and details on how Magic Hour translates, voice-clones, and lip-syncs your video. The pages also link directly to the translator flow with the selected language prefilled once the tool is available. Farsi searches are redirected to the Persian page. Try it out now: [https://magichour.ai/products/ai-video-translator](https://magichour.ai/products/ai-video-translator) ## New Character Replace We added the Character Replace. Upload a video, select the person to replace by clicking a point on any frame, and choose a replacement character by uploading an image or selecting a preset. The workspace also supports Character Only and Full Scene modes, along with negative points that mark objects to preserve in the generated video. The initial version focuses on the core replacement flow for uploaded videos. Try it out now: [https://magichour.ai/create/character-replace](https://magichour.ai/create/character-replace) ## Added Nano Banana 2 Lite to AI Image Tools Nano Banana 2 Lite is now available in AI Image Editor and AI Image Generator. It is built for speed and high-quality results, with the same credit cost as Nano Banana at 1K resolution and the same higher-resolution scaling used for Nano Banana 2. ## New AI Video Editor Creation Flow The AI Video Editor creation flow now brings the core setup into one place: upload and trim a video, add a prompt, choose a model, and select a resolution. The flow uses existing creation components to make it easier to prepare and generate AI video edits. Try it out now: [https://magichour.ai/create/ai-video-editor](https://magichour.ai/create/ai-video-editor) ## Added Gemini Omni for AI Video Editing Gemini Omni is now available for high-quality video editing with flexible control. It supports 720p output and video lengths from 3 to 10 seconds in 1-second increments. Gemini Omni uses a 6x credit multiplier. ## Added References to Kling 3.0 Kling 3.0 now supports reference inputs for more flexible video generation. Add image and video assets to your prompt, then reference them with identifiers such as `@image_1` and `@video_1`. Kling supports up to one video reference. The input type is inferred from the asset itself, and references can be combined in prompts like: ```text theme={null} @image_1 fighting @image_2 in the style of @video_1 ``` ## New Character Replace and Animate Modes Create new videos with Character Replace by swapping a person in a driving video with a reference image. You can also use Animate mode to make a reference image follow the motion—including lip sync—from a video while keeping the image’s background in the final result. The workflow supports multiple tracking points, up to 60 seconds of video, and output resolution capped at 1024px. Generation costs 48 credits per second. ## Added a Contact Form to the API Page The API page now includes a dedicated contact form section, giving users a clear way to get in touch about their API needs. ## Added Seedance 2 Mini for Video Generation Seedance 2 Mini is now available for text-to-video, image-to-video, and reference-to-video generation. It delivers fast, consistent video with strong motion quality. Credit multipliers vary by output: 480p uses 2× credits, 720p uses 4×, and 720p with a video reference uses 6×. ## Estimate Usage-Based API Costs with Graduated Pricing The API cost estimator now supports Usage-Based mode. Switch modes from the section header to compare per-1,000-credit pricing tiers, view your subtotal, and see estimated monthly costs calculated from the graduated pricing ladder. You’ll also see the calculated volume discount for your usage. For Face Swap, Lip Sync, and Talking Photo, resolution options now reflect tier availability: resolutions above 1024x are unavailable on Creator, and resolutions above 1472x are unavailable on Pro. ## Search and Filter Templates While Creating You can now search and filter templates directly in the in-creation flow. Browse templates by **All**, **Popular**, or **New**, with the existing randomized template selection available under **All**. Search and filter controls appear only when **Templates** is selected. ## Estimate API Costs with Subscription Plans You can now estimate API costs based on your expected tool, model, resolution, and quantity usage. The calculator recommends the lowest subscription tier that covers your estimated monthly usage across Creator, Pro, and Business plans. Compare monthly and yearly billing, review your estimated coverage or shortfall, and use the recommended plan card to fill the calculator with the suggested annual plan. The shared cost engine also provides the foundation for future usage-based estimates. ## Choose Photo or Video for Lip Sync and Talking Photo Lip Sync and Talking Photo now include a photo/video toggle, making it easier to choose the right input type before getting started. ## Explore the New AI Video Editor Meet AI Video Editor, a new product page for prompt-based video editing. Learn how to remove, replace, add, change, restyle, or enhance elements in your footage without traditional timeline editing. The tool is coming soon, with examples, use cases, and an overview of how prompt-based video editing works. Try it out now: [https://magichour.ai/products/ai-video-editor](https://magichour.ai/products/ai-video-editor) ## Find Templates in Search Templates now appear in the search modal alongside tools and models. When tools or models match your search, templates are shown below them using the same search interface; when there are no tool or model results, the modal shows the templates search view directly. ## Switch Between Face Swap Photo and Video You can now toggle between photo and video face swap directly in the dashboard. Your selected new face is preserved when switching between the two formats, making it easier to create both versions without starting over. ## Try Flux 2 Klein with Free Image Tools The Flux 2 Klein model page now includes free tools for both text-to-image generation and image editing with an input image. Explore the model and try either workflow directly from the page. ## Explore the New API-v2 Page We launched a new API-v2 page that brings Magic Hour’s popular tools and available models into one place. Browse tools including Face Swap Video, Image to Video, AI Image Editor, Text to Video, Talking Photo, Lip Sync, and more. The page also includes an API calculator that helps you estimate credit usage, compare subscription plans, and find usage-based pricing when your selected plan doesn’t cover your needs. Try it out now: [Explore the API-v2 page](https://magichour.ai/api) ## Wan 2.2 Now Supports Videos Up to 60 Seconds We updated the Wan 2.2 script with a method that works better for longer videos and fixes issues affecting long generations. Wan 2.2 can now generate videos up to 60 seconds, with generation times remaining the same in testing. ## Compare Face Swap Results with a Before-and-After Toggle Face Swap Video now includes a before-and-after toggle, making it easier to compare the original video with the face-swapped result. The controls also use a consistent dark theme for a more cohesive experience. ## A Faster, More Powerful Search Modal The search modal now makes it easier to find tools, models, and templates. Browse recent tools, trending tools, and popular products, or filter results by All, Models, Tools, and Templates. Selecting a model takes you to a compatible tool with that model preselected. The modal is now centered with an improved layout, while `Ctrl+K` and `Cmd+K` continue to open search. Use **See All** to browse every product at [https://magichour.ai/create/tools](https://magichour.ai/create/tools). ## Seedance 1.5 Is Now Available The web app now uses Seedance 1.5 for Seedance video generation. Existing Seedance traffic is automatically redirected to Seedance 1.5, keeping current integrations working while Seedance 1 is deprecated. Seedance 1.5 supports text-to-video and image-to-video generation, with optional audio and support for end-frame images. Reference mode remains available only with Seedance 2.0. ## More Headshot Styles in the Headshot Generator The dashboard’s headshot generator now includes additional headshot types alongside Professional. Choose from the expanded set of styles to create headshots tailored to different looks and use cases. ## Explore AI Templates in One Place We added a new Templates page at `/create/templates`, using the same structure as the Effects page. Browse available templates with updated navigation, category pills, and model selection controls. Try it out now at [https://magichour.ai/create/templates](https://magichour.ai/create/templates). ## New AI Headshot Generator Variation Pages We launched SEO-optimized variation pages for the AI Headshot Generator, each with its own prompt, title, description, and URL. Every page includes tailored before-and-after examples for male and female inputs, with a streamlined gallery and a CTA into the existing AI Headshot Generator flow. Try it out now at [https://magichour.ai/products/ai-headshot-generator](https://magichour.ai/products/ai-headshot-generator). ## Upgraded to Seedance 1.5 Pro for Video Generation Image-to-video and text-to-video generation now use Seedance 1.5 Pro on Fal instead of Seedance 1. Seedance 1.5 Pro also supports optional audio generation. Turning audio on uses double the usual credit cost. ## Explore the New Flux 2 Klein Model Flux 2 Klein is now available in the AI Image Generator and AI Image Editor, giving you a new model to use for creating and editing images. You can also learn more about its capabilities on the new model page and find it in the `/models` directory. ## Run More Generations at Once with Plan-Based Concurrency Limits You can now run multiple web app generations at the same time, based on your plan: * Free: **1 concurrent generation** * Creator: **3 concurrent generations** * Pro: **5 concurrent generations** * Business: **Unlimited concurrent generations** If you reach your limit, we’ll show an upgrade prompt explaining how to run more generations simultaneously. Concurrency limits apply to web app generations only—API usage does not count toward this limit. ## Discover Related Templates from Every Template Template detail cards now show related templates, making it easier to find similar creative starting points. Related templates use the same similarity scoring already available in For You. ## View Generation Details While They’re Still Processing You can now open the details infotip for generations even before they’re complete. Check your generation information while you wait and keep track of what’s in progress. ## Kling Now Supports Reference Images Kling generation now allows you to include reference images, giving you more control over the look and consistency of your results. ## Added Flux Klein for AI Image Generation and Editing Magic Hour now supports the Flux Klein model in both AI Image Generator and AI Image Editor. Flux Klein supports up to 1080p (2K) output and accepts up to 6 input images for image editing. You can also control the output resolution by setting a maximum resolution or providing custom width and height values. ## GPT Image 2 Is Now Available GPT Image 2 now has its own model page and is available in the AI Image Generator and AI Image Editor. You can learn more about the model and start using it directly from the supported creation tools and the `/models` directory. ## Scrub to Choose Any Video Frame The Video Frame Extract modal now includes a timeline scrubber, so you can move through a video and select the exact frame you want to use. The still preview updates as you scrub, and confirming the selection uses the currently displayed frame. Initial positions are preserved for image-to-video slots, including the last frame for start slots and the first frame for end or generic slots. ## Explore Tutorials on the Create Page A new Tutorials section on the Create page makes it easier to discover practical AI creation workflows. Browse guides covering video, image, face swap, lip sync, upscaling, anime transformations, and more. The section includes curated tutorials such as Talking Fruits Drama, Time Travel Vlogs, Viral Skeleton Facts, image-to-video workflows, and other popular techniques from the Magic Hour YouTube channel. ## Chain Video Outputs into Face Swap You can now send video outputs directly to Face Swap from the create pages. Select **Swap Faces** on a video output to open the Face Swap Video workflow with your input video prepopulated. Free users will be directed to the Face Swap Video page to continue. Try it out now: [Face Swap Video](https://magichour.ai/create/face-swap-video) ## Upscale Video Outputs in One Click You can now send video outputs directly to Video Upscaler from the create pages. Select **Upscale** on a video output to continue with your input video prepopulated. Subscribers can open the Video Upscaler workflow directly, while free users will see an upgrade prompt to unlock video upscaling. Try it out now: [Video Upscaler](https://magichour.ai/create/video-upscaler) ## Extract Video Frames for Image Workflows You can now extract a frame from a video and use it as an image input across image input forms. ## Extract Video Frames with a New Confirmation Modal Image inputs can now use shared video frame extraction building blocks. When enabled, a video can be converted into a still image with a preview, loading state, and **Use this frame** or **Cancel** actions. Pressing Esc or clicking outside the modal cancels the action. The extraction supports both the first and last frame and validates the resulting JPEG before it is used as an image input. ## Use Video Frames in Image-to-Video Inputs You can now drop or select a video for either image input in the dashboard and free image-to-video tools. A confirmation modal previews the extracted frame before adding it to your form. The start image defaults to the video’s last frame, while the end image defaults to the first frame. Canceling, pressing Esc, or clicking outside the modal leaves your form unchanged. Existing image uploads continue to work as before. Try it out now: * [Dashboard image-to-video](https://magichour.ai/create/image-to-video) * [Free image-to-video](https://magichour.ai/products/image-to-video) ## Extract Video Frames from More Image Inputs Video frame extraction is now available across additional image inputs in both free tools and dashboard tools. Upload a video where an image is expected, preview the extracted still, and confirm it before using it in your generation. By default, image inputs use the last frame of a video. In image-to-video end-image inputs, the first frame is used instead. ## Expand Your Videos with AI The new AI Video Expander free tool lets you extend your videos with AI. Choose from preset aspect ratios and expand videos up to 5 seconds, with output capped at 1024px (1K). Try it out now: [https://magichour.ai/products/ai-video-expander](https://magichour.ai/products/ai-video-expander) ## Talking Photo Enables Multi-Minute Generations Talking Photo now supports multi-minute video generations, making it possible to create substantially longer talking photo videos. ## Remove Watermarks from Face Swap Videos Free face swap video outputs now include a **Remove watermark** option in the dashboard. Users can view subscription options from the output and, after subscribing, have the watermark removed from their existing video without regenerating it. ## Explore 300 New AI Voices The AI Voice Generator now includes 300 additional voices, with new subpages to help you discover and preview them. We also updated metadata for existing voices, including tags, to make browsing the voices library more useful. ## New AI Music Generator Product Page Explore Magic Hour’s new AI Music Generator page to learn how to create original music from a text prompt. Discover use cases, generation features, and helpful guidance for making music for videos, social posts, podcasts, games, ads, and more. Try it out now: [https://magichour.ai/products/ai-music-generator](https://magichour.ai/products/ai-music-generator) ## Create Music from the Dashboard The AI Music Generator is now available through a dedicated dashboard creation flow. Describe the music you want, adjust your generation settings, preview the result, and download your track. Generation costs 1 credit per second. ## AI Music Generator Colab Script Added a Colab notebook for generating AI music, including songs up to 120 seconds long. Each generation only requires rerunning the section under **EDIT INPUTS**. Generation takes about 40 seconds on an L4 GPU and about 8 seconds on a full RTX GPU. The notebook requires approximately 18 GB of GPU VRAM. ## New Face Swap Photo Inspiration Added a Face Swap Photo inspiration tab to the creation flow, making it easier to discover examples and get started with face swap photos. ## Manage Multiple Library Items at Once My Library now supports grid and list views with bulk selection. Select multiple items to download or delete them in one action, with progress feedback while each operation is processing. ## Add Audio to Video to the API You can now add audio to video through the Magic Hour API, making it easier to create videos with sound programmatically. ## Add Audio References to Seedance 2.0 Seedance 2.0 now supports audio references in the web app, giving you more control over your video generations. ## Discover GPT Image 2 in the Create Carousel GPT Image 2 is now featured in the `/create` carousel for state-of-the-art image generation and editing. ## Get Helpful Tips While Your Render Processes Select modes now show rotating tips at the bottom of the render modal. Use the wait time to learn techniques and ideas that can help improve your future results. ## Discover the AI Image Extender We launched a new AI Image Extender product page to help you learn how to expand images beyond their borders, resize without cropping, and reframe visuals for social media, ads, ecommerce, and design. The page walks through uploading an image, choosing an aspect ratio or custom padding, and extending the image with AI. Explore use cases, key features, and answers to common questions about outpainting, supported formats, output resolutions, and more. The free tool section is marked as coming soon. Try it out now: [https://magichour.ai/products/ai-image-extender](https://magichour.ai/products/ai-image-extender) ## Create Images with AI Image Expander The new AI Image Expander creation flow is now available on the web. Upload an image, optionally add a prompt to guide the out-painted area, choose an output aspect ratio—including a custom ratio—and select a 640px, 1K, or 2K resolution. The flow is currently available behind a feature flag while the generation script is being prepared. Try it out now: [AI Image Expander](https://magichour.ai/create/ai-image-expander) ## Create Image Variations with Nano Banana Models The AI Image Editor now includes a Variations tab with nine ways to generate multiple images from a single input image: Profile Picture Generator, Ad Variations, Product Shots, Relight, Style Variations, Headshot Variations, Angles, Outfit Variations, and Background Variations. Choose from Nano Banana, Nano Banana 2, and Nano Banana Pro, along with 1:1, 16:9, or 9:16 aspect ratios and 2x2 or 3x3 grids. The editor also applies the appropriate model defaults for each variation mode. ## Use Image References in the AI Image Generator You can now drag and drop an image into the AI Image Generator prompt box to continue in the AI Image Editor with your existing inputs preserved. If you were using Z-Image or Flux Schnell, the model automatically switches to the AI Image Editor’s default model. ## Filter Inspiration Templates by Model Inspirations for Image-to-Video and Text-to-Video now include model breakdowns. Select a model to view templates created with it, with only models that have at least 8 templates shown. ## More Model Filters on Effects The Effects page and modal now include additional model pills for Image-to-Video and Text-to-Video. Models with at least 7 templates are included, making it easier to find effects built for the models you want to use. ## Added Five AI Image Variation Tools We added five new product pages and free tools powered by AI Image Editor Variations: * AI Ad Creative Generator — Ad Variations * AI Product Photography — Product Shots * AI Background Changer — Background Variations * AI Photo Relighting — Relight * AI Multi-Angle Photo Generator — Angles Each tool includes preset examples so you can preview different outputs before generating. ## Added Video Expander Creation Flow You can now start a Video Expander project from the dashboard by adding a video and choosing an aspect ratio. Select from common presets—including vertical, widescreen, square, portrait, standard, photo, and cinematic formats—or define a custom width-to-height ratio. ## Added GPT Image 2 to the Models Page GPT Image 2 is now available on the [Magic Hour models page](https://magichour.ai/models), making it easier to discover the new model alongside the rest of the available options. ## Improved Video-to-Video Form Layout The video-to-video form now adapts its menu height to the available space. When no video has been uploaded, the video file input remains visible on most devices, making it easier to get started. ## New AI Variations Product Pages and Free Tools We added dedicated product pages and free tools for six AI Image Editor Variations features: * AI Profile Picture Generator * AI Ad Creative Generator * AI Product Photography * AI Background Changer * AI Photo Relighting * AI Multi-Angle Photo Generator Each free tool includes preset images with preview outputs that users can select to update the hero image. After reaching the daily limit, users are directed to AI Image Editor with the selected Variation pre-filled when available. ## Added Image Extender Generation Script We built an image extender script that supports adjustable image, resolution, aspect ratio, padding, and an optional prompt. Resolution options include 640px, 1K, and 2K, with custom aspect ratios supporting separate left, right, top, and bottom padding controls. ## See Video Duration Before Generating Video tools in the dashboard now show the selected duration persistently above the Generate button. You can confirm the length of your video before starting generation, alongside a more compact resolution display. ## Reuse Previously Used Assets in Face Swap Photo You can now select previously used assets when creating a face swap photo. The asset picker matches the face swap video experience and is available above Advanced Settings. ## Add Kling 3.0 4K to Text-to-Video and Image-to-Video API The API now supports Kling 3.0 4K for text-to-video and image-to-video generation. ## Explore Templates by AI Model New model template pages make it easier to discover what each AI model can create. Browse curated templates in a gallery directly beneath the model overview, then open any template in the familiar template details modal. Each page also includes structured metadata and breadcrumbs to improve discoverability. ## Create Image Variations with More Control The AI Image Editor now includes a Variations workflow for generating multiple versions of an image at once. Choose from 1:1, 16:9, or 9:16 aspect ratios; 2×2 or 3×3 grids; 1K, 2K, or 4K image sizes; and NB1, NB2, or NBP models. Describe the variation you want with a prompt, then generate a grid of results that can be split into individual images. ## Refined Face Swap Product Navigation We productionized the face swap cluster navigation with a cleaner, more integrated experience across the face swap, multiple face swap, head swap, and body swap pages. Users can now explore the available modes from compact, crawlable links, with single and multiple face swap rows linking to their photo pages and format pills linking directly to photo, video, and GIF pages. The navigation also includes clearer active states, polished hover and focus behavior, and improved spacing across desktop and mobile layouts. ## Added Video Output Info Tips Video modes with a middle panel now include an info-tip next to the full-screen control. Open it to view additional information about the video output directly within the create experience. ## Add Reference Assets to Seedance 2.0 You can now use reference assets with Seedance 2.0 for text-to-video and image-to-video generation. Add images, videos, or audio references and mention them in your prompt with tags like `@Image1` or `@Video1` to guide character appearance, style, motion, and more. Every asset referenced in the prompt must be included in the references list, or the generation will be rejected. ## Filter Effects by Video or Image You can now filter the Effects page by video or image effects, making it easier to find the right tool for your project on desktop and mobile. ## Added GPT Image 2 to AI Image Generator and Editor GPT Image 2 is now available in both the AI Image Generator and AI Image Editor. It offers strong prompt adherence and editing with model-default quality. Pricing is 50 credits at 640 px, 100 credits at 1K, 200 credits at 2K, and 400 credits at 4K. ## Create Videos in New Aspect Ratios with Video Outpainting Video outpainting is now available as an add-on to Any-to-Video v2, using the same base models and infrastructure. Upload a video, choose an output aspect ratio, and expand the frame into a new composition. You can trim the source video, select a custom aspect ratio from 1:8 to 8:1, and generate at the resolution available for your plan. Custom aspect ratios are available when you choose Custom. ## View Face Swap Results in the Middle Panel Face Swap now displays generated videos in the middle panel, while video trimming uses the same contextual trimmer available in Lip Sync. This keeps editing and output review in a more consistent workflow. ## Add Multiple Faces to Photo and Video Face Swap The free Face Swap tool now supports multi-face swapping across photo, video, and GIF modes. Select multiple detected faces and choose the replacement face for each one without leaving the existing workflow. Try it out now at [Multiple Face Swap](https://magichour.ai/products/multiple-face-swap) or [Multiple Face Swap Video](https://magichour.ai/products/multiple-face-swap/video). ## Discover Talking Photo Templates Talking Photo templates are now supported across the template experience. You can submit Talking Photo generations as templates, filter for them in Create and Templates, and browse them at `/templates/talking-photo`. Selecting a Talking Photo template from `/create/ai-talking-photo` now automatically populates its values so you can get started faster. Try it out now at [AI Talking Photo](https://magichour.ai/create/ai-talking-photo) or [Talking Photo Templates](https://magichour.ai/templates/talking-photo). ## See Popular Templates at a Glance Template usage counts are now displayed as a fire component, making popular templates easier to recognize while browsing. ## New AI Video Expander Product Page Explore AI Video Expander, a new way to expand video frames and change aspect ratios without cropping. The product page covers video outpainting use cases, including repurposing horizontal clips for TikTok, Reels, Shorts, and vertical videos for YouTube, ads, and presentations. Learn how AI-generated surrounding content can create room for captions, CTAs, and overlays while keeping the original video intact. The free tool is coming soon. Try it out now: [https://magichour.ai/products/ai-video-expander](https://magichour.ai/products/ai-video-expander) ## Create AI Videos from Text, Images, or Clips The AI Video Generator product page now includes free Text-to-Video and Image-to-Video tools in one place. Switch between text, images, and clips using the same familiar controls as the existing free tools. The page description has also been updated to: “Create AI videos from text, images, or clips — all in one place.” Switching between tools keeps you on the same page without changing the URL or other page content. ## Discover Face Swap Inspiration in Video-to-Video The Video-to-Video tool now includes an Inspiration tab with a random assortment of face swap video templates each session, making it easier to find a starting point for your next creation. ## Generate Kling 3.0 Videos in 4K Kling 3.0 now supports 4K video generation. 4K generations cost 30 credits per second, with pricing set proportionally to the existing 720p and 1080p options. ## Use Voice Cloner for Lip Sync and Talking Photo Voice Cloner is now available as an option in the Lip Sync and Talking Photo tools from the dashboard. Select a cloned voice when creating your next talking video. ## Upgraded Audio to Video to LTX-2.3 Audio to Video now uses LTX-2.3, with support for the new FFLF mode. Audio to Video and IA2V modes support Audio to Video generation, while IA2V can also be used for Talking Photo creations. IA2V now replaces the previous v4 workflow for Talking Photo, while existing credits, GPU requirements, and length limitations remain unchanged. ## Improved Model Selection Across Creation Tools We redesigned the model dropdowns in Text-to-Video, Image-to-Video, AI Image Editor, and AI Image Generator. You can now view model descriptions and metadata together, making it easier to compare options and choose the right model. The updated selectors also include search, model filtering, and Favorites for quicker access to frequently used models. On desktop, dropdowns preserve visibility of the input, while mobile uses a focused pop-up experience for browsing available models. ## New Free Video Tools for Seedance, Wan, and LTX-2 You can now try free video tools for Seedance 1.0, Wan 2.2, and LTX-2. Seedance 1.0 is available once per day, while Wan 2.2 and LTX-2 are each available three times per day. ## New Text-to-Video and Image-to-Video Creation Flow We launched a new creation flow for text-to-video and image-to-video, with a streamlined layout for setting up your generations and applying effects. Generation history remains in the standard vertical stack for a familiar browsing experience. ## New Free Flux Schnell and Z-Image Turbo Tools You can now use dedicated free tools for Flux Schnell and Z-Image Turbo, making it easier to try both text-to-image models directly in the web app. ## Try Free Tools Directly from Model Pages Model pages now include free tools for image editing, text-to-image, and video generation. You can try Qwen Edit, Nano Banana 2, Nano Banana, Seedream 4, Flux Schnell, Z-image Turbo, Seedance 1.0, Wan 2.2, and LTX-2 without leaving the model catalog. Daily free usage varies by model, with up to 10 uses for Flux Schnell and Z-image Turbo, and 3 uses for the other models. Try it out now: [Browse the models](https://magichour.ai/models) ## Explore Lip Sync Inspiration Templates The Lip Sync tool now includes an Inspiration tab with a random assortment of video templates each session, making it easier to discover ideas and get started. ## Body Swap Is Now Available via the API You can now use Body Swap through the Magic Hour API, making it easier to add body-swapping workflows to your applications. ## New API Pages for Our Models We’ve added API-focused pages for our models, with model details and code examples to help you get started quickly. Explore the new model pages at [magichour.ai/models/seedance-2-0](https://magichour.ai/models/seedance-2-0). Try it out now: [View the model pages](https://magichour.ai/models/seedance-2-0) ## Multi-Image Support for Nano Banana 2 You can now use multiple images with Nano Banana 2 in the AI Image Editor, bringing its multi-image workflow to the 4K-capable model. ## Explore Image-to-Video Effects The new Effects page brings Magic Hour’s Image-to-Video templates into one place. Browse templates across Camera Motion, Transitions, Visual Effects, Transformations, Styles, Actions, and Showcase. Try it out now: [https://magichour.ai/create/effects](https://magichour.ai/create/effects) ## A New Template-Focused Create Page The Create page is now organized around the tools, models, and templates users want to explore. Discover popular tools, top picks, and top models, then browse all available video, image, and audio tools from one place. Image-to-Video effects are grouped into Camera Motion, Transitions, Visual Effects, Transformations, Styles, Actions, and Showcase. ## Discover Face Swap Ideas with the New Inspiration Tab The Face Swap video tool now includes an Inspiration tab with a random assortment of templates each session, making it easier to find a starting point for your next video. ## Chain Image Outputs into Face Swap You can now start a face swap directly from image outputs in our `/create` pages. Select **Swap Faces** from an image output to continue with that image as the base image. Free users are taken to the Face Swap page with the base image prepopulated. Try it out now: [https://magichour.ai/create/face-swap-photo](https://magichour.ai/create/face-swap-photo) ## Control Audio Timing in Talking Photo You can now set the audio start and end time when creating a Talking Photo, giving you more control over which portion of your audio is used for the result. ## Chain Image Outputs into Talking Photo You can now send image outputs from the `/create` pages directly to Talking Photo. Select **Talking Photo** from an image output, and you’ll be taken to the Talking Photo tool with your image prepopulated. Try it out now: [Create a Talking Photo](https://magichour.ai/create/ai-talking-photo) ## Create More Consistent Videos with Kling 3.0 Omni Kling 3.0 Omni is now available for image-to-video generation. Add reference images and videos to create more consistent characters, replace characters, and remix videos. You can use up to one reference video or seven reference images. When a reference video is included, you can upload up to four total reference images or elements. Generations support durations of up to 15 seconds. ## Add Reference Videos to Seedance 2.0 Seedance 2.0 now supports reference-to-video generation with reference videos, images, and audio. Add up to one video, nine images, and three audio references to guide your generation. Reference videos can be 2–15 seconds long, while image and audio references must be under 10 MB each. Video inputs are supported in MP4 and MOV formats. ## Resolution-Based Pricing for Image Generation and Editing Image generation and editing now support resolution-based credit pricing. Choose from Auto, 2K, or 4K where supported by the selected model, with credits charged based on the selected resolution bucket—not the final image dimensions. Free users are limited to the default 640px resolution, while paid users can access higher resolutions for eligible models. The selected resolution preserves the image’s aspect ratio and is adjusted to provider-supported dimensions. 4K is not available for Qwen, Z-Image, or Flux Schnell. ## Free AI Character Swap Tool You can now swap a character into any image with Magic Hour’s free character swap tool—no sign-up required. Upload a target scene, upload the person you want to swap in, and click **Swap Characters** to generate a new still image. Free usage includes 1 character swap per day. The new product page also includes step-by-step instructions, use cases, feature highlights, and guidance for getting the best results. Try it out now: [https://magichour.ai/products/character-swap](https://magichour.ai/products/character-swap) ## LTX Image-to-Video Now Supports 1- and 2-Second Generations LTX image-to-video generations now support 1-second and 2-second durations in both the Magic Hour web app and API, giving you more flexibility for short video clips. ## Seedance 2.0 Is Now Available in Create Seedance 2.0 is now featured in the Create carousel as a newly launched model for state-of-the-art video generation. It’s designed for cinematic shots, action, and storytelling, with options for both text-to-video and image-to-video. ## New Model Leaderboards Across Four Modalities We added model leaderboard pages for Text-to-Image, Text-to-Video, Image-to-Video, and Text-to-Speech. Compare models across each modality, including available performance and speed data. ## Character Swap Dashboard Is Now Available Character Swap is now available in the dashboard alongside Head Swap. Upload a target scene and the person to swap in, then generate the result for 100 credits. The Character Swap API supports `type: "character-replace"` with two image keys: one for the target scene and one for the person being swapped in. ```python theme={null} type = "character-replace" image_keys = [target_scene_key, person_to_swap_key] ``` ## Added Seedance 2.0 for Text-to-Video and Image-to-Video You can now create videos with Seedance 2.0 in both Text-to-Video and Image-to-Video. The model is available at 480p and 720p, with 720p generation priced at \$0.2427 per second. ## New AI Image Editing Model Leaderboard You can now explore the AI Image Editing model leaderboard to compare available models in one place. This release includes a model leaderboard hub and a dedicated AI Image Editing leaderboard page, with a flexible format ready for additional categories in the future. ## Choose a Language for Voice Generation and Cloning The free Voice Generator and Voice Cloner now include a language selector. Choose Auto, Chinese, English, Japanese, Korean, German, French, Russian, Portuguese, Spanish, or Italian for more control over your audio results. ## Add Tags When Submitting Templates You can now add tags when submitting templates, making it easier to categorize and organize them from the start. ## Added Veo 3.1 Lite for Text-to-Video and Image-to-Video Veo 3.1 Lite is now available for text-to-video and image-to-video generation as a more affordable option for fast, high-quality daily generations. Veo 3.1 Lite costs 48 credits per second at 720p and 72 credits per second at 1080p. ## Explore All Tools in One Place We built a new `/create/tools` page to make it easier to discover and access Magic Hour’s creative tools. Newly launched tools can now be highlighted using a simple recency-based “New” label. ## Drag and Drop Files Anywhere on Free Tools Free tools that accept file uploads now support full-screen drag and drop. When a tool accepts different file types, dropping a file routes it to the correct input automatically. For tools with two inputs of the same type, the screen is split into two equally sized drop zones so you can place each file where it belongs. ## WAN 2.2 Model in Text-to-Video and Image-to-Video API We've added the WAN 2.2 model to both the Text-to-Video and Image-to-Video APIs, giving you another high-quality option for generation. Choose it by setting the model to "wan-2.2" in your request. ```ts theme={null} // Text-to-Video await client.v1.textToVideo.generate({ model: "wan-2.2", prompt: "A cinematic timelapse over snow-capped mountains at sunrise", }); // Image-to-Video await client.v1.imageToVideo.generate({ model: "wan-2.2", imageUrl: "https://example.com/source-image.jpg", }); ``` ## Added Wan 2.2 for Image-to-Video and Text-to-Video Added Wan 2.2 for Image-to-Video and
Text-to-Video Wan 2.2 is now available for Image-to-Video and Text-to-Video. Expect visibly higher quality compared to LTX-2. Try it out now: [https://magichour.ai/create/image-to-video](https://magichour.ai/create/image-to-video), [https://magichour.ai/create/text-to-video](https://magichour.ai/create/text-to-video) ## Storyboard Generator Upgraded to Nano Banana 2; Nano Banana Deprecated Nano Banana 2 (NB2) is now available in the Storyboard Generator. NB2 supports 4K and delivers higher quality results than the original Nano Banana. The original Nano Banana model is deprecated. Try it out now: [https://magichour.ai/create/ai-storyboard-generator](https://magichour.ai/create/ai-storyboard-generator) ## Head Swap in the API Head swap is now available in the Magic Hour API, enabling you to programmatically swap heads in your content. Use it to automate personalized creatives and power interactive experiences directly from your app or workflow. Try it out now: [https://docs.magichour.ai/api-reference/image-projects/head-swap](https://docs.magichour.ai/api-reference/image-projects/head-swap) ## One-Step Lip Sync Creation One-Step Lip Sync Creation We simplified the Lip Sync creation flow to a single step, so you can get from idea to result faster with fewer clicks. Try it out now: [https://magichour.ai/create/lip-sync](https://magichour.ai/create/lip-sync) ## Previously Used Images in AI Image Editor (Desktop) Previously Used Images in AI Image Editor
(Desktop) On desktop, the AI Image Editor now surfaces your previously used images during the creation flow so you can quickly pick up where you left off or reuse a reference. Try it out now: [https://magichour.ai/create/ai-image-editor](https://magichour.ai/create/ai-image-editor) ## Specifying Language in Voice Tools TTS v2: Optional Language Parameter (Auto by
default) You can now specify a language when using the voice tools. If you don’t provide a value, we’ll use Auto detection by default. Try it out now: [https://magichour.ai/create/ai-voice-generator](https://magichour.ai/create/ai-voice-generator) ## Choose Output Resolution in AI Image Editor Choose Output Resolution in AI Image Editor
v2 You can now select the output resolution directly in the AI Image Editor tool. Try it out now: [https://magichour.ai/create/ai-image-editor](https://magichour.ai/create/ai-image-editor) ## End-Frame Support in Image-to-Video API We added end-frame support to the Image-to-Video API. Now, you can provide an end frame to use as the last frame of the video. ```python theme={null} res = client.v1.image_to_video.generate( assets={ "image_file_path": "/path/to/1234.png", "end_image_file_path": "/path/to/1234.png", }, end_seconds=5.0, ) ``` ## Out-of-credit API errors now return HTTP 402 Our API now returns HTTP 402 Payment Required when a request fails due to insufficient credits (previously 422). This better reflects the billing-related nature of the error. If you check for specific status codes, update your error handling to look for 402. ```ts theme={null} // Example: handle out-of-credit responses const res = await fetch(url, options); if (res.status === 402) { // Payment Required — prompt user to add credits or update billing throw new Error("Payment required: please add credits to continue."); } if (!res.ok) { // Handle other errors const err = await res.text(); throw new Error(`Request failed: ${res.status} ${err}`); } // Proceed with success path const data = await res.json(); ``` ## Refreshed Lip Sync free tool UI Refreshed Lip Sync free tool UI We updated the Lip Sync free tool with a cleaner, more consistent interface. The refreshed layout makes common actions easier to find and use. Try it out now: [https://magichour.ai/products/lip-sync](https://magichour.ai/products/lip-sync) ## Revamped Talking Photo free tool UI Revamped Talking Photo free tool
UI The Talking Photo free tool now sports an updated UI for a more streamlined experience. The refreshed visuals align it with the rest of the Magic Hour toolkit. Try it out now: [https://magichour.ai/products/ai-talking-photo](https://magichour.ai/products/ai-talking-photo) ## Audio-to-Video Comes to the Dashboard Audio-to-Video Comes to the
Dashboard Turn any audio clip into a stylized video right from the Video section of the dashboard. Try it out now: [https://magichour.ai/create/audio-to-video](https://magichour.ai/create/audio-to-video) ## Free Video Extender Free Video Extender We launched a Video Extender that appends 5 seconds to your uploaded clip—no prompt or audio needed. Try it out now: [https://magichour.ai/products/ai-video-extender](https://magichour.ai/products/ai-video-extender) ## New AI Video Extender New AI Video Extender Easily extend your existing videos with our new AI Video Extender. Upload a clip, choose how much longer you want it, and optionally guide the continuation with a prompt or add audio. No subscription required. * Cost: 1 credit per frame * Typical speed: \~60s for a 5s 576x576 video * Inputs: Video, Length, Resolution, Prompt (optional), Audio (optional) Try it out now by visiting [https://magichour.ai/create/ai-video-extender](https://magichour.ai/create/ai-video-extender). ## Free Add Sound to Video (5s Preview) Free Add Sound to Video (5s
Preview) We launched a free tool that generates audio for your uploaded video and merges it back into the clip. Upload a video and generation starts immediately so you can hear results fast. You get 3 free generations per day, and each preview covers the first 5 seconds of your video. Output is the same video file with the newly generated audio track—no separate audio download required. The primary action is labeled “Add Sound to Video (5s)”. ## Nano Banana 2 in AI Image Editor and Generator Nano Banana 2 in AI Image Editor and
Generator Nano Banana 2 is now available in both the AI Image Editor and AI Image Generator. In the model selector, it appears with the description: “Fast, high-quality with strong prompt adherence and reliable text.” Due to higher compute costs, Nano Banana 2 consumes 2x the credits of Nano Banana when selected. ## New Video to Audio Tool New Video to Audio Tool Turn any video into an audio track in the browser. We've added a simple, fast Video to Audio creator under the Audio section, using our familiar creation template—no subscription required. Inputs: * Video * Length (trims video if shorter than the source) * Resolution * Prompt (optional) Generation takes about \~60s for a 5s 576x576 video. Usage is billed at 1 credit per frame. Try it out now: [https://magichour.ai/create/video-to-audio](https://magichour.ai/create/video-to-audio) ## Added LTX-2 Model for Image-to-Video and Text-to-Video We now added LTX-2 Model for Image-to-Video and Text-to-Video. The big change we made is that you can use LTX-2 (at 480p) without a subscription. LTX-2 Model in Image-to-Video You can try it now by visiting [https://magichour.ai/create/image-to-video](https://magichour.ai/create/image-to-video) and [https://magichour.ai/create/text-to-video](https://magichour.ai/create/text-to-video). ## Added Kling 3.0 support to APIs We now added Kling 3.0 to our API and released it for all SDKs. ```python theme={null} res = client.v1.image_to_video.generate( ... model="kling-3.0", ) ``` Try it out now by upgrading to the latest version of the SDK. ## Added Kling 3.0 To Image-to-Video and Text-to-Video We introduced Kling 3.0 to Image-to-Video and Text-to-Video. It is a new model that is faster, sharper, and has better results. Kling 3.0 in Image-to-Video You can try it now by visiting [https://magichour.ai/create/image-to-video](https://magichour.ai/create/image-to-video) and [https://magichour.ai/create/text-to-video](https://magichour.ai/create/text-to-video). ## Named export for Client in Node SDK To resolve import issues between cjs/esm modules, we have added a named export for the Client class in the Node SDK. This fixes the issue "TypeError: Client is not a constructor" when importing the Client class in a Node.js environment. ```ts theme={null} const { Client } = require("magic-hour"); import { Client } from "magic-hour"; ``` Try it out now by upgrading to the latest version of the SDK. ## Updated AI Image Editor API to include models param You can now specify the following models for AI Image Editor: * `qwen-edit` * `nano-banana` * `nano-banana-pro` * `seedream-v4` * `seedream-v4.5` ```python theme={null} res = client.v1.ai_image_editor.generate( ... model="nano-banana", ) ``` Try it out now by upgrading to the latest version of the SDK. ## AI Storyboard Generator We launched a new tool called AI Storyboard Generator. It allows you to generate a storyboard for your video project. AI Storyboard Generator Try it out now: [https://magichour.ai/create/ai-storyboard-generator](https://magichour.ai/create/ai-storyboard-generator) ## Cheaper AI Image Upscaler We introduced a new mode for AI Image Upscaler that is 50% cheaper. We are calling it `Pro` mode. | Mode | Scale Factor | Credits | | :------- | :----------- | :------ | | Pro | 2x | 25 | | Creative | 2x | 50 | | Pro | 4x | 100 | | Creative | 4x | 200 | Try it out now: [https://magichour.ai/create/ai-image-upscaler](https://magichour.ai/create/ai-image-upscaler) ## M4A Audio Support Added support for `.m4a` audio uploads so you can upload more types of audio files. This feature is available to all modes with audio input. ## Lip Sync Mode Improvements We made some internal improvements to Lip Sync standard and pro modes for smoother, more reliable results. Try it out now: [https://magichour.ai/create/lip-sync](https://magichour.ai/create/lip-sync) Note: This feature is currently available to users with a subscription. ## Video Upscaler Dashboard Tool Added a new dashboard tool to upscale videos. Video Upscaler Dashboard Tool Try it out now: [https://magichour.ai/create/video-upscaler](https://magichour.ai/create/video-upscaler) Note: This feature is currently available to users with a subscription. ## AI Image Generator API Model Selection The AI Image Generator API now accepts a `model` parameter so users can choose between available models. ```python theme={null} res = client.v1.ai_image_generator.create( ... model="z-image-turbo", ) ``` Try it out now by upgrading to the latest version of the SDK. ## Improved Head Swap Dashboard We added Head Swap to the dashboard experience. Head Swap Dashboard Try it out now: [https://magichour.ai/products/head-swap](https://magichour.ai/products/head-swap) ## Model Selection for Image-to-Video and Text-to-Video APIs Added a `model` parameter to the Image-to-Video and Text-to-Video APIs so users can choose between available models. This brings the same model selection experience to the API as the create page. ```python theme={null} res = client.v1.image_to_video.create( ... model="sora-2", ) ``` Try it out now by upgrading to the latest version of the SDK. ## Head Swap Free Tool Launched the free Head Swap tool at `/products/head-swap` for quick, zero-cost swaps. Head Swap Free Tool Try it out now: [https://magichour.ai/products/head-swap](https://magichour.ai/products/head-swap) ## New Usage Page Added a dedicated `/usage` page so users can track their consumption at a glance. Usage Page ## Node SDK URL Inputs The Node SDK now supports URL inputs for `.generate` functions in v0.45.0, previously it only supported local file paths. ## Added `max_resolution` to AI Talking Photo API We added a new `max_resolution` param to the AI Talking Photo API. You can now specify the maximum resolution of the output video if you want a specific resolution. ```python theme={null} res = client.v1.ai_talking_photo.generate( ... max_resolution="576" ) ``` ## Added Nano Banana Pro to AI Image Generator AI Image Generator now includes Nano Banana Pro as a new model option. Nano Banana Pro in AI Image
Generator Try it out now: [https://magichour.ai/create/ai-image-generator](https://magichour.ai/create/ai-image-generator) ## Higher Free User Resolution Free users can now generate sharper outputs with a higher max resolution of 576px (up from 512px). ## Improved AI Clothes Changer We improved the AI Clothes Changer to use Qwen for better results. Improved AI Clothes Changer Try it out now: [https://magichour.ai/create/ai-clothes-changer](https://magichour.ai/create/ai-clothes-changer) ## Improved Photo Colorizer We've improved the Photo Colorizer with better color accuracy and more natural results. Try it out now! Before: Improved Photo Colorizer After: Improved Photo Colorizer Try it out now: [https://magichour.ai/create/photo-colorizer](https://magichour.ai/create/photo-colorizer) ## Voice Cloner API We've added support for the Voice Cloner API, allowing you to clone voices programmatically. To take advantage of this change, please upgrade your SDK version to the latest. * [Python SDK](https://github.com/magichourhq/magic-hour-python/releases) * [Node SDK](https://github.com/magichourhq/magic-hour-node/releases) * [Go SDK](https://github.com/magichourhq/magic-hour-go/releases) * [Rust SDK](https://github.com/magichourhq/magic-hour-rust/releases) ## Qwen-Edit Model for AI Image Editor We added Qwen-Edit as a new model option for AI Image Editor. This model has great performance at 1/5 the cost of our other models. Qwen-Edit Model for AI Image Editor Try it out now: [https://magichour.ai/create/ai-image-editor](https://magichour.ai/create/ai-image-editor) # 2023 Source: https://docs.magichour.ai/changelog/2023 List of feature releases in 2023 ## More Frames for Sign Up! We increased sign up frames from **300** -> **400**! Now you can try out more of the product! ## Limited Time Access To Video-to-Video for All Users For a limited time only, everyone has access to our premium Video-to-Video mode. Try it out now: [https://magichour.ai/create/video-to-video](https://magichour.ai/create/video-to-video) ## Currency Support for Subscriptions Now, if you visit [https://magichour.ai/pricing](https://magichour.ai/pricing), the subscription pricing will be converted to your local currency, making it easier than ever to sign up for a subscription. Pricing Euro ## Currency Support for Frame Packs Now, if you visit [https://magichour.ai/pricing](https://magichour.ai/pricing), the frame pack pricing will be converted to your local currency! Pricing Euro ## Animation Previews and Cancels, Plus Faster Rendering Image previews and cancels are now available for [Animation](https://magichour.ai/create/animation), and render speeds is now 60% faster. ## 8 New Art Styles for Video-to-Video Video-to-Video Art Styles Try it out now [https://magichour.ai/create/video-to-video](https://magichour.ai/create/video-to-video) ## Video-to-Video Previews and Cancels In Video-to-Video mode, you can now preview frames as they render and cancel the render to receive all frames back, regardless of progress. No more wasted renders. 🔥 Video-to-Video Preview Try it out now [https://magichour.ai/create/video-to-video](https://magichour.ai/create/video-to-video) ## More Frames for Sign Up! We increased sign up frames from **200** -> **300**! ## Share Your Own and Watch Other User's Videos We added the ability to share your videos publicly from the video details page. Share Video And a feed to watch other user's videos: [https://magichour.ai/feed](https://magichour.ai/feed) ## Delete Video You can now delete generated videos to keep your dashboard clean. You can delete from * Project details page * Dashboard project tile menu ## Higher Resolution for Pro Subscribers We increased maximum video resolution fro pro users from **1280** to **1344**! ## Improved Quality for Video-to-Video We made improvements to the quality of the outputs. Go check it out now [https://magichour.ai/create/video-to-video](https://magichour.ai/create/video-to-video) ## Reserve Your Magic Hour Username *Pst*: reserve your username at [https://magichour.ai/settings](https://magichour.ai/settings) We're launching some social features soon and this will allow you to get the name you want. Share Video ## Launching Magic Hour Blog 🌟 Have you heard about our blog? We've launched a treasure trove of articles aimed at helping you become a pro at AI video generation. Dive into the insights and tips at [https://blog.magichour.ai/](https://blog.magichour.ai/) One of the most frequently asked questions we receive is, "What should I input for my prompt?" Well, today's your lucky day! We just dropped a brand new article packed with invaluable advice on how to supercharge your prompts. Don't miss it! [Check it out](https://magichour.ai/blog/how-to-prompt-ai-videos-a-simple-guide) 💡 ## 6 New Art Styles for Video-to-Video Launching 6 new art styles for Video-to-Video, including characters like Sub-Zero from Mortal Kombat and Ghost from Call of Duty Share Video ## Create Video From Template in Explore Page In the Explore page, you can now click "Create Video Using Template" to start a project with the same settings! ## Launching Explore Page Introducing the Explore page, which highlights videos made using Magic Hour. Gain insight into the prompts and settings used so you can recreate styles that you like. In the future, we'll enable you to start projects based on these videos, and allow you to share your own creations. Share Video ## Higher File Size Upload Limit for Paid users We increased file size for paid users from **200mb** to **500mb**! ## Improved Video-to-Video Quality Hey everyone, we just rolled out a substantial update to Video-to-Video, resulting in a significant improvement in video quality. ## New feature alert: Text-to-Video 📢 We're excited to announce a new mode we've been working on called **Text-to-Video**. Think of it like Animation but with more realism and consistency. It comes with your favorite features from Animation like initial images, art styles, Use Lyrics as Prompt, and Have AI Choose Prompts. Get started at [https://magichour.ai/create/text-to-video](https://magichour.ai/create/text-to-video) today, currently available to Creator and Pro users only. ## Adjust FPS of Output in Video-to-Video In Video-to-Video mode, you can now adjust the FPS of your final video. The two options are full FPS (same FPS as original video) and half FPS (half the FPS of the original video). The default is half FPS, as there's usually not a significant difference between half and full FPS, but full FPS may be good for professional use cases or videos that you want to appear more "full." Note that full FPS costs twice the frames of half FPS. Half FPS ## Longer Videos for Face Swap and Animation We updated the maximum frames for Face Swap Video and Animation * Animation: 5,000 -> 10,000 * Face Swap: 2,000 -> 4,000 Now you can generate videos that are **2x** longer! 📈 ## Launching Notifications Center! When a video has finished rendering or ran into an error, you'll now get an in-app notification and email. You can see the in-app notifications by clicking the bell icon in your dashboard. To turn either notification off, simply go to the Settings tab in your dashboard and toggle them off. No more watching your generations and refreshing the page. You can now grab a coffee and get a ping as soon as they're done! Half FPS ## Launching 4 New Art Styles for Video-to-Video We just dropped 4 new art styles: 3D Render, Neon Dreams, Oil Painting, and Pixel. Below is a quick preview using the prompt "Flash the superhero." Check it out at [https://magichour.ai/create/video-to-video](https://magichour.ai/create/video-to-video) ## Use Lyrics in Animation You can now click the "Use Lyrics as Prompt" button to use the lyrics of your audio as the prompts. In addition, it splits your video into scenes to give it that montage look. Also, did you know if you click the "Have AI Choose Prompts" button, it feeds the lyrics into ChatGPT and asks it to direct a music video for you? This creates videos that tell interesting stories - try it out sometime and share your results! Use Prompt Button ## Launching Frame Packs You can now purchase Frame Packs at [https://magichour.ai/pricing](https://magichour.ai/pricing) if you run out of frames before your next top up. Frames come in increments of 1K for \$3. Note: you'll still get the same features as your plan, with the exception of watermarks, which will be removed if you purchase a Frame Pack. Enjoy! Frame Pack ## Launching Face Swap for All Users Face Swap is now available for all users! Try it out today [https://magichour.ai/create/face-swap](https://magichour.ai/create/face-swap) ## Launching Face Swap Create realistic face swaps (or deep fakes) today, available to Creator and Pro users. Try it out today [https://magichour.ai/create/face-swap](https://magichour.ai/create/face-swap) ## Improved Video-to-Video Consistency We heard from some of you that you wanted more change in your video-to-video renders. By default, we have frame-to-frame consistency on, but now you can turn it off in your renders if you'd like. Here's a before and after of what the feature does. Try it out today [https://magichour.ai/create/video-to-video](https://magichour.ai/create/video-to-video) ## Launching 4 Art Styles for Video-to-Video We're thrilled to announce the launch of four brand-new art style templates in Video-to-Video: Mech, Van Gogh, Watercolor, and Comic! 🎨🖌️ Frame Pack Try it out today [https://magichour.ai/create/video-to-video](https://magichour.ai/create/video-to-video) ## Upload Your Own Audio and Video We just launched the functionality to allow you to upload your own audio and video to use! Discover this feature in the first page of the Animated Music Video and Video-to-Video project flows. ## Launching Video-to-Video 📢 Video-to-Video allows you to take an existing video and apply any style. You can use it to: * Bring animations like Spirited Away to life with breathtaking realism * Witness a mesmerizing marble statue duel instead of a regular UFC fight * Turn your favorite meme into any character your heart desires Frame Pack Try it out today [https://magichour.ai/create/video-to-video](https://magichour.ai/create/video-to-video) ## Sample Audio For Animation To make it easier to create your next video, we added a few sample audio files 🎵 for Animation. Sample Audio ## New Dashboard UI Each video card now has a preview image that is clickable and takes you into the video page, and yes, T Swift was my inspiration. 🖼️🎵👀 Dashboard ## Launching Initial Image for Animation Now you can upload an initial image for your video 🔥 Initial Image ## Launching Magic Hour Publicly! We are officially live! We have been in private beta for days. Now when you log in, you are able to start creating animations! Initial Create Page ## Magic Hour Private Beta Now you can sign up for an account. You will be added to our waitlist page and we will slowly grant access from our list of users. Landing Page Sign up Today: [https://magichour.ai](https://magichour.ai) ## Magic Hour Landing Page Our landing page is live. Sign up with your email and we will let you know once the app is live! Landing Page Sign up Today: [https://magichour.ai](https://magichour.ai) # 2024 Source: https://docs.magichour.ai/changelog/2024 List of feature releases in 2024 ## File URL Support for Face Swap Video and Photo APIs. To make it easier to use input files, we now allow file URLs for `image_file_path` and `video_file_path` in the following APIs: * POST /v1/face-swap * POST /v1/face-swap-photo Simply pass the url in the `file_path` inputs: ```json theme={null} { "image_file_path": "https://cdn.yourwebsite.com/files/image.png", "video_file_path": "https://cdn.yourwebsite.com/files/video.mp4" } ``` ## Increased Video Download URL Expiration In the response of `GET /v1/video-projects/:id`, the expiration time is increased from **1 hour** to **24 hours**. The `GET /v1/image-projects/:id` was already returning 24 hours expiration, so this change make both of these APIs have matching behavior. ## Added `.downloads` key to `GET /v1/video-projects/:id` In the response of `GET /v1/video-projects/:id`, we added `.downloads` key to return a list of output urls. This make video and image project response have matching behavior so it's easier to work with both APIs. The `.download` key is still part of the response to ensure backwards compatibility. ```diff theme={null} { - "download": { - "url": "https://video.magichour.ai/id/output.mp4?auth-token=1234", - "expires_at": "2024-12-18T00:00:00.000Z" - }, + "downloads": [ + { + "url": "https://video.magichour.ai/id/output.mp4?auth-token=1234", + "expires_at": "2024-12-18T00:00:00.000Z" + } + ], } ``` ## Added Image Background Remover API Now you can remove background from your images via API. Get started with this new API using: * [Python SDK](https://github.com/magichourhq/magic-hour-python/tree/main/magic_hour/resources/v1/image_background_remover) * [Node SDK](https://github.com/magichourhq/magic-hour-node/tree/main/src/resources/v1/image-background-remover) * [Go SDK](https://github.com/magichourhq/magic-hour-go/tree/main/resources/v1/image_background_remover) * [Rust SDK](https://github.com/magichourhq/magic-hour-rust/tree/main/src/resources/v1/image_background_remover) * [Rest API](/api-reference/image-projects/image-background-remover) ## Launching Image Background Remover! Head over to [Image Background Remover](https://magichour.ai/create/image-background-remover) to create yours today! Dashboard View We also launched a free tool to the [Image Background Remover Product Page](https://magichour.ai/products/image-background-remover) so you can try out the new product for free! Dashboard View ## Specify Height & Width Must be at Least 64px in `POST /v1/video-to-video` This is an API validation update to ensure that `height` and `width` of the API call are both above 64. Originally, this was not validated at the API level, and the video will fail to render. Now, you will get a validation error for invalid height/width in the request body. For example, passing `height: 32` will return in the following 400 response: ```json theme={null} { "message": "Request validation failed", "pathParameterErrors": null, "headerErrors": null, "queryParameterErrors": null, "bodyErrors": { "issues": [ { "code": "too_small", "minimum": 64, "type": "number", "inclusive": true, "exact": false, "message": "Number must be greater than or equal to 64", "path": ["height"] } ], "name": "ZodError" } } ``` ## Add Animation to API Now you can create stop-motion animations via API. Get started with this new API using: * [Python SDK](https://github.com/magichourhq/magic-hour-python/tree/main/magic_hour/resources/v1/animation) * [Node SDK](https://github.com/magichourhq/magic-hour-node/tree/main/src/resources/v1/animation) * [Go SDK](https://github.com/magichourhq/magic-hour-go/tree/main/resources/v1/animation) * [Rust SDK](https://github.com/magichourhq/magic-hour-rust/tree/main/src/resources/v1/animation) * [Rest API](/api-reference/video-projects/animation) Fun Fact: Animation was the first mode we built on Magic Hour! ## Error Visibility on Errors Previously, whenever an error occurred on generation, there's no additional details on why. We now added an `error` key to the `GET /v1/image-projects/:id` and `GET /v1/video-projects/:id` APIs to give you deeper visibility into what happened. ```json theme={null} { "error": { "code": "", "message": "" } } ``` If you see `unknown_error` as the code, please reach out to our team at [support@magichour.ai](mailto:support@magichour.ai) to debug further. ## Delete Image/Video Project APIs After you downloaded the output, you now can delete the generated asset from our storage. This is a highly requested feature from our API users to improve privacy for the end user. Now you can call ``` DELETE /v1/image-projects/:id DELETE /v1/video-projects/:id ``` This change is also released to all SDKs. * [Python SDK](https://github.com/magichourhq/magic-hour-python/tree/main/magic_hour/resources/v1/video_projects) * [Node SDK](https://github.com/magichourhq/magic-hour-node/tree/main/src/resources/v1/video-projects) * [Go SDK](https://github.com/magichourhq/magic-hour-go/tree/main/resources/v1/video_projects) * [Rust SDK](https://github.com/magichourhq/magic-hour-rust/tree/main/src/resources/v1/video_projects) ## Improve Error Message When File Path is Invalid If the `audio_file_path`, `image_file_path`, or `video_file_path` points to a file that does not exist in our storage, we previously returns the error > Asset path api-assets/id/video.mp4 is invalid This error is not very actionable. The new error is now: > file path ' api-assets/id/video.mp4' does not exist. Please make sure that the value matches the 'items.\[].file\_path' from the /v1/files/upload-urls API and the files are uploaded by sending a PUT request to the upload url. ## Added `max_fps_limit` to `POST /v1/lip-sync` This was a feature requested by one of our API users, who wanted reduce frame usage while using APIs. Allowing to set a `max_fps_limit` means you limit the maximum of cost of the video. For example, if the original video has an FPS of 60 with a duration of 10 seconds, generation would previously cost **600** frames. Now, you can pass `"max_fps_limit": 24` and only be charged **240** frames. If the video fps is lower than the `max_fps_limit` set, we will deduct frames based on the actual FPS of the video You can see more details in the [API Reference](/api-reference/video-projects/lip-sync#body-max-fps-limit) ## Longer Videos for Face Swap and Lip Sync We updated the maximum frames for Face Swap Video and Lip Sync from 6,000 to 20,000 frames. Now you can generate videos that are **3.3x** longer! 📈 The table summaries the length of video you can generate for a few common video FPS: | FPS | Previous | New | | :-- | :------- | :------- | | 24 | minutes | minutes | | 30 | minutes | minutes | | 60 | minutes | minutes | ## See Your Email in Menu Many of us have multiple emails, and sometimes it is hard to know what email you used. So we added your email to the profile menu. User Menu ## Select Events for Webhook We added the ability to select specific events to send to your webhook event. User Menu You can create your webhook by going to the [Developer Hub](https://magichour.ai/developer) ## Make `name` Actually Optional in APIs Currently, the `name` field in APIs are `nullable`, when it should be `optional`. Before this change, even if you didn't want to add a name for a particular render, you still need to pass in the key with a `null` value. ```json theme={null} { "name": null } ``` After this change, you can leave off `name` completely. ## New AI Image APIs Added the following AI image tools to our API: * [POST /v1/ai-headshot-generator](/api-reference/image-projects/ai-headshot-generator) * [POST /v1/ai-image-generator](/api-reference/image-projects/ai-image-generator) * [POST /v1/ai-image-upscaler](/api-reference/image-projects/ai-image-upscaler) * [POST /v1/ai-qr-code-generator](/api-reference/image-projects/ai-qr-code-generator) All of our SDKs are updated with the new endpoints * [Python SDK](https://github.com/magichourhq/magic-hour-python) * [Node SDK](https://github.com/magichourhq/magic-hour-node) * [Go SDK](https://github.com/magichourhq/magic-hour-go) * [Rust SDK](https://github.com/magichourhq/magic-hour-rust) ## First AI Image APIs and Webhook Events We have added our first set of APIs for image modes: * [GET /v1/image-projects/:id](/api-reference/image-projects/get-image-details) * [POST /v1/face-swap-photo](/api-reference/image-projects/face-swap-photo) Also introduced 3 additional webhook events * `image.started` * `image.completed` * `image.errored` ## Documentation Improvements * We reorganized our API docs to combine all video and image modes under one tag. * [Webhook Documentation](/webhook-reference) is split into its own documentation. ## Added `high_quality` Param to Image-to-Video API Added `High Quality` mode to [Image-to-Video](https://magichour.ai/create/image-to-video). "High Quality" is better at detail preservation, ideal for humans and complex images. To use high quality mode, simply check `High Quality` checkbox. ## Added Support for `.webm` Video Now you can use `.webm` files in our web app without having to convert to a supported format. ## Added Support for `.gif` For Face Swap Ever saw a funny gif and wanted to put yourself in the scene? Now you can! We added support to allow `.gif` files for face swap mode, both in the web UI and API ## Delete Generated Images on Account Deletion When you request to delete your account, we remove all personal data, delete your information from Stripe, and erase all generated videos. We discovered that generated images were not included in this process. This has now been fixed—going forward, and we have reviewed and deleted any remaining images for previously deleted accounts. ## Allow Custom Prompt for AI Image Upscaler Now you can have more control over the upscaled image. We enabled custom prompt for "Creative" style. User Menu ## AI Headshot Generator We added the `AI Headshot Generator` to our dashboard! Try it now: [AI Headshot Generator](https://magichour.ai/create/ai-headshot-generator) ## Parallel Image Rendering For our image modes, you used to have to wait for the previous render to complete first before queueing your next image. We have now enabled parallel rendering for * [AI Image Generator](https://magichour.ai/create/ai-image-generator) * [AI QR Code](https://magichour.ai/create/ai-qr-code-generator) * [Face Swap Photo](https://magichour.ai/create/face-swap-photo) ## Launching AI Image Upscaler We shipped a new too: `AI Image Upscaler`! Try it now: [AI Image Upscaler](https://magichour.ai/create/ai-headshot-generator) ## Allow Any Text in QR Code Generator QR Code can represent any text, and we removed the `URL` limitation to our QR code generator. You can now use any text! Try it now: [AI QR Code Upscaler](https://magichour.ai/create/ai-qr-code-generator) ## Launching Face Swap Photo in Dashboard We added Face Swap Photo to our dashboard experience. Try it now: [Face Swap Photo](https://magichour.ai/create/face-swap-photo) ## Launching AI QR Code Generator in Dashboard We added AI QR Code Generator to our dashboard experience. Try it now: [AI QR Code Generator](https://magichour.ai/create/face-swap-photo) ## Webhook Notification You can now configure a webhook endpoint to be notified of status changes. We support `video.started`, `video.completed`, and `video.errored` events. This is a more efficient way to check whether a video ahs completed as compared to polling `GET /v1/video-projects/:id`. Get more details in our [webhook overview](/integration/webhook/overview) ## Launching 4 Additional Video-to-Video Art Styles User Menu Try them now: [Video-to-Video](https://magichour.ai/create/video-to-video) ## Launching Free Lip Sync Tool Now you can try out our lip sync tool even without creating an account. Lip Sync Free Tool Try it now: [Free Lip Sync](https://magichour.ai/products/lip-sync) ## Multi-Face Swap we just launched a highly requested feature: Multi-Face Swap! Now, you can swap individual faces in a video with anyone you choose. Just head to [https://magichour.ai/create/face-swap](https://magichour.ai/create/face-swap), upload your video, and select "Individual Faces" to get started Multi-Face Swap UI ## New AI Image Generator We launched a new AI image generator - it's now higher resolution, faster, more photorealistic, and has better prompt adherence. You can try it at [https://magichour.ai/create/ai-image-generator](https://magichour.ai/create/ai-image-generator), available to all users. Multi-Face Swap UI ## Launching 10 New Video-to-Video Art Styles Video to Video Art Styles Try them now: [Video-to-Video](https://magichour.ai/create/video-to-video) ## Video-to-Video V2! We launched a new version of Video-to-Video called **V2**. It has a stylistic quality that is more consistent and less "noisy" than V1. Some styles that work well with V2 are "The Void," "Android," and "Street Fighter," try them out yourself! Try them now: [Video-to-Video](https://magichour.ai/create/video-to-video) ## Added Text-to-Video API Text-to-Video is now available via API! Get started with this new API using: * [Python SDK](https://github.com/magichourhq/magic-hour-python/tree/main/magic_hour/resources/v1/text_to_video) * [Node SDK](https://github.com/magichourhq/magic-hour-node/tree/main/src/resources/v1/text-to-video) * [Go SDK](https://github.com/magichourhq/magic-hour-go/tree/main/resources/v1/text_to_video) * [Rust SDK](https://github.com/magichourhq/magic-hour-rust/tree/main/src/resources/v1/text_to_video) * [Rest API](/api-reference/video-projects/text-to-video) ## Added Image-to-Video API Image-to-Video is now available via API! Get started with this new API using: * [Python SDK](https://github.com/magichourhq/magic-hour-python/tree/main/magic_hour/resources/v1/image_to_video) * [Node SDK](https://github.com/magichourhq/magic-hour-node/tree/main/src/resources/v1/image-to-video) * [Go SDK](https://github.com/magichourhq/magic-hour-go/tree/main/resources/v1/image_to_video) * [Rust SDK](https://github.com/magichourhq/magic-hour-rust/tree/main/src/resources/v1/image_to_video) * [Rest API](/api-reference/video-projects/image-to-video) ## Improved Dashboard Now that we support image generation, we now allow you toggle between videos and images in your dashboard. New Dashboard ## Launching Image-to-Video Mode We launched a new image-to-video mode. It's higher quality, can generate **one minute** videos, and you can enter a prompt in addition to the image. Some of our favorite use cases are animating paintings, historic photos, and memes. You can try it out at [https://magichour.ai/create/image-to-video](https://magichour.ai/create/image-to-video) ## Launching Text-to-Video Mode We launched a new text-to-video mode. It's has high realism and consistency, and you can generate up to 60 second videos. You can try it out at [https://magichour.ai/create/text-to-video](https://magichour.ai/create/text-to-video) ## Added Video-to-Video API Video-to-Video is now available via API! Get started with this new API using: * [Python SDK](https://github.com/magichourhq/magic-hour-python/tree/main/magic_hour/resources/v1/video_to_video) * [Node SDK](https://github.com/magichourhq/magic-hour-node/tree/main/src/resources/v1/video-to-video) * [Go SDK](https://github.com/magichourhq/magic-hour-go/tree/main/resources/v1/video_to_video) * [Rust SDK](https://github.com/magichourhq/magic-hour-rust/tree/main/src/resources/v1/video_to_video) * [Rest API](/api-reference/video-projects/video-to-video) ## Added Lip Sync API Lip Sync is now available via API! Get started with this new API using: * [Python SDK](https://github.com/magichourhq/magic-hour-python/tree/main/magic_hour/resources/v1/lip_sync) * [Node SDK](https://github.com/magichourhq/magic-hour-node/tree/main/src/resources/v1/lip-sync) * [Go SDK](https://github.com/magichourhq/magic-hour-go/tree/main/resources/v1/lip_sync) * [Rust SDK](https://github.com/magichourhq/magic-hour-rust/tree/main/src/resources/v1/lip_sync) * [Rest API](/api-reference/video-projects/lip-sync) ## AI Headshot Now 3x Faster We made a major speed improvement that reduced AI Headshot generation time > \~90s -> \~30s. 3x faster! 🚀 ## Launching AI Headshot Generator we launched a new free tool today: AI headshot generator. It takes one selfie and turns it into a professional photo. AI Headshot Generator Try it out at [https://magichour.ai/products/ai-headshot-generator](https://magichour.ai/products/ai-headshot-generator) ## Launching Free AI Image Generator we launched a free AI image generator. Generate any image using text! AI Image Generator Try it out at [https://magichour.ai/products/ai-image-generator](https://magichour.ai/products/ai-image-generator) ## Launching Free Face Swap Video we launched a free version of our face swap video tool. Face Swap Video Try it out at [https://magichour.ai/products/face-swap?mode=video](https://magichour.ai/products/face-swap?mode=video) ## API Now Available to All Users We want everyone to be able to try out our API, so removed any limitation based on subscription tier. Now you can create an API key and call the API! Now, you no longer need an active `Pro` or `Business` subscription to access the API. ## New Sign In/Up Pages We rebuild the Sign In and Sign Up Pages for a more optimize flow. ## Launching Free Face Swap Photo Tool You can now swap faces into photos completely free, sign in not required (but we would love it if you do) Face Swap Photo Try it now: [https://magichour.ai/products/face-swap?mode=photo](https://magichour.ai/products/face-swap?mode=photo) ## API Now Available to Pro Users We opened up our API to more users. Anyone with an active `Pro` or `Business` subscription to access the API. ## Launching the Magic Hour API Magic Hour officially has an API offering! It is currently available to users with an active `Business` subscription. But we plan to open up the API to more users. As part of the initial launch, you can upload assets to our storage and create face swap videos. ```sh theme={null} curl --request POST \ --url https://api.magichour.ai/v1/face-swap \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "name": "Face Swap video", "height": 960, "width": 512, "start_seconds": 0, "end_seconds": 15, "assets": { "image_file_path": "image/id/1234.png", "video_source": "file", "video_file_path": "api-assets/id/1234.mp4" } }' ``` The following APIs are available as part of the initial launch * [POST /v1/face-swap](/api-reference/video-projects/face-swap-video) * [GET /v1/video-projects/:id](/api-reference/video-projects/get-video-details) * [POST /v1/files/upload-urls](/api-reference/files/generate-asset-upload-urls) Get started by creating an [API key](https://magichour.ai/developer) ## Search Templates Did you find a template you like and want to find more templates like it? Now you can with our new template search! Template Search ## 10 New Styles for Video-to-Video We just added 10 more art styles you can use for video-to-video * Ink * Underwater * On Fire * Tarot Card * Power Armor * Unholy * Thick Impasto * Impressionism * Film * Lego ## Explore Templates Page We just replaced our old "Feed" page with a new templates page with thousands of editable, high quality templates that make creating engaging videos easy. ## Text to Speech for Lip Sync Videos You can now generate audio for a list of celebrity voices to use for your lip sync videos! Text to Speech Try it out now: [https://magichour.ai/create/lip-sync](https://magichour.ai/create/lip-sync) ## Introducing Business Subscription We launched a new pricing tier, benefits includes: * 250,000 frames per month. * 4K resolution for Face Swap and Lip Sync. * 3GB file uploads * Direct line to the CEO We are discounting this plan by 40% to start! Check it out now [https://magichour.ai/pricing](https://magichour.ai/pricing) ## Delete Account We understand if you just want to try us out and delete your account for privacy reasons. So we now allow you to delete your account. To delete, visit [https://magichour.ai/settings](https://magichour.ai/settings) and click `Delete Account`. ## Launching Lip Sync We launched Lip Sync, a new mode that lets you match the lips in a video to any audio. Try it out at [https://magichour.ai/create/lip-sync](https://magichour.ai/create/lip-sync) ## Submit Your Video as Template For certain modes, you now can submit your output as a template. Template Submission ## Daily Reward Every day, you can now claim 100 free frames! That's 3000 frames per month! Daily Reward Visit [https://magichour.ai/create](https://magichour.ai/create) to claim yours today! ## New Landing Page We overhauled our landing page designs. Check it out and let us know what you think! [https://magichour.ai/](https://magichour.ai/) ## Launching Templates We just launched Templates. Templates are the easiest way to make high quality videos. No more fumbling over prompts, just choose a template, customize it, and share it. You can find Templates at [https://magichour.ai/create](https://magichour.ai/create). Templates are only available for Video-to-Video, but we're working on adding more templates. ## 6 New Styles for Video-to-Video We just added 6 more art styles you can use for video-to-video * Yoji Shinkawa * Clay * Marble Sculpture * Street Fighter * Cyberpunk Illustration * Ki Charge ## Integrated Realistic Model for Video-to-Video Now you can pick between `Dreamshaper` and `Realistic` AI models to use when generating your video. ## Double Creator Subscription Frames We updated creator plan monthly frames from **5,000** to **10,000**. So now you can make twice as many videos! ## New Notification Center We launched a new in-app notification center, so you can see all of your video progress in one place. ## Longer Videos for Face Swap We updated the maximum frames for Face Swap Video from 4,000 to 6,000 frames. Now you can generate videos that are **1.5x** longer! The table summaries the length of video you can generate for a few common video FPS: | FPS | Previous | New | | :-- | :------- | :------- | | 24 | minutes | minutes | | 30 | minutes | minutes | | 60 | minutes | minutes | ## Improving Subscription Benefits We've just made some major improvements to our subscription plans. **Creator** * 960x960 to 1024x1024 max resolution * 500 MB uploads to 1 GB uploads **Pro** * 1344x1344 to 1476x1476 max resolution * 1 GB uploads to 2 GB uploads Plus, we made some behind the scene changes that starts your video upload as soon as you drag it in, so you won't have to wait as long after clicking render. ## Upgrading Video-to-Video Quality Exciting news: we've upgraded Video-to-Video, enhancing quality across all tiers, especially for Pro users. The changes, subtle yet significant, are ideal for discerning and professional users. Check out our before-and-after video to see the improvements: ## Launching Image-to-Video Quality We just launched a new mode called Image-to-Video. Simply upload an image and generate a 4 second video bringing it to life. Note it's available to Creator and Pro users only. ## Improved Face Swap with High Quality Swaps We drastically improved the output quality of faces in Face Swap # 2025 Source: https://docs.magichour.ai/changelog/2025 Latest feature enhancements in Magic Hour ## Z-Image-Turbo Model for AI Image Generator We added Z-Image-Turbo as a new model option for AI Image Generator, providing faster and higher-quality image generation. Z-Image-Turbo Model ## Saved Faces for Face Swap You can now save frequently used faces for Face Swap, making it easier to swap faces with your go-to images. Try it out now: [https://magichour.ai/create/face-swap](https://magichour.ai/create/face-swap) by uploading a face to use and save it for future use. ## New Image Editor Models: Nano Banana Pro and Seedream 4.5 We added support for Nano Banana Pro and Seedream 4.5 models, giving you even more options for high-quality image editing. New Image Editor Models: Nano Banana Pro and Seedream
4.5 Try it out now: [https://magichour.ai/create/ai-image-editor](https://magichour.ai/create/ai-image-editor) ## Improved Upload Experience We make a significant improvement to the upload experience, including upload speed and estimated time remaining (ETA). You'll now see real-time progress updates. Improved Upload Experience Upload Retry Experience Plus, we added a lot of behind the scene changes to make the upload process faster and more reliable, especially for spotty internet connections. We now * retry the upload on any network error (up to 40 times) * resume the upload from the last uploaded chunk so you don't have to start over if the upload is interrupted ## 4x Upscale Available to All Users We made 4x upscale available to all users. Previously, this feature was only available to Pro+ users. Try it out now: [https://magichour.ai/create/ai-image-upscaler](https://magichour.ai/create/ai-image-upscaler) ## Increased Prompt Character Limit for AI Image Editor to 15k We increased the prompt character limit for AI Image Editor from the 2,000 characters to 15,000 characters, allowing you to provide more detailed editing instructions. ## Allow Selecting Multiple Models in AI Image Editor You can now select multiple AI models in the AI Image Editor to compare results and choose the best output for your needs. Allow Selecting Multiple Models ## Veo 3.1 Extend - Up to 56 Seconds We added Veo 3.1 Extend support for Text-to-Video and Image-to-Video, allowing you to generate videos up to 56 seconds long with extended capabilities. Try it out now: [https://magichour.ai/create/text-to-video](https://magichour.ai/create/text-to-video) and [https://magichour.ai/create/image-to-video](https://magichour.ai/create/image-to-video) ## Introducing Sora 2 in Text-to-Video and Image-to-Video We added support for Sora 2 in Text-to-Video and Image-to-Video, giving you access to the latest video generation model for even better results. We allow you to generate videos up to 60 seconds! Sora 2 in Text-to-Video and
Image-to-Video ## Mobile Navigation Bar on /create Page We added a mobile navigation bar to the /create page for better navigation on mobile devices. Mobile Navigation Bar on /create Page ## Show Upload Progress on Free Tools We added a progress indicator to show upload progress on all free tools. This gives you better visibility into the upload process. Show Upload Progress on Free
Tools ## Increased Lip Sync Frame Limit from 20k to 120k We increased the frame limit for Lip Sync from 20k to 120k frames, allowing you to generate longer videos. For reference, for a 30 FPS video: | FPS | Previous | New | | :-- | :------- | :------- | | 30 | minutes | minutes | ## Face Swap Video Frame Limit Increased to 100k We increased the frame limit for Face Swap Video from 20k to 100k frames. You can now generate even longer videos. For reference, for a 30 FPS video: | FPS | Previous | New | | :-- | :------- | :------- | | 30 | minutes | minutes | ## Single-Step Face Swap Video We simplified the Face Swap Video creation process in /create/face-swap-video to require just one step instead of multiple steps. Single-Step Face Swap Video Try it out now: [https://magichour.ai/create/face-swap-video](https://magichour.ai/create/face-swap-video) ## New Mobile /create Page UI We redesigned the /create page UI for mobile users to be more user-friendly and intuitive. New Mobile /create Page UI Try it out now: [https://magichour.ai/create](https://magichour.ai/create) ## New AI Selfie Product Page We redesigned the AI Selfie Product Page to be more user-friendly and intuitive. Try it out now: [https://magichour.ai/products/ai-selfie-generator](https://magichour.ai/products/ai-selfie-generator) ## Save Image-to-Video and Text-to-Video Model as Default You can now save your preferred AI model as the default for Image-to-Video and Text-to-Video, making it easier to generate videos with your favorite model. Save Image-to-Video and Text-to-Video Model as
Default ## New Face Swap GIF Product Page We added free tool to allow you to face swaps gifs. Face Swap GIF Free Tool Try it out now: [https://magichour.ai/products/face-swap/gif](https://magichour.ai/products/face-swap/gif) ## New Models for Image-to-Video and Text-to-Video, plus audio support We added support for the following models in Image-to-Video and Text-to-Video: * Kling 2.5 Audio * Veo 3.1 * Veo 3.1 Audio Try it out now: [https://magichour.ai/create/image-to-video](https://magichour.ai/create/image-to-video) and [https://magichour.ai/create/text-to-video](https://magichour.ai/create/text-to-video) ## Updated Video-to-Video UI We updated the Video-to-Video UI to align with the rest of the product pages, providing a more consistent user experience. Video-to-Video UI Try it out now: [https://magichour.ai/create/video-to-video](https://magichour.ai/create/video-to-video) ## Image Editor Sub-tools We added sub-tools to the Image Editor to better demonstrate the capabilities of the tool. Image Editor Sub-tools Try it out now: [https://magichour.ai/products/ai-image-editor](https://magichour.ai/products/ai-image-editor) ## Image-to-Image Free Tool We launched a free tool for Image-to-Image. Try it out without signing up! Image-to-Image Free Tool Try it out now: [https://magichour.ai/products/image-to-image](https://magichour.ai/products/image-to-image) ## New AI Headshot Generator Product Page We redesigned the AI Headshot Generator Product Page to be more user-friendly and intuitive. Try it out now: [https://magichour.ai/products/ai-headshot-generator](https://magichour.ai/products/ai-headshot-generator) ## Video-to-Video Free Tool We launched a free tool for Video-to-Video. Now you can get a better sense of what you can create with Video-to-Video. Video-to-Video Free Tool Try it out now: [https://magichour.ai/products/video-to-video](https://magichour.ai/products/video-to-video) ## New UI for AI Image Generator Product Page We redesigned the AI Image Generator Product Page to be more user-friendly and intuitive. Try it out now: [https://magichour.ai/products/ai-image-generator](https://magichour.ai/products/ai-image-generator) ## New UI for AI Image Editor Product Page We redesigned the AI Image Editor Product Page to be more user-friendly and intuitive. Try it out now: [https://magichour.ai/products/ai-image-editor](https://magichour.ai/products/ai-image-editor) ## New UI for AI GIF Generator Product Page We redesigned the AI GIF Generator Product Page to be more user-friendly and intuitive. Try it out now: [https://magichour.ai/products/ai-gif-generator](https://magichour.ai/products/ai-gif-generator) ## Allow Video for Audio Inputs in Lip Sync and AI Talking Photo You can now use video files as audio inputs for Lip Sync and AI Talking Photo. We will handle the audio extraction for you. ## New UI for AI Talking Photo Product Page We redesigned the AI Talking Photo Product Page to be more user-friendly and intuitive. New UI for AI Talking Photo Product
Page Try it out now: [https://magichour.ai/products/ai-talking-photo](https://magichour.ai/products/ai-talking-photo) ## New UI for AI Image Upscaler We redesigned the AI Image Upscaler UI to be more user-friendly and intuitive. New UI for AI Image Upscaler Try it out now: [https://magichour.ai/create/ai-image-upscaler](https://magichour.ai/create/ai-image-upscaler) ## New UI for Image Background Remover We redesigned the Image Background Remover UI to be more user-friendly and intuitive. New UI for Image Background
Remover Try it out now: [https://magichour.ai/create/image-background-remover](https://magichour.ai/create/image-background-remover) ## New UI for Lip Sync Product Page We redesigned the Lip Sync Product Page to be more user-friendly and intuitive. New UI for Lip Sync Product Page Try it out now: [https://magichour.ai/products/lip-sync](https://magichour.ai/products/lip-sync) ## Product Switcher to allow easy navigation between products We added a new product switcher to the header so you can easily navigate between products. Product Switcher ## New UI for AI Headshot Generator We redesigned the AI Headshot Generator UI to be more user-friendly and intuitive. New UI for AI Headshot Generator Try it out now: [https://magichour.ai/create/ai-headshot-generator](https://magichour.ai/create/ai-headshot-generator) ## New UI for Face Swap Photo We redesigned the Face Swap Photo UI to be more user-friendly and intuitive. New UI for Face Swap Photo ## Sign in using 6 digit code We added a new way to sign in to your account using a 6 digit code sent to your email. You can still sign in using the magic link. This way, you don't have to use the same device to sign in. Sign in using 6 digit code ## New UI for Auto Subtitle Generator We redesigned the Auto Subtitle Generator UI to be more user-friendly and intuitive. New UI for Auto Subtitle
Generator Try it out now: [https://magichour.ai/create/auto-subtitle-generator](https://magichour.ai/create/auto-subtitle-generator) ## Brand new UI for Face Swap Product Page We completely redesigned the Face Swap Product Page to be more user-friendly and intuitive. Brand new UI for Face Swap Product
Page Try it out now: [https://magichour.ai/products/face-swap](https://magichour.ai/products/face-swap) ## New UI for Dashboard AI Clothes Changer New UI for AI Clothes Changer in dashboard. New UI for AI Clothes Changer in
dashboard Try it out now: [https://magichour.ai/products/ai-clothes-changer](https://magichour.ai/products/ai-clothes-changer) ## Add voice recording AI Voice Changer We added a new voice recording feature to AI Voice Changer. You can now record your voice and use it as the basis for your voice change. Voice Recording AI Voice
Changer Try it out now: [https://magichour.ai/create/ai-voice-changer](https://magichour.ai/create/ai-voice-changer) ## Allow `webm` for audio files We added support for `webm` audio files in Lip Sync and AI Talking Photo. ## Added generation sidebar to more modes Our new generation sidebar is now available for AI Talking Photo, Image to Video, Text to Video, Face Swap, Lip Sync, and AI Voice Changer. The sidebar allows you to better keep track of your generations while you're working on your next idea. Try it out now: * [https://magichour.ai/create/ai-talking-photo](https://magichour.ai/create/ai-talking-photo) * [https://magichour.ai/create/image-to-video](https://magichour.ai/create/image-to-video) * [https://magichour.ai/create/text-to-video](https://magichour.ai/create/text-to-video) * [https://magichour.ai/create/face-swap](https://magichour.ai/create/face-swap) * [https://magichour.ai/create/lip-sync](https://magichour.ai/create/lip-sync) * [https://magichour.ai/create/ai-voice-changer](https://magichour.ai/create/ai-voice-changer) ## New UI for Image-to-Video We redesigned the Image-to-Video UI to be more user-friendly and intuitive. New Image-to-Video UI Try it out now: [https://magichour.ai/create/image-to-video](https://magichour.ai/create/image-to-video) ## Video-to-Video Now Accessible to All Users We made Video-to-Video accessible to all users. We want everyone to be able to try it out and see the quality of the videos we can generate. We also make Video-to-Video preview image generation temporarily free of charge. Video-to-Video Preview Image Generation Temporarily Free of
Charge Try it out now: [https://magichour.ai/create/video-to-video](https://magichour.ai/create/video-to-video) ## Faster and more variety for AI Image Editor suggestions We made two big improvements to AI Image Editor suggestions: * Suggestions are now 50% faster * On suggestion refresh, we now show more variety of suggestions. ## AI Voice Changer Free Tool and Dashboard Tool We launched a new free tool to change the voice of an audio file. Just upload an audio file and we'll change the voice for you. AI Voice Changer Free Tool Try it out now: [https://magichour.ai/products/ai-voice-changer](https://magichour.ai/products/ai-voice-changer) and [https://magichour.ai/create/ai-voice-changer](https://magichour.ai/create/ai-voice-changer) ## New UI for Text-to-Video We redesigned the Text-to-Video UI to be more user-friendly and intuitive. New Text-to-Video UI Try it out now: [https://magichour.ai/create/text-to-video](https://magichour.ai/create/text-to-video) ## Adaptive Suggestions for AI Image Editor We added suggestions for AI Image Editor based on the image you're editing. Adaptive Suggestions for AI Image
Editor Try it out now: [https://magichour.ai/create/ai-image-editor](https://magichour.ai/create/ai-image-editor) ## Added payment section to My Plan page We added a new payment section to My Plan page. You can now see your list of payment methods. Payment Section to My Plan page Try it out now: [https://magichour.ai/my-plan](https://magichour.ai/my-plan) ## HEIC support for AI Image Editor We added support for HEIC images in AI Image Editor. Try it out now: [https://magichour.ai/create/ai-image-editor](https://magichour.ai/create/ai-image-editor) ## 4k Resolution Images for AI Image Generator You can now generate 4k resolution images. 4k Resolution Images for AI Image
Generator Try it out now: [https://magichour.ai/create/ai-image-generator](https://magichour.ai/create/ai-image-generator) Note: The 4k resolution is only available for users with an business subscription. ## Multi-Image Edit Mode for AI Image Editor We added a new multi-image edit mode to AI Image Editor. You can now include up to 10 images in your edit. Multi-Image Edit Mode for AI Image
Editor Try it out now: [https://magichour.ai/create/ai-image-editor](https://magichour.ai/create/ai-image-editor) ## All subscribers now have access to 720p and 1080p resolution for Image to Video and Text to Video We no longer require Pro+ subscription to generate 720p and 1080p videos for Image to Video and Text to Video. All subscribers can now enjoy higher resolution videos. 720p and 1080p resolution for Image to Video and Text to
Video Try it out now: [https://magichour.ai/create/image-to-video](https://magichour.ai/create/image-to-video) or [https://magichour.ai/create/text-to-video](https://magichour.ai/create/text-to-video) ## New UI for AI Image Generator We redesigned the AI Image Generator UI to be more user-friendly and intuitive. We added a new sidebar so you can better keep track of your generations while you're working on your next idea. New AI Image Generator UI Try it out now: [https://magichour.ai/create/ai-image-generator](https://magichour.ai/create/ai-image-generator) ## Voice Cloner Free Tool We launched a new free tool to clone voices. Just upload a voice and we'll clone it for you. Voice Cloner Free Tool Try it free now: [https://magichour.ai/products/ai-voice-cloner](https://magichour.ai/products/ai-voice-cloner) ## Increased character limit to 1000 for all users We updated the character limit for text to speech to 1000 for all users. You can now generate longer audio files for Lip Sync and AI Talking Photo. Try it out now: [https://magichour.ai/create/lip-sync](https://magichour.ai/create/lip-sync) or [https://magichour.ai/create/ai-talking-photo](https://magichour.ai/create/ai-talking-photo) ## New UI for AI Gif Generator We redesigned the AI Gif Generator UI to be more user-friendly and intuitive. We added a new sidebar so you can better keep track of your generations while you're working on your next idea. New AI Gif Generator UI Try it out now: [https://magichour.ai/create/ai-gif-generator](https://magichour.ai/create/ai-gif-generator) ## Improved precision for start and end seconds We updated all the inputs for start and end seconds to allow 2 decimal places. This is a quality of life improvement to make it easier to specify the exact time you want to start and end the video. ## Added `Pro` mode to AI Image Generator You can now use the `Pro` mode to generate images with higher quality and more natural results. See the difference: AI Image Generator Pro Mode Try it out now: [https://magichour.ai/create/ai-image-generator](https://magichour.ai/create/ai-image-generator) and select the `Pro` mode to get started. **Note**: The `Pro` mode is only available for users with an existing subscription. ## New Lip Sync UI We redesigned the Lip Sync UI to be more user-friendly and intuitive. New Lip Sync UI Try it out now: [https://magichour.ai/create/lip-sync](https://magichour.ai/create/lip-sync) ## New /create page UI for desktop users We redesigned the /create page UI for desktop users to be more user-friendly and intuitive. New /create page UI for desktop users Try it out now: [https://magichour.ai/create](https://magichour.ai/create) ## Add generation mode to Lip Sync API We recently introduced standard and pro mode to Lip Sync, and now we've added a generation mode param to the API and SDKs. ## Added audio project APIs We recently added support for audio generations, and now we've added the following APIs for audio projects. * `POST /v1/ai-voice-generator` * `GET /v1/audio-projects/:id` * `DELETE /v1/audio-projects/:id` ## New AI Talking Photo UI We redesigned the Talking Photo UI to be more user-friendly and intuitive. New Talking Photo UI Try it out now: [https://magichour.ai/create/ai-talking-photo](https://magichour.ai/create/ai-talking-photo) ## New Face Swap Video UI We redesigned the Face Swap Video UI to be more user-friendly and intuitive. New Face Swap Video UI Try it out now: [https://magichour.ai/create/face-swap-video](https://magichour.ai/create/face-swap-video) ## Added `Standard` and `Pro` mode to Lip Sync We added two new modes to Lip Sync: `Standard` and `Pro`. The existing Lip Sync mode is renamed to `Lite` See the video below for the difference: Try it out now: [https://magichour.ai/create/lip-sync](https://magichour.ai/create/lip-sync) and select the `Standard` or `Pro` mode to get started. **Note**: The `Standard` and `Pro` modes are only available for users with an existing subscription. ## Speed up Lip Sync Videos We sped up the generation time for Lip Sync by 50-60%! This change applies to all Lip Sync users. Give it a try now: [https://magichour.ai/create/lip-sync](https://magichour.ai/create/lip-sync) ## Simplified the Video-to-video API by making multiple params optional. We updated the following params in the Video-to-video API to be optional: * `version` - default to `default` * `prompt_type` - default to `default` * `prompt` - now it is no longer necessary to specify `prompt: null` if you don't want to have any prompt. * `model` - default to `default` ## AI Voice Generator Dashboard Tool The AI Voice Generator is now available in the /create page. AI Voice Generator Dashboard
Tool Try it out now: [https://magichour.ai/create/ai-voice-generator](https://magichour.ai/create/ai-voice-generator). This feature is available to all users! ## AI Voice Generator Free tool. Now available! We launched a free tool to generate voice from text. Just upload a text file and we'll generate a voice for you. AI Voice Generator Free tool Try it out now: [https://magichour.ai/products/ai-voice-generator](https://magichour.ai/products/ai-voice-generator) **Note**: The free tool is limited to 200 characters per generation. ## Added output format to the AI Gif Generator API We added a new `output_format` param to the AI Gif Generator API. You can now specify the output format of the generated file. Valid values are `gif`, `webm`, and `mp4`. ```python theme={null} res = client.v1.ai_gif_generator.generate( ... output_format="mp4" ) ``` Try it up by updating to latest version of the SDK ## Increased gif output resolution GIF outputs were previously limited to 512x512. They now correctly support resolutions up to 1024x1024. ## AI Talking Photo Pro Mode We just launched Pro Mode for Talking Photo — with better lip-sync accuracy, higher-quality visuals, and more natural results. See and hear the difference: Try it out now: [https://magichour.ai/create/ai-talking-photo](https://magichour.ai/create/ai-talking-photo) ## Brand new UI for AI Image Editor We redesigned the UI for AI Image Editor to make it more user-friendly and intuitive. Brand new UI for AI Image
Editor Try it out now: [https://magichour.ai/create/ai-image-editor](https://magichour.ai/create/ai-image-editor) ## Version Selector for Face Swap Photo Since we introduced a new version of Face Swap Photo, we added a version selector to the create page so you can switch between the old and new versions. Version Selector for Face Swap
Photo Try it out now: [https://magichour.ai/create/face-swap-photo](https://magichour.ai/create/face-swap-photo) ## Version Selector for Face Swap Video Since we introduced a new version of Face Swap Video, we added a version selector to the create page so you can switch between the old and new versions. Version Selector for Face Swap
Video Try it out now: [https://magichour.ai/create/face-swap-video](https://magichour.ai/create/face-swap-video) ## New and improved Node/Typescript SDK We have updated the Node/Typescript SDK to include a `.generate` function for every client. This function will handle the uploading and downloading of files for you. ```typescript theme={null} const client = new Client((token = "YOUR_API_KEY")); const res = await client.v1.imageToVideo.generate( { assets: { imageFilePath: "/path/to/1234.png" }, endSeconds: 5.0, name: "Image To Video video", resolution: "720p", }, { waitForCompletion: true, downloadOutputs: true, downloadDirectory: ".", } ); ``` Checkout the release notes to learn more: [https://github.com/magichourhq/magic-hour-node/releases/tag/v0.37.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.37.0) ## New and improved Python SDK Two pain points for our SDKs are * uploading files * checking for status / downloading output. We have now updated the Python SDK to include a `.generate` function for every client. This function will handle the uploading and downloading of files for you. ```python theme={null} from magic_hour import Client client = Client(token="YOUR_API_KEY") response = client.v1.image_to_video.generate( assets={"image_file_path": "path/to/image.png"}, end_seconds=5.0, resolution="720p", wait_for_completion=True, download_outputs=True, download_directory="", ) ``` Checkout the release notes to learn more: [https://github.com/magichourhq/magic-hour-python/releases/tag/v0.36.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.36.0) ## New Face Swap Version We deployed a new version of face that is faster, sharper, and has better identify preservation. All web users has automatically been upgraded to the new version. If you're using the API, you can fallback to the old version by specifying `.style.version = "v1"`. Please upgrade your sdk version to `v0.36.0` to take advantage of the this change. * [Python SDK v0.36.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.36.0) * [Node SDK v0.36.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.36.0) * [Go SDK v0.36.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.36.0) * [Rust SDK v0.36.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.36.0) ## Fixing generation error for long animation videos We fixed a generation error for long animation videos. This error was caused by the server running out of disk space. We have since fixed the issue! ## Brand new headshot form UI We rebuilt the headshot form UI to be more user-friendly and intuitive. Brand new headshot form UI Try it out now: [https://magichour.ai/create/ai-headshot-generator](https://magichour.ai/create/ai-headshot-generator) ## Fully deprecating all height/width params for face swap, image-to-video, lip sync, and video-to-video API We removed all validations for the height and width params for face swap, image-to-video, lip sync, and video-to-video API. Previously, if you specified height or width, you're required to specify both. Now, we will automatically calculate the output size based on the input size and your subscription tier. To take advantage of the this change, please upgrade your sdk version to `v0.35.0`. * [Python SDK v0.35.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.35.0) * [Node SDK v0.35.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.35.0) * [Go SDK v0.35.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.35.0) * [Rust SDK v0.35.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.35.0) ## Simplifying `POST /v1/ai-face-editor` API by making individual face editing params optional. Previously, you need to specify all the face editing params. Now, you can specify only the params you want to edit. This is a quality of life improvement to make it easier to use the API. ```diff theme={null} res = client.v1.ai_face_editor.create( assets={"image_file_path": "api-assets/id/1234.png"}, style={ - "enhance_face": False, "eye_gaze_horizontal": 50, - "eye_gaze_vertical": 0.0, - "eye_open_ratio": 0.0, - "eyebrow_direction": 0.0, - "head_pitch": 0.0, - "head_roll": 0.0, - "head_yaw": 0.0, - "lip_open_ratio": 0.0, - "mouth_grim": 0.0, - "mouth_position_horizontal": 0.0, - "mouth_position_vertical": 0.0, - "mouth_pout": 0.0, - "mouth_purse": 0.0, - "mouth_smile": 0.0, }, name="Face Editor image", ) ``` To take advantage of the this change, please upgrade your sdk version to `v0.34.0`. * [Python SDK v0.34.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.34.0) * [Node SDK v0.34.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.34.0) * [Go SDK v0.34.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.34.0) * [Rust SDK v0.34.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.34.0) ## Improved error message for APIs We reworking the error handling for APIs. Now, the error message will be more descriptive and helpful. A few examples: ```diff theme={null} { - "message": "Expected integer, received float" + "message": "Invalid type for height: expected integer, got float" } ``` ```diff theme={null} { - "message": "Required, Required" + "message": "Missing required field: start_seconds (expected number); Missing required field: end_seconds (expected number)" } ``` ## Recent images for Image to Video We added a quality of life improvement to Image to Video. You can now see the recent 4 images you've used for Image to Video. Recent images for Image to
Video Try it out now: [https://magichour.ai/create/image-to-video](https://magichour.ai/create/image-to-video) ## Updating API documentation sample code to SDK samples Previously, the code samples in the API documentation were not using our SDK. We pushed an update to ensure all languages we support now show the SDK code as the sample to make it easier to integrate. Check it out now: [https://docs.magichour.ai/api-reference](https://docs.magichour.ai/api-reference) ## Migrated AI Headshot Generator API to the latest model After our latest model update for AI Headshot Generator, we migrated the API to the latest model. No change is needed to start generating using the latest model. ## Multi-face swap for Face Swap Photo Now, you can pick and choose which faces to swap in a photo. Multi-face swap for Face Swap Photo Try it out now: [https://magichour.ai/create/face-swap-photo](https://magichour.ai/create/face-swap-photo) ## End frame support for Image to Video. We've added support for end frame in Image to Video. You can now specify the end frame of the video, and watch the start frame morph into the end frame. Try it out now: [https://magichour.ai/create/image-to-video](https://magichour.ai/create/image-to-video) Note: this is only available for the 480p resolution. We are working on adding support for the higher resolutions. ## Major Improvements to One-shot Headshot Outputs We've made major improvements to the one-shot headshot outputs. See the before and after below: One-shot Headshot Outputs Head over to [https://magichour.ai/create/ai-headshot-generator](https://magichour.ai/create/ai-headshot-generator) to try it out now! ## Train Model Page We created a new page to train your own model and see the list of models you've trained. Train Model Page Head over to [https://magichour.ai/create/train-model](https://magichour.ai/create/train-model) to try it out now! ## Launching V3 of Video-to-Video We just launched Video-to-Video v3: our most consistent, stylized, and realistic version yet. You can turn anyone into a Minecraft character, Game of Thrones character, or WWI soldier - without VFX, post-production, or a big budget. Try it out now: [https://magichour.ai/create/video-to-video](https://magichour.ai/create/video-to-video) ## Added background replacement functionality to Image Background Remover API We've added background replacement functionality to the Image Background Remover API. You can now replace the background of an image with a new background. To take advantage of the this change, please upgrade your sdk version to `v0.33.0`. * [Python SDK v0.33.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.33.0) * [Node SDK v0.33.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.33.0) * [Go SDK v0.33.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.33.0) * [Rust SDK v0.33.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.33.0) ## Added support for individual face swapping in Face Swap Video API We've enhanced the Face Swap Video API to support selective face swapping. You can now specify which faces to swap in a video using the new `face_mappings` parameter. This gives you more precise control over face swapping operations. To take advantage of the this change, please upgrade your sdk version to `v0.32.0`. * [Python SDK v0.32.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.32.0) * [Node SDK v0.32.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.32.0) * [Go SDK v0.32.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.32.0) * [Rust SDK v0.32.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.31.1) Note: `v0.31.0` had a bug where the `image_file_path` is still required even if you're using `face_swap_mode: individual-faces`. This has been fixed in `v0.31.1`. ## Fixed authentication issue on `GET /v1/face-detection/{id}` API We noticed that the `GET /v1/face-detection/{id}` API was always returning 401 Unauthorized. We have since fixed the issue and the API should now be working as expected. To take advantage of the this change, please upgrade your sdk version to `v0.30.1`. * [Python SDK v0.30.1](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.30.1) * [Node SDK v0.30.1](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.30.1) * [Go SDK v0.30.1](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.30.1) * [Rust SDK v0.30.1](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.30.1) ## Added APIs for face detection and face swap We added two new APIs for face detection and face swap: [`POST /v1/face-detection`](/api-reference/files/face-detection) * Detect faces in an image or video [`GET /v1/face-detection/{id}`](/api-reference/files/get-face-detection-details) * Get the result of a face detection job * Detected faces are added to the response as `faces` as soon as any face is detected. Example usage: ```python theme={null} res = client.v1.face_detection.create( assets={ "target_file_path": "https://cdn.magichour.ai/target.jpg", }, confidence_score='0.75', ) task_id = res.id status = "pending" while status != 'complete': res = client.v1.face_detection.get(id=task_id) status = res.status time.sleep(1) res = client.v1.face_swap_photo.create( assets={ "face_swap_mode": 'individual-faces', "target_file_path": "https://cdn.magichour.ai/target.jpg", "face_mappings": [ { "new_face": "https://cdn.magichour.ai/new_face.jpg", "original_face": res.faces[0].path } ] }, ) ``` To take advantage of the this change, please upgrade your sdk version to `v0.30.0`. * [Python SDK v0.30.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.30.0) * [Node SDK v0.30.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.30.0) * [Go SDK v0.30.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.30.0) * [Rust SDK v0.30.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.30.0) ## Added support for individual face swapping in Face Swap Photo API We've enhanced the Face Swap Photo API to support selective face swapping. You can now specify which faces to swap in a photo using the new `face_mappings` parameter. This gives you more precise control over face swapping operations. Example usage: ```python theme={null} res = client.v1.face_swap_photo.create( assets={ "face_swap_mode": 'individual-faces', "target_file_path": "https://cdn.magichour.ai/target.jpg", "face_mappings": [ { "new_face": "https://cdn.magichour.ai/new_face.jpg", "original_face": "api-assets/id/0-0.png" } ] }, ) ``` To take advantage of the this change, please upgrade your sdk version to `v0.29.0`. * [Python SDK v0.29.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.29.0) * [Node SDK v0.29.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.29.0) * [Go SDK v0.29.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.29.0) * [Rust SDK v0.29.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.29.0) Note: the original face path is part of the response from the face detection API \[Coming soon] ## Increased Talking Photo duration limit to 60 seconds We've extended the maximum duration for Talking Photo generations from 30 seconds to 60 seconds. This allows for longer, more engaging animated content while maintaining the same high quality output. This update applies to both the API and dashboard. To take advantage of the this change, please upgrade your sdk version to `v0.28.0`. * [Python SDK v0.28.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.28.0) * [Node SDK v0.28.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.28.0) * [Go SDK v0.28.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.28.0) * [Rust SDK v0.28.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.28.0) ## Added additional functions to the Image Details modal Image Details Modal We added additional functions to the Image Details modal, including: * Upscale * Edit * Turn into video * Remove background ## Added `POST /v1/auto-subtitle-generator` API We added support for the `POST /v1/auto-subtitle-generator` API, which allows you to generate subtitles for your videos. To take advantage of the this change, please upgrade your sdk version to `v0.27.0`. * [Python SDK v0.27.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.27.0) * [Node SDK v0.27.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.27.0) * [Go SDK v0.27.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.27.0) * [Rust SDK v0.27.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.27.0) ## Fixing duration issue in 480p Image to Video and Text to Video We found an issue where the duration of the output video is always 5s even if you specified 10s. We have since fixed the issue and the duration of the output video should now be correct. And we fully refunded all affected videos. ## Fixing webhook timeout issue We were notified that webhook delivery was seeing delays. This was caused by a invalid webhook configuration where the delivery was not properly timing out in a reasonable amount of time. We have since fixed the issue and webhook delivery should be back to normal. ## Cheaper Image to Video and Text to Video We introduced a new 480p resolution for Image to Video and Text to Video, which is 60% cheaper! For a 5s video, 480p uses 120 credits, while 720p uses 300 credits. Note that the 480p resolution only support 5s or 10s videos. Check it out now: * [https://magichour.ai/create/image-to-video](https://magichour.ai/create/image-to-video) * [https://magichour.ai/create/text-to-video](https://magichour.ai/create/text-to-video) To take advantage of the 480p resolution video in our SDKs, please upgrade your sdk version to `v0.26.0`. * [Python SDK v0.26.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.26.0) * [Node SDK v0.26.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.26.0) * [Go SDK v0.26.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.26.0) * [Rust SDK v0.26.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.26.0) ## AI Image Editor Free Tool, Dashboard Tool, and API endpoint We launched everything in one go for AI Image Editor: * Free tool: [https://magichour.ai/products/ai-image-editor](https://magichour.ai/products/ai-image-editor) * Dashboard tool: [https://magichour.ai/create/ai-image-editor](https://magichour.ai/create/ai-image-editor) * API endpoint: [https://docs.magichour.ai/api-reference/image-projects/ai-image-editor](https://docs.magichour.ai/api-reference/image-projects/ai-image-editor) AI Image Editor To take advantage of the this change, please upgrade your sdk version to `v0.25.0`. * [Python SDK v0.25.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.25.0) * [Node SDK v0.25.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.25.0) * [Go SDK v0.25.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.25.0) * [Rust SDK v0.25.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.25.0) ## Brand new my library page We consolidated my video and my image pages into a single my library page. We believe this is a more intuitive way to manage your projects. Go check it out now: [https://magichour.ai/my-library](https://magichour.ai/my-library) ## Image to Video Free Tool As a fast follow, we launched a free tool for Image to Video. Image to Video Free Tool Try it out now: [https://magichour.ai/products/image-to-video](https://magichour.ai/products/image-to-video) ## Text to Video Free Tool We launched a free tool that allows you to create videos from text. Try without signing up! Text to Video Free Tool Try it out now: [https://magichour.ai/products/text-to-video](https://magichour.ai/products/text-to-video) ## Simplify file upload We updated the signature of the upload url from the `POST /v1/files/upload-urls` endpoint to not require `Content-Type` header. This should simplify the upload process a bit, you're welcome to pass in the content type of your file or leave it completely. ```diff theme={null} with open(local_path, 'rb') as file: response = requests.put( response.items[0].upload_url, data=file, - headers={'Content-Type': 'application/octet-stream'} ) ``` ## Split up AI headshot and selfie modes On our dashboard page, we split up AI headshot and AI selfie into separate modes, making it easier to find the correct mode you're interested in. AI Headshot and AI Selfie Split ## Additional art styles for stylized selfies for one-shot model We added additional styles specifically for our "One-Shot" model, which do not require training your own model so you can easily get started. AI Selfie Generator One-shot Style Look for styles with the following icon: ## Add support for tools for `POST /v1/ai-image-generator` endpoint We added `.style.tool` to the API so you can pick a specific tool to use for the AI Image generator. We have over 30 tools available to help better customize the style of your output. See the list of tools available via: [https://docs.magichour.ai/api-reference/image-projects/ai-image-generator#body-style-tool](https://docs.magichour.ai/api-reference/image-projects/ai-image-generator#body-style-tool) To take advantage of the this change, please upgrade your sdk version to `0.24.0`. * [Python SDK v0.24.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.24.0) * [Node SDK v0.24.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.24.0) * [Go SDK v0.24.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.24.0) * [Rust SDK v0.24.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.24.0) ## Brand new developer page We rebuild the Developer Hub to make it more informative and easier to navigate. Check it out and let me know what you think at [https://magichour.ai/developer](https://magichour.ai/developer) AI Selfie Generator Free Tool * unified usage analytics with the rest of the developer hub, so you don't have to visit a different url to see your usage. * split up the page into multiple tabs for `Analytics & Billing`, `API Keys`, and `Webhooks`. * added an invoice history table to show all prior invoices, and also showing if any credit is applied. ## Stylized selfie free tool We shipped a free tool so you can try out AI Selfie for free. AI Selfie Generator Free Tool Try it out now: [https://magichour.ai/products/ai-selfie-generator](https://magichour.ai/products/ai-selfie-generator)! ## Added image preview for professional headshot styles To make it easier to pick the right style for your headshot, we added image previews to the style buttons AI Headshot Generator Try it out now: [https://magichour.ai/create/ai-headshot-generator](https://magichour.ai/create/ai-headshot-generator)! ## Stylized Selfies We created a new tool that allow you to stylized your selfies in a variety of styles. You have the ability to train a model using your photos or use one image as your base image. AI Selfie Generator Try it out now: [https://magichour.ai/create/ai-selfie-generator](https://magichour.ai/create/ai-selfie-generator)! ## Improved AI Headshot Generator We improved our AI Headshot Generator! Now you can create studio-quality headshots by training a model using your photos. It's more realistic, accurate, and consistent than our one-shot model. Try it out at [https://magichour.ai/create/ai-headshot-generator](https://magichour.ai/create/ai-headshot-generator) No Art Style (Note: each training costs 2,000 credits) ## Fixed: Unable to cancel subscription with scheduled plan change We resolved an issue that prevented users from canceling their subscription if a plan change was scheduled. You can now cancel at any time, even with an upcoming plan update. ## Add generation mode and intensity param to talking photo API We recently introduced `Expressive` mode to Talking Photo, and now it has been added to the API as well. We also added a `.style.intensity` param to help control amount of motion and expressiveness. To take advantage of the this change, please upgrade your sdk version to `0.23.0`. * [Python SDK v0.23.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.23.0) * [Node SDK v0.23.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.23.0) * [Go SDK v0.23.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.23.0) * [Rust SDK v0.23.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.23.0) ## 🚀 Brand new Pricing Page We've redesigned our pricing page to make it easier to pick the right plan for you. The new page includes a clear breakdown of how many videos and images you can generate with each plan. No more guesswork — just clear, upfront info so you can create with confidence. No Art Style No Art Style We also think it looks way, way better. See for yourself [https://magichour.ai/pricing](https://magichour.ai/pricing) ## Renamed `frames` to `credit` in the API. We originally named our credit system `frame` because we started off only having video modes, but since we now include images as well, using frame as the name of our credit system can cause confusion, so we renamed it. We introduced this change in sdk v0.22.0, and deprecated fields referencing `frames` * added `credits_charged` field in the API response for get details API and success responses from create API calls. * deprecated `total_frame_cost` from the `GET /v1/video-projects/:id` API. * deprecated `estimated_frame_cost` from the success response in create video API. * deprecated `total_frame_cost` from the `GET /v1/image-projects/:id` API. * deprecated `frame_cost` from the success response in create image API. To take advantage of the this change, please upgrade your sdk version to `0.22.0`. * [Python SDK v0.22.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.22.0) * [Node SDK v0.22.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.22.0) * [Go SDK v0.22.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.22.0) * [Rust SDK v0.22.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.22.0) ## "No Art Style" Art Style for Video to Video We launched No Art Style in Video-to-Video, allowing you to "choose-your-own-adventure" with any art style or character No Art Style ## High Bitrate in Face Swap Video and Lip Sync We launched High Bitrate in Face Swap Video and Lip Sync, which maintains output video quality. This feature is currently available to users with a Pro or Business subscription. ## Launching Talking Photos called Expressive Mode It features more facial expressions and better mouth movement accuracy. The previous version of Talking Photos is still available as Stable Mode. ## Simplify Video-to-Video API by making `height`/`width` optional We removed `height` and `width` as required params for the `POST /v1/video-to-video` API. We now will automatically figure out orientation of the output video based on the input video. To take advantage of the this change, please upgrade your sdk version to `0.20.0`. * [Python SDK v0.20.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.20.0) * [Node SDK v0.20.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.20.0) * [Go SDK v0.20.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.20.0) * [Rust SDK v0.20.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.20.0) ## Stable Mode in Lip Sync Video We launched **Stable Mode** in Lip Sync Video, which reduces flickering in face swaps with beards, mustaches, piercings, and other items on the face. To use it, select "Stable Mode" under the setting Face Mask after uploading a video. The default setting, "Smart Mode," remains the same and is recommended for most videos. ## AI Gif Generator in Create Page + API Create Page Tile Try it now [https://magichour.ai/create](https://magichour.ai/create) The GIF Generator is also available via [API](/api-reference/image-projects/ai-gif-generator). To take advantage of the this change, please upgrade your sdk version to `0.19.0`. * [Python SDK v0.19.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.19.0) * [Node SDK v0.19.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.19.0) * [Go SDK v0.19.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.19.0) * [Rust SDK v0.19.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.19.0) ## 🎉 New: Free AI Gif Generator You can create cool gif for free! Create Page Tile Try it out now [https://magichour.ai/products/ai-gif-generator](https://magichour.ai/products/ai-gif-generator) ## Simplify Image-to-Video API We removed `height` and `width` as required params for the `POST /v1/image-to-video` API. We now will automatically figure out orientation of the output video based on the input image. To take advantage of the this change, please upgrade your sdk version to `0.18.0`. * [Python SDK v0.18.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.18.0) * [Node SDK v0.18.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.18.0) * [Go SDK v0.18.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.18.0) * [Rust SDK v0.18.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.18.0) ## Photo Colorizer in Create Page + API Create Page Tile If you have any old black/white photos, you can now colorizer it in the dashboard. Try it now [https://magichour.ai/create](https://magichour.ai/create) The colorizer is also available via [API](/api-reference/image-projects/photo-colorizer). To take advantage of the this change, please upgrade your sdk version to `0.17.0`. * [Python SDK v0.17.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.17.0) * [Node SDK v0.17.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.17.0) * [Go SDK v0.17.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.17.0) * [Rust SDK v0.17.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.17.0) ## Simplify Lip Sync API and New AI Face Editor API We removed `height` and `width` as required params for the `POST /v1/lip-sync` API. We now will automatically figure out aspect ratio of the input video and resize based on your subscription's maximum resolution. This makes it easier to create the video as you no longer need to figure out the aspect ratio of your input video and compute the height/width. See [Resolution Limits by Subscription](/billing/resolution-limits) for detailed limits by tier. We also released `POST /v1/ai-face-editor` API. To take advantage of the this change, please upgrade your sdk version to `0.16.0`. * [Python SDK v0.16.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.16.0) * [Node SDK v0.16.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.16.0) * [Go SDK v0.16.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.16.0) * [Rust SDK v0.16.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.16.0) ## Simplify Face Swap Video API We removed `height` and `width` as required params for the `POST /v1/face-swap` API. We now will automatically figure out aspect ratio of the input video and resize based on your subscription's maximum resolution. This makes it easier to create the video as you no longer need to figure out the aspect ratio of your input video and compute the height/width. See [Resolution Limits by Subscription](/billing/resolution-limits) for detailed limits by tier. To take advantage of the this change, please upgrade your sdk version to `0.15.0`. * [Python SDK v0.15.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.15.0) * [Node SDK v0.15.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.15.0) * [Go SDK v0.15.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.15.0) * [Rust SDK v0.15.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.15.0) ## Stable Mode in Face Swap Video and Photo We launched **Stable Mode** in Face Swap Video and Photo, which reduces flickering in face swaps with beards, mustaches, piercings, and other items on the face. To use it, select "Stable Mode" under the setting Face Mask after uploading a video. The default setting, "Smart Mode," remains the same and is recommended for most videos. ## Higher Quality Text-to-Video We added higher quality mode to `Text-to-video` Create Page Tile This feature is available to all web and API users. ## 🎉 New: Free AI Face Editor in Create Page Now you can edit photos in our create page, meaning your outputs will be saved and you have no daily limit! Each meme costs 1 frame. Create Page Tile ## API Changes To take advantage of the latest changes, please upgrade to `0.13.0`. * [Python SDK v0.13.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.13.0) * [Node SDK v0.13.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.13.0) * [Go SDK v0.13.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.13.0) * [Rust SDK v0.13.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.13.0) ### 🎉 Create Memes via API We added `POST /v1/ai-meme-generator` to our APIs and SDKs. Now you can create memes even faster! Check out [API reference](/api-reference/image-projects/ai-meme-generator) ### ⚠️ Breaking changes in GET image/video project APIs The `GET /v1/video-projects/:id` and `GET /v1/image-projects/:id` API type field is updated from enum to string field. This field is mainly used for internal filtering, and having the field being enum means any new mode we add becomes a breaking change. So we decided to introduce this breaking change once so we can more easily add new modes down the line. ## 🎉 Edit Face in Create Page Now you can create memes in our create page, meaning your outputs will be saved and you have no daily limit! Each meme costs 10 frames. Create Page Tile ## 🎉 New: Photo Colorizer Free Tool Colorize your photo in seconds, completed free! [https://staging.magichour.ai/products/photo-colorizer](https://staging.magichour.ai/products/photo-colorizer) ## 🎉 Create Memes in Create Page Now you can create memes in our create page, meaning your outputs will be saved and you have no daily limit! Each meme costs 10 frames. Create Page Tile ## 🎉 New: Free AI Face Editor Change expression in seconds, completely free! [https://magichour.ai/products/ai-face-editor](https://magichour.ai/products/ai-face-editor) ## Longer Talking Photo Videos We made improvements to our Talking Photo mode. Now you can generate videos up to 30 seconds, up from 10s. | Previous | New | Improvement | | :------- | :-- | :---------- | | 10s | 30s | 3x | ## 🎉 New: Free AI Meme Generator Create hilarious memes in seconds using our curated templates. No signup needed—just pick a template and go! [https://magichour.ai/products/ai-meme-generator](https://magichour.ai/products/ai-meme-generator) Product Page ## 🐛 Fixed 500 Error in Video Projects API We fixed an issue where `GET /v1/video-projects` would return a 500 error when requesting a video project with `type: TALKING_PHOTO`. ## 🎨 Improved Animation Output We improved Animation mode to make results more artistic, creative, and fluid. Previously, we used a fast LCM model that often produced a grainy, burnt-in look. Now, we've prioritized visual quality and switched to a higher quality model with minimal speed tradeoffs. Give it a try at [https://magichour.ai/create/animation](https://magichour.ai/create/animation) and let us know what you think. ## 🎙️ Launching AI Talking Photo API Checkout the [AI Talking Photo API Reference](/api-reference/video-projects/ai-talking-photo) For SDKs, please upgrade to `0.12.0` to create AI Talking Photo * [Python SDK v0.12.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.12.0) * [Node SDK v0.12.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.12.0) * [Go SDK v0.12.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.12.0) * [Rust SDK v0.12.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.12.0) ## Launching 5 New Video-to-Video Art Styles We added a few additional art styles for Video-to-Video. User Menu Try them now: [Video-to-Video](https://magichour.ai/create/video-to-video) ## Improved Error Banner We updated the error banner you see in the image/video details Create Page The new banner includes additional info on why the render failed, and provided some quick actions to retry or contact us for assistance. ## AI Talking Photo Create Page Launch We've launched AI Talking Photo in the [Create Page](https://magichour.ai/create)! Create Page With this launch, you can: * generate up to 10 seconds of video, vs 5 seconds in the free tool. * utilize our text-to-speech feature as the basis for your audio Try it now at [AI Talking Photo Create Page](https://magichour.ai/create/ai-talking-photo) ## Launching AI Talking Photo Free Tool Now you can turn photo into a live video! Try it now for free at [https://magichour.ai/products/ai-talking-photo](https://magichour.ai/products/ai-talking-photo) Product Page ## Launching 36 AI Image Generator Art Styles We launched **36** new AI image generator art styles so you can generate the image in your vision more easily. This feature is available to all users! Product Page Try it for free: [https://magichour.ai/products/ai-image-generator](https://magichour.ai/products/ai-image-generator) Dashboard Create an account to create more [https://magichour.ai/create/ai-image-generator](https://magichour.ai/create/ai-image-generator) ## AI Clothes Changer Speed Improvement We've upgraded our AI Clothes Changer to version 3, providing enhanced quality and performance for all your virtual wardrobe needs. On [2025-03-07](/changelog#2025-03-07), we released an improved version of AI Clothes changer with higher quality to paid users only. This version improved the quality of the output; however, it was much slower than the previous version. In this latest update, we drastically improved generation time of the new version. | Version | Time | Improvement | | :------ | :----------- | :---------- | | 1.0 | \~18 seconds | -- | | 2.0 | \~43 seconds | 2.4x slower | | 3.0 | \~6 seconds | 7.2x faster | This release is available to all users! ## AI Headshot Generator Custom Prompt Feature We've added prompt customization to our AI Headshot Generator, allowing you to better control the style and appearance of your professional headshots. This feature is available on the web app and API. Custom Prompt To try it out, head over to [AI Headshot Generator](https://magichour.ai/create/ai-headshot-generator). To use custom prompt with Rest API, add the `prompt` param to your request. ```json theme={null} { "style": { "prompt": "professional passport photo" } } ``` For SDKs, please upgrade to `0.10.0` to use custom prompt with AI Headshot Generator. * [Python SDK v0.10.0](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.10.0) * [Node SDK v0.10.0](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.10.0) * [Go SDK v0.10.0](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.10.0) * [Rust SDK v0.10.0](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.10.0) ## Added Error Codes For Invalid File Path We added 3 API error codes to identify invalid file path issues. You can get the error details using the `GET /v1/image-projects/:id` and `GET /v1/video-projects/:id` endpoints by accessing `.error.code` and `.error.message` fields. | Error Code | Error Message | | :------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `invalid_audio_path` | The audio\_file\_path value provided does not point to a valid audio file. This value should be a valid URL or file\_path from the API [https://docs.magichour.ai/api-reference/files/generate-asset-upload-urls](https://docs.magichour.ai/api-reference/files/generate-asset-upload-urls). | | `invalid_image_path` | The image\_file\_path value provided does not point to a valid image file. This\` value should be a valid URL or file\_path from the API [https://docs.magichour.ai/api-reference/files/generate-asset-upload-urls](https://docs.magichour.ai/api-reference/files/generate-asset-upload-urls). | | `invalid_video_path` | The video\_file\_path value provided does not point to a valid video file. This value should be a valid URL or file\_path from the API [https://docs.magichour.ai/api-reference/files/generate-asset-upload-urls](https://docs.magichour.ai/api-reference/files/generate-asset-upload-urls). | ## Improved Model / Garment Detection For Small Images We make improvements to our model and garment detection to better find subjects in low resolution images. Previously, you would get the error `Please use an image with a detectable model` or `Please use an image with a detectable outfit`. ## AI Clothes Changer Quality Upgrade We released an improved version of AI Clothes changer with higher quality to paid users only. This version has better masking, less errors, and preserves elements more of the original image, like hair. Cloth Changer upgrade ## Faster Rendering for AI Headshot and Face Swap Photo We optimized our AI Headshot Generator and Face Swap Photo tools, cutting rendering times drastically. | Tool | Previous Time | New Time | Improvement | | :-------------------- | :------------ | :---------- | :---------- | | AI Headshot Generator | \~30 seconds | \~7 seconds | 4.3x faster | | Face Swap Photo | \~16 seconds | \~6 seconds | 2.5x faster | ## Improved face detection in Multi-Face Swap We improved face detection accuracy in Multi-Face Swap for images with zoomed in faces. ## Multi Face Swap Just Got Better! 🚀 We've improved our Multi Face Swap mode — now with **2.5x** faster processing and higher quality! ## Added `.m4v` File Support All APIs and web UIs now allows files with `.m4v` extension. All SDKs are updated to reflect this change. * [Python SDK v0.9.3](https://github.com/magichourhq/magic-hour-python/releases/tag/v0.9.3) * [Node SDK v0.9.3](https://github.com/magichourhq/magic-hour-node/releases/tag/v0.9.3) * [Go SDK v0.9.3](https://github.com/magichourhq/magic-hour-go/releases/tag/v0.9.3) * [Rust SDK v0.9.3](https://github.com/magichourhq/magic-hour-rust/releases/tag/v0.9.3) ## Customizable Caption Templates! 🎉 We've added customization options to give you control over your subtitles appearance. You can now customize: * Text color, text highlight color, and Text border color. * Vertical and horizontal position. Customize Template UI Try it now: [Auto Subtitle Generator](https://magichour.ai/create/auto-subtitle-generator) ## Animation and Video-to-Video APIs Now Allow File URLs. To make it easier to use input files, we have now updated final two endpoints to support file urls for input files: * `POST /v1/animation` * `POST /v1/video-to-video` Now, you won't have to upload files to our storage first before calling these two APIs. ```python Python SDK {4} theme={null} res = client.v1.video_to_video.create( assets={ "video_source": "file", "video_file_path": "https://cdn.yourwebsite.com/files/video.mp4" } ) ``` ```typescript Node SDK {4} theme={null} const res = await client.v1.videoToVideo.create({ assets: { videoSource: "file", videoFilePath: "https://cdn.yourwebsite.com/files/video.mp4", }, }); ``` ```go Go SDK {4} theme={null} res, err := client.V1.VideoToVideo.Create(video_to_video.CreateRequest{ Assets: types.PostV1VideoToVideoBodyAssets{ VideoSource: types.PostV1VideoToVideoBodyAssetsVideoSourceEnumFile, VideoFilePath: nullable.NewValue("https://cdn.yourwebsite.com/files/video.mp4"), }, }) ``` ```rust Rust SDK {4} theme={null} let res = client.v1().video_to_video().create(magic_hour::resources::v1::video_to_video::CreateRequest { assets: magic_hour::models::PostV1VideoToVideoBodyAssets { video_source: magic_hour::models::PostV1VideoToVideoBodyAssetsVideoSourceEnum::File, video_file_path: Some("https://cdn.yourwebsite.com/files/video.mp4".to_string()), }, ); ``` ```python Python SDK {4-5} theme={null} res = client.v1.animation.create( assets={ "audio_source": "file", "audio_file_path": "https://cdn.yourwebsite.com/files/audio.mp3", "image_file_path": "https://cdn.yourwebsite.com/files/image.png", }, ) ``` ```typescript Node SDK {4-5} theme={null} const res = await client.v1.animation.create({ assets: { audioSource: "file", audioFilePath: "https://cdn.yourwebsite.com/files/audio.mp3", imageFilePath: "https://cdn.yourwebsite.com/files/image.png", }, }); ``` ```go Go SDK {4-5} theme={null} res, err := client.V1.Animation.Create(animation.CreateRequest{ Assets: types.PostV1AnimationBodyAssets{ AudioSource: types.PostV1AnimationBodyAssetsAudioSourceEnumFile, AudioFilePath: nullable.NewValue("https://cdn.yourwebsite.com/files/audio.mp3"), ImageFilePath: nullable.NewValue("https://cdn.yourwebsite.com/files/image.png"), }, }) ``` ```rust Rust SDK {4-5} theme={null} let res = client.v1().animation().create(magic_hour::resources::v1::animation::CreateRequest { assets: magic_hour::models::PostV1AnimationBodyAssets { audio_file_path: Some("https://cdn.yourwebsite.com/files/audio.mp3".to_string()), audio_source: magic_hour::models::PostV1AnimationBodyAssetsAudioSourceEnum::File, image_file_path: Some("https://cdn.yourwebsite.com/files/image.png".to_string()), }, ); ``` All of our APIs now supports file URLs! ## Generate Subtitle for 3+ Minutes Videos We added the `Auto Subtitle Generator` to our dashboard! The max duration of our [current tool](https://magichour.ai/products/auto-subtitle-generator) is 30 seconds. We have now expanded the max to 10,000 frames. This means you can generate videos of length: * 6.9 minutes at 24fps * 5.5 minutes at 30fps Dashboard View Try it now: [Auto Subtitle Generator](https://magichour.ai/create/auto-subtitle-generator) ## Added `high_quality` Param to Image-to-Video API We previously introduced a `High Quality` checkbox to the web app. Now, our API supports high quality generation as well. As a quick recap, "High Quality" is better at detail preservation, ideal for humans and complex images. To use high quality mode, simply add `.style.high_quality` param when calling the API. ```diff theme={null} { "style": { + "high_quality": true } } ``` ## File URL Support for 5 different APIs. To make it easier to work with input files, we added support for file urls for the following APIs: * POST /v1/ai-headshot-generator * POST /v1/ai-image-upscaler * POST /v1/image-background-remover * POST /v1/image-to-video * POST /v1/lip-sync Now you don't have to upload the file to Magic Hour storage first before using the asset in the APIs. Simply use the url in the `file_path` inputs: ```json theme={null} { "audio_file_path": "https://cdn.yourwebsite.com/files/audio.mp3", "image_file_path": "https://cdn.yourwebsite.com/files/image.png", "video_file_path": "https://cdn.yourwebsite.com/files/video.mp4" } ``` ## Launching Usage-Based Pricing for API users Benefits: * ✅ Pay only for what you use * ✅ Volume discounts—up to 50% off * ✅ Never run out of frames For example, 15,000 frames now costs just \$18 on our Creator tier instead of \$27 with a subscription. ![Pricing Comparison](https://videos.magichour.ai/api-assets/api-pricing-comparison-chart.png) If you're interested in our new pricing, send us an email at [support@magichour.ai](mailto:support@magichour.ai). ## Detailed Usage Cost Breakdown + Usage Analytics For users who has an active usage-based pricing subscription, we added detailed breakdown of cost based on each usage tier in your [Developer Hub](https://magichour.ai/developer) Cost Breakdown Plus, get detailed analytics on your usage: Usage Analytics ## Added More Error Codes For More Visibility We added 4 more API error codes to help you identify why a particular render failed. You can get the error details using the `GET /v1/image-projects/:id` and `GET /v1/video-projects/:id` endpoints by accessing `.error.code` and `.error.message` fields. | Error Code | Error Message | | :-------------------- | :---------------------------------------------------------------------------------------------------------------------------------- | | `invalid_audio_file` | The audio file contains invalid data. Please try a different file. | | `invalid_image_file` | The image file contains invalid data. Please try a different file. | | `invalid_video_file` | The video file contains invalid data. Please try a different file. | | `invalid_youtube_url` | The youtube video cannot be downloaded. The video may be private, age-restricted, or region-restricted. Please try a different url. | ## Added AI Clothes Changer API AI Clothes Changer is now available via API! Get started with this new API using: * [Python SDK](https://github.com/magichourhq/magic-hour-python/tree/main/magic_hour/resources/v1/ai_clothes_changer) * [Node SDK](https://github.com/magichourhq/magic-hour-node/tree/main/src/resources/v1/ai-clothes-changer) * [Go SDK](https://github.com/magichourhq/magic-hour-go/tree/main/resources/v1/ai_clothes_changer) * [Rust SDK](https://github.com/magichourhq/magic-hour-rust/tree/main/src/resources/v1/ai_clothes_changer) * [Rest API](/api-reference/image-projects/ai-clothes-changer) ## Officially Launching the Magic Hour API Today we're excited to announce the Magic Hour API! Developers can now integrate our AI image & video generation tools directly into their apps and products. Free to get started for all users, with usage-based pricing available by request. Check it out at: [https://magichour.ai/api](https://magichour.ai/api). ☝️ The API page also includes a calculator to help estimate cost ## Launching AI Clothes Changer! Head over to [AI Clothes Changer](https://magichour.ai/create/ai-clothes-changer) to create yours today! Dashboard View We also launched a free tool to the [AI Clothes Changer Product Page](https://magichour.ai/products/ai-clothes-changer) so you can try out the new product for free! Dashboard View ## Launching Auto Subtitle Generator Free Tool We launched a free tool to automate subtitle generation on your video. Head over to the [Auto Subtitle Generator Product Page](https://magichour.ai/products/auto-subtitle-generator) to try out the new product for free! Dashboard View Each generation is limited to 30 seconds, and you can generate 3 videos free per day. That's 1.5 minutes of free usage every day! # Authentication Source: https://docs.magichour.ai/get-started/authentication Create and manage API keys to authenticate your requests to the Magic Hour API. ## Overview Magic Hour uses API keys to authenticate requests. Your API keys carry many privileges, so be sure to keep them secure! Do not share your secret API keys in publicly accessible areas such as GitHub, client-side code, and so forth. All API requests must be authenticated by including your API key in the `Authorization` header: ```bash theme={null} Authorization: Bearer your_api_key_here ``` ## Creating Your First API Key Visit [magichour.ai](https://magichour.ai/sign-up) and sign in to your account. If you don't have an account yet, you can create one for free. Sign in page Once signed in, go directly to the [API Keys section of the Developer Hub](https://magichour.ai/developer?tab=api-keys). Developer Hub with API Keys Click the **"Create API Key"** button to generate a new key. Create API Key button Give your API key a descriptive name to help you identify its purpose later: * **Name**: Choose a clear name (e.g., "Production App", "Development Testing", "Mobile App") * **Permissions**: Select the appropriate permissions for your use case API Key configuration Click **"Create Key"** to generate your new API key. **Important**: Your API key will only be shown once. Copy it immediately and store it securely. API Key created **Save your API key now!** You won't be able to see the full key again after closing this dialog. If you lose it, you'll need to create a new one. ## Managing Your API Keys ### Viewing API Keys In the Developer Hub, you can see all your API keys with: * **Name**: The descriptive name you gave the key * **Last 4 Characters**: The last 4 characters of the key * **Created**: When the key was created API Keys list ### Revoking API Keys To revoke an API key: 1. Find the key in your API Keys list 2. Click the **Delete (Trash Icon)** button next to the key 3. Click "Delete key" to confirm the revocation Revoke API Key **Immediate Effect**: Revoked keys stop working immediately. Any applications using the revoked key will start receiving authentication errors. ### Rotating API Keys For security best practices, regularly rotate your API keys: 1. **Create a new API key** with the same permissions 2. **Update your applications** to use the new key 3. **Test thoroughly** to ensure everything works 4. **Revoke the old key** once you're confident the new key is working ## Using API Keys ### In Code ```python Python SDK theme={null} from magic_hour import Client # Initialize client with your API key client = Client(token="your_api_key_here") # Or use environment variable import os client = Client(token=os.getenv("MAGIC_HOUR_API_KEY")) ``` ```javascript Node.js SDK theme={null} import { Client } from "magic-hour"; // Initialize client with your API key const client = new Client({ token: "your_api_key_here", }); // Or use environment variable const client = new Client({ token: process.env.MAGIC_HOUR_API_KEY, }); ``` ```bash cURL theme={null} curl -X POST "https://api.magichour.ai/v1/ai-image-generator" \ -H "Authorization: Bearer your_api_key_here" \ -H "Content-Type: application/json" \ -d '{ "image_count": 1, "aspect_ratio": "16:9", "resolution": "1k", "style": { "prompt": "A beautiful sunset over mountains" } }' ``` ### Environment Variables Store your API key as an environment variable for security: ```bash .env file theme={null} MAGIC_HOUR_API_KEY=your_api_key_here ``` ```bash Shell Export theme={null} export MAGIC_HOUR_API_KEY=your_api_key_here ``` ```bash Windows theme={null} set MAGIC_HOUR_API_KEY=your_api_key_here ``` ## Security Best Practices ### ✅ Do * **Store keys securely** in environment variables or secure key management systems * **Use different keys** for different environments (development, staging, production) * **Rotate keys regularly** (every 90 days recommended) * **Revoke unused keys** immediately * **Monitor key usage** in the Developer Hub * **Implement proper error handling** for authentication failures ### ❌ Don't * **Never commit keys** to version control (Git, SVN, etc.) * **Don't expose keys** in client-side code (JavaScript, mobile apps) * **Don't share keys** via email, chat, or other insecure channels * **Don't use production keys** in development environments * **Don't ignore security warnings** about exposed keys ### Key Storage Solutions **For Development:** * Environment variables (`.env` files) * Local configuration files (excluded from version control) **For Production:** * AWS Secrets Manager * Azure Key Vault * Google Secret Manager * HashiCorp Vault * Kubernetes Secrets ## Authentication Errors Common authentication errors and how to resolve them: ### `401 Unauthorized` **Cause**: Invalid or missing API key **Solutions**: * Check that your API key is correct * Ensure the `Authorization` header is properly formatted * Verify the key hasn't been revoked ## Monitoring Usage **Usage-Based Pricing Only**: Analytics & Billing monitoring is only available for users on a usage-based pricing plan. Subscription users can view basic key information in the API Keys section. For usage-based pricing users, track your API key usage in the Developer Hub: * **Credit Usage**: Credits consumed by your requests This helps you: * Detect unusual activity that might indicate a compromised key * Plan for capacity and billing ## Next Steps Make your first API call with your new API key Try the Magic Hour APIs with ready-to-run sample code. Just add your API key. Use official SDKs for easier integration Understand how API usage affects billing Set up webhooks for real-time notifications ## Need Help? If you're having trouble with authentication: * Check our [troubleshooting guide](/get-started/quick-start#troubleshooting) * Contact support at [support@magichour.ai](mailto:support@magichour.ai) * Join our community on [Discord](https://discord.gg/JX5rgsZaJp) **Security Concern?** If you believe your API key has been compromised, revoke it immediately in the Developer Hub and create a new one. # API Cookbook - Google Colab Source: https://docs.magichour.ai/get-started/cookbook Try the Magic Hour APIs with ready-to-run sample code. ## Overview The Magic Hour API Cookbook is a comprehensive Google Colab notebook containing working examples for the most popular Magic Hour APIs. Simply add your API key and start experimenting with: * All video tools (Animation, Face Swap, Lip Sync, etc.) * All image tools (Image Generator, Face Editor, Upscaler, etc.) * All audio tools (Voice Generator) Launch the notebook in Google Colab. Just add your API key and run any example. ## What's Included The cookbook includes: * **Working code samples** for the most popular APIs * **Pre-configured parameters** optimized for fast execution * **Error handling** examples * **Best practices** and tips ## Getting Started 1. **Get your API key** - If you haven't already, [create an API key](/get-started/authentication) 2. **Open the notebook** - Click the link above to open in Google Colab 3. **Add your API key** - Set it in the first cell 4. **Run any example** - Execute any code cell to try that API **No installation required:** Google Colab runs Python in your browser with all dependencies pre-installed. ## Related Resources Step-by-step guide to your first API call Complete API documentation # Quick Start Source: https://docs.magichour.ai/get-started/quick-start Generate an API key and make your first call in <3 minutes. Magic Hour is an AI video and image generation platform. You submit a job, we render it, and you download the result. This guide gets you to your first output in a few minutes. Try every API from your browser with only an API key. **What you'll accomplish:** 1. **Create an API key** - Get credentials to authenticate with our API 2. **Set up your development environment** - Install SDK and create project files 3. **Generate your first output** - Make an API call and download the result **Credit Cost:** Pick the example that matches your goal. The image tab costs about 5 credits; the face swap video tab costs about 200 credits for the sample clip. New accounts get 400 free credits plus 100 daily credits if you claim them in the web app. ## 1. Create your API key **Why:** API keys authenticate your requests and track your usage. 1. Open the [API Keys section of the Developer Hub](https://magichour.ai/developer?tab=api-keys\&ref=docs-quickstart) and sign in. 2. Click **Create key**, give it a name (e.g., "My First Project"), and create it. 3. **Copy the API key immediately** — you won't be able to see it again. See screenshots, permissions, and key-management guidance in the Authentication guide. Store your API key as an environment variable: ```bash macOS/Linux theme={null} export MAGIC_HOUR_API_KEY="your_api_key_here" ``` ```cmd Windows theme={null} setx MAGIC_HOUR_API_KEY "your_api_key_here" ``` ```powershell PowerShell theme={null} $env:MAGIC_HOUR_API_KEY = "your_api_key_here" ``` **Never commit API keys to version control.** Always use environment variables or secure credential management. ## 2. Set up your development environment **Why:** SDKs handle authentication, polling, and file downloads automatically, reducing boilerplate code. ### Create your project directory ```bash Python theme={null} # Create and navigate to project directory mkdir magic-hour-quickstart cd magic-hour-quickstart # Create your Python file touch main.py ``` ```bash Node.js theme={null} # Create and navigate to project directory mkdir magic-hour-quickstart cd magic-hour-quickstart # Initialize npm project npm init -y # Enable ES module support (required for the SDK) npm pkg set type=module # Create your JavaScript file touch main.js ``` ```bash Go theme={null} # Create and navigate to project directory mkdir magic-hour-quickstart cd magic-hour-quickstart # Initialize Go module go mod init magic-hour-quickstart # Create your Go file touch main.go ``` ```bash Rust theme={null} # Create new Rust project cargo new magic-hour-quickstart cd magic-hour-quickstart ``` ### Install the SDK ```sh Python SDK theme={null} pip install magic_hour ``` ```sh Node SDK theme={null} npm install magic-hour ``` ```sh Go SDK theme={null} go get -u github.com/magichourhq/magic-hour-go ``` ```sh Rust SDK theme={null} cargo add magic_hour ``` Want to test your integration without spending credits? The SDKs include a [mock server](/integration/development-and-testing#mock-server-recommended-for-development) that returns sample responses instantly. ## 3. Generate your first output **What we're doing:** Submit a generation job, wait for Magic Hour to render it, then download the result. Choose the example that matches what you want to build: | Example | Best for | Typical cost | Typical wait | | :------------------ | :------------------------ | :-------------------------------- | :----------- | | **Generate image** | Cheapest first success | 5 credits | 5-30 seconds | | **Face swap video** | Our most popular API path | \~200 credits for the sample clip | 2-5 minutes | ### Copy the code and run it **SDK version required for `generate()`:** Use Python SDK v0.36.0+ or Node SDK v0.37.0+. Older versions do not include this helper. Go and Rust use the manual create/poll/download pattern shown below. 1. **Choose an example tab**, then copy the code from your preferred language tab below. Create an image from a text prompt. This is the cheapest way to verify your setup end to end. **Why these parameters:** * `image_count: 1` - Generate one image (costs 5 credits) * `aspect_ratio: "16:9"` - Widescreen (landscape) output; also supports `1:1` and `9:16` * `resolution: "1k"` - Request an explicit supported resolution instead of deprecated `auto` * `wait_for_completion: true` - SDK polls until done * `download_outputs: true` - Automatically download to local disk * `download_directory: "."` - Save to the current directory ```python Python SDK theme={null} from magic_hour import Client import os # Use environment variable for security client = Client(token=os.getenv("MAGIC_HOUR_API_KEY")) result = client.v1.ai_image_generator.generate( image_count=1, aspect_ratio="16:9", resolution="1k", style={ "prompt": "Epic anime art of wizard casting a cosmic spell in the sky that says 'Magic Hour'" }, wait_for_completion=True, # wait for the render to complete download_outputs=True, # download the outputs to local disk download_directory=".", # save the outputs to the current directory ) print(f"created image with id {result.id}, spent {result.credits_charged} credits. Outputs are saved at {result.downloaded_paths}") ``` ```typescript Node SDK theme={null} import { Client } from "magic-hour"; // Use environment variable for security const client = new Client({ token: process.env.MAGIC_HOUR_API_KEY }); async function main() { const createRes = await client.v1.aiImageGenerator.generate( { imageCount: 1, aspectRatio: "16:9", resolution: "1k", style: { prompt: "Epic anime art of wizard casting a cosmic spell in the sky that says 'Magic Hour'", }, }, { waitForCompletion: true, // wait for the render to complete downloadOutputs: true, // download the outputs to local disk downloadDirectory: ".", // save the outputs to the current directory } ); console.log( `created image with id ${createRes.id}, spent ${createRes.creditsCharged} credits. Outputs are saved at ${createRes.downloadedPaths}` ); } main(); ``` ```go Go SDK theme={null} package main import ( "fmt" "io" "net/http" "os" "time" sdk "github.com/magichourhq/magic-hour-go/client" nullable "github.com/magichourhq/magic-hour-go/nullable" "github.com/magichourhq/magic-hour-go/resources/v1/ai_image_generator" "github.com/magichourhq/magic-hour-go/resources/v1/image_projects" "github.com/magichourhq/magic-hour-go/types" ) func main() { // Use environment variable for security client := sdk.NewClient(sdk.WithBearerAuth(os.Getenv("MAGIC_HOUR_API_KEY"))) createRes, err := client.V1.AiImageGenerator.Create(ai_image_generator.CreateRequest{ ImageCount: 1, AspectRatio: nullable.NewValue(types.V1AiImageGeneratorCreateBodyAspectRatioEnum169), Resolution: nullable.NewValue(types.V1AiImageGeneratorCreateBodyResolutionEnum1k), Style: types.V1AiImageGeneratorCreateBodyStyle{ Prompt: "Epic anime art of wizard casting a cosmic spell in the sky that says 'Magic Hour'", }, }) if err != nil { fmt.Println(err) return } fmt.Printf("queued image with id %s, spent %d credits\n", createRes.Id, createRes.CreditsCharged) for { res, err := client.V1.ImageProjects.Get(image_projects.GetRequest{Id: createRes.Id}) if err != nil { fmt.Println(err) return } if res.Status == "complete" { println("render complete!") url := res.Downloads[0].Url outputFile := "output.png" resp, err := http.Get(url) if err != nil { fmt.Println(err) return } defer resp.Body.Close() out, err := os.Create(outputFile) if err != nil { fmt.Println(err) return } defer out.Close() _, err = io.Copy(out, resp.Body) if err != nil { fmt.Println(err) return } fmt.Printf("file downloaded successfully to %s\n", outputFile) break } else if res.Status == "error" { println("render failed") break } else { fmt.Printf("render in progress: %s\n", res.Status) time.Sleep(1 * time.Second) } } } ``` ```rust Rust SDK theme={null} use magic_hour; use reqwest; use std::io::Read; #[tokio::main] async fn main() { // Use environment variable for security let api_key = std::env::var("MAGIC_HOUR_API_KEY") .expect("MAGIC_HOUR_API_KEY environment variable not set"); let mut client = magic_hour::Client::default().with_bearer_auth(&api_key); let create_res = client .v1() .ai_image_generator() .create(magic_hour::resources::v1::ai_image_generator::CreateRequest { image_count: 1, aspect_ratio: Some(magic_hour::models::V1AiImageGeneratorCreateBodyAspectRatioEnum::Enum169), resolution: Some(magic_hour::models::V1AiImageGeneratorCreateBodyResolutionEnum::Enum1k), style: magic_hour::models::V1AiImageGeneratorCreateBodyStyle { prompt: "Epic anime art of wizard casting a cosmic spell in the sky that says 'Magic Hour'".to_string(), ..Default::default() }, ..Default::default() }) .await .unwrap(); let project_id = create_res.id; let credits_charged = create_res.credits_charged; println!("queued image with id {project_id}, spent {credits_charged} credits"); loop { let res = client .v1() .image_projects() .get(magic_hour::resources::v1::image_projects::GetRequest { id: project_id.clone(), }) .await .unwrap(); match res.status { magic_hour::models::V1ImageProjectsGetResponseStatusEnum::Complete => { println!("render complete!"); let url = res.downloads[0].url.clone(); tokio::task::block_in_place(move || { let response = reqwest::blocking::get(url).unwrap(); let output_path = "output.png"; if response.status().is_success() { let mut output_file = std::fs::File::create(output_path).unwrap(); std::io::copy(&mut response, &mut output_file).unwrap(); println!("file downloaded successfully to {}", output_path); } else { println!("failed to download file: {}", response.status()); } }); return; } magic_hour::models::V1ImageProjectsGetResponseStatusEnum::Error => { println!("render failed"); return; } _ => { println!("render in progress: {}", res.status); std::thread::sleep(std::time::Duration::from_secs(1)); } } } } ``` ```sh cURL theme={null} #!/bin/bash set -e URL="https://api.magichour.ai/v1/ai-image-generator" STATUS_URL="https://api.magichour.ai/v1/image-projects" # Use environment variable for security API_KEY="$MAGIC_HOUR_API_KEY" OUTPUT_PATH="output.png" create_response=$(curl -s $URL \ --request POST \ --header "Content-Type: application/json" \ --header "Authorization: Bearer $API_KEY" \ --data '{ "image_count": 1, "aspect_ratio": "16:9", "resolution": "1k", "style": { "prompt": "Epic anime art of wizard casting a cosmic spell in the sky that says \"Magic Hour\"" } }') project_id=$(echo $create_response | jq -r '.id') credits_charged=$(echo $create_response | jq -r '.credits_charged') echo "queued image with id $project_id, spent $credits_charged credits" while true; do status_response=$(curl -s $STATUS_URL/$project_id --header "Authorization: Bearer $API_KEY") status=$(echo $status_response | jq -r '.status') if [ "$status" == "complete" ]; then echo "render complete!" download_url=$(echo $status_response | jq -r '.downloads[0].url') echo "downloading image from $download_url..." curl -s $download_url -o $OUTPUT_PATH echo "file downloaded successfully to $OUTPUT_PATH" break elif [ "$status" == "error" ]; then echo "render failed" break else echo "render in progress" sleep 1 fi done ``` **Expected output for Python and Node.js:** ``` created image with id clx1234567890, spent 5 credits. Outputs are saved at ['./output-0.png'] ``` **Expected output for Go, Rust, and cURL:** ``` queued image with id clx1234567890, spent 5 credits render complete! file downloaded successfully to output.png ``` Swap a face into a video clip. This is our most popular API path. The sample uses a 6.2-second segment (`start_seconds` to `end_seconds`). Video pricing is duration-based, so longer clips cost more. ```python Python SDK theme={null} from magic_hour import Client import os # Use environment variable for security client = Client(token=os.getenv("MAGIC_HOUR_API_KEY")) result = client.v1.face_swap.generate( name="Swap Tom Cruise into Iron Man scene", assets={ "image_file_path": "https://videos.magichour.ai/api-assets/sample/tom-cruise.png", "video_file_path": "https://videos.magichour.ai/api-assets/sample/iron-man.mp4", "video_source": "file", }, start_seconds=2.3, end_seconds=8.5, wait_for_completion=True, # wait for the render to complete download_outputs=True, # download the outputs to local disk download_directory=".", # save the outputs to the current directory ) print(f"created face swap video with id {result.id}, spent {result.credits_charged} credits. Outputs are saved at {result.downloaded_paths}") ``` ```typescript Node SDK theme={null} import { Client } from "magic-hour"; // Use environment variable for security const client = new Client({ token: process.env.MAGIC_HOUR_API_KEY }); async function main() { const createRes = await client.v1.faceSwap.generate( { name: "Swap Tom Cruise into Iron Man scene", assets: { imageFilePath: "https://videos.magichour.ai/api-assets/sample/tom-cruise.png", videoFilePath: "https://videos.magichour.ai/api-assets/sample/iron-man.mp4", videoSource: "file", }, startSeconds: 2.3, endSeconds: 8.5, }, { waitForCompletion: true, // wait for the render to complete downloadOutputs: true, // download the outputs to local disk downloadDirectory: ".", // save the outputs to the current directory } ); console.log( `created face swap video with id ${createRes.id}, spent ${createRes.creditsCharged} credits. Outputs are saved at ${createRes.downloadedPaths}` ); } main(); ``` ```go Go SDK theme={null} package main import ( "fmt" "io" "net/http" "os" "time" sdk "github.com/magichourhq/magic-hour-go/client" nullable "github.com/magichourhq/magic-hour-go/nullable" "github.com/magichourhq/magic-hour-go/resources/v1/face_swap" "github.com/magichourhq/magic-hour-go/resources/v1/video_projects" "github.com/magichourhq/magic-hour-go/types" ) func main() { // Use environment variable for security client := sdk.NewClient(sdk.WithBearerAuth(os.Getenv("MAGIC_HOUR_API_KEY"))) createRes, err := client.V1.FaceSwap.Create(face_swap.CreateRequest{ Name: nullable.NewValue("Swap Tom Cruise into Iron Man scene"), Assets: types.V1FaceSwapCreateBodyAssets{ ImageFilePath: "https://videos.magichour.ai/api-assets/sample/tom-cruise.png", VideoFilePath: nullable.NewValue("https://videos.magichour.ai/api-assets/sample/iron-man.mp4"), VideoSource: types.V1FaceSwapCreateBodyAssetsVideoSourceEnumFile, }, StartSeconds: 2.3, EndSeconds: 8.5, }) if err != nil { fmt.Println(err) return } fmt.Printf("queued video with id %s, spent %d credits based on 30fps. This value will be adjusted after render completes if fps is different.\n", createRes.Id, createRes.CreditsCharged) for { res, err := client.V1.VideoProjects.Get(video_projects.GetRequest{Id: createRes.Id}) if err != nil { fmt.Println(err) return } if res.Status == "complete" { fmt.Printf("render complete! Final credits charged is %d, actual fps is %f.\n", res.CreditsCharged, res.Fps) url := res.Downloads[0].Url outputFile := "output.mp4" resp, err := http.Get(url) if err != nil { fmt.Println(err) return } defer resp.Body.Close() out, err := os.Create(outputFile) if err != nil { fmt.Println(err) return } defer out.Close() _, err = io.Copy(out, resp.Body) if err != nil { fmt.Println(err) return } fmt.Printf("file downloaded successfully to %s\n", outputFile) break } else if res.Status == "error" { println("render failed") break } else { fmt.Printf("render in progress: %s\n", res.Status) time.Sleep(1 * time.Second) } } } ``` ```rust Rust SDK theme={null} use magic_hour; use reqwest; #[tokio::main] async fn main() { // Use environment variable for security let api_key = std::env::var("MAGIC_HOUR_API_KEY") .expect("MAGIC_HOUR_API_KEY environment variable not set"); let mut client = magic_hour::Client::default().with_bearer_auth(&api_key); let create_res = client .v1() .face_swap() .create(magic_hour::resources::v1::face_swap::CreateRequest { name: Some("Swap Tom Cruise into Iron Man scene".to_string()), assets: magic_hour::models::V1FaceSwapCreateBodyAssets { image_file_path: "https://videos.magichour.ai/api-assets/sample/tom-cruise.png" .to_string(), video_file_path: Some( "https://videos.magichour.ai/api-assets/sample/iron-man.mp4".to_string(), ), video_source: magic_hour::models::V1FaceSwapCreateBodyAssetsVideoSourceEnum::File, ..Default::default() }, start_seconds: 2.3, end_seconds: 8.5, ..Default::default() }) .await .unwrap(); let project_id = create_res.id; let credits_charged = create_res.credits_charged; println!("queued face swap video with id {project_id}, spent {credits_charged} credits based on 30fps. This value will be updated after render completes if fps is different."); loop { let res = client .v1() .video_projects() .get(magic_hour::resources::v1::video_projects::GetRequest { id: project_id.clone(), }) .await .unwrap(); match res.status { magic_hour::models::V1VideoProjectsGetResponseStatusEnum::Complete => { println!( "render complete! Final credit charged is {}, actual fps is {}", res.credits_charged, res.fps ); let url = res.downloads[0].url.clone(); tokio::task::block_in_place(move || { let response = reqwest::blocking::get(url).unwrap(); let output_path = "output.mp4"; if response.status().is_success() { let mut output_file = std::fs::File::create(output_path).unwrap(); std::io::copy(&mut response, &mut output_file).unwrap(); println!("file downloaded successfully to {}", output_path); } else { println!("failed to download file: {}", response.status()); } }); return; } magic_hour::models::V1VideoProjectsGetResponseStatusEnum::Error => { println!("render failed"); return; } _ => { println!("render in progress: {}", res.status); std::thread::sleep(std::time::Duration::from_secs(1)); } } } } ``` ```sh cURL theme={null} #!/bin/bash set -e URL="https://api.magichour.ai/v1/face-swap" STATUS_URL="https://api.magichour.ai/v1/video-projects" # Use environment variable for security API_KEY="$MAGIC_HOUR_API_KEY" OUTPUT_PATH="output.mp4" create_response=$(curl -s $URL \ --request POST \ --header "Content-Type: application/json" \ --header "Authorization: Bearer $API_KEY" \ --data '{ "name": "Swap Tom Cruise into Iron Man scene", "assets": { "image_file_path": "https://videos.magichour.ai/api-assets/sample/tom-cruise.png", "video_file_path": "https://videos.magichour.ai/api-assets/sample/iron-man.mp4", "video_source": "file" }, "start_seconds": 2.3, "end_seconds": 8.5 }') project_id=$(echo $create_response | jq -r '.id') credits_charged=$(echo $create_response | jq -r '.credits_charged') echo "queued face swap video with id $project_id, spent an estimated $credits_charged credits based on 30fps. This value will be adjusted after render completes if fps is different." while true; do status_response=$(curl -s $STATUS_URL/$project_id --header "Authorization: Bearer $API_KEY") status=$(echo $status_response | jq -r '.status') if [ "$status" == "complete" ]; then credits_charged=$(echo $status_response | jq -r '.credits_charged') fps=$(echo $status_response | jq -r '.fps') download_url=$(echo $status_response | jq -r '.downloads[0].url') echo "render complete! Final credit charged is $credits_charged, actual fps is $fps." echo "downloading video from $download_url..." curl -s $download_url -o $OUTPUT_PATH echo "file downloaded successfully to $OUTPUT_PATH" break elif [ "$status" == "error" ]; then echo "render failed" break else echo "render in progress: $status" sleep 3 fi done ``` **Expected output for Python and Node.js:** ``` created face swap video with id clx1234567890, spent 186 credits. Outputs are saved at ['./output.mp4'] ``` 2. **Paste it into your file** (`main.py`, `main.js`, `main.go`, etc.) 3. **Run the code:** ```bash Python theme={null} # Make sure you're in your project directory cd magic-hour-quickstart # Run the script python main.py ``` ```bash Node.js theme={null} # Make sure you're in your project directory cd magic-hour-quickstart # Run the script node main.js ``` ```bash Go theme={null} # Make sure you're in your project directory cd magic-hour-quickstart # Run the program go run main.go ``` ```bash Rust theme={null} # Make sure you're in your project directory cd magic-hour-quickstart # Run the program cargo run ``` ## Troubleshooting ### Common Issues **FileNotFoundError when using a custom `download_directory`** The SDK saves outputs into `download_directory` but does **not** create the folder for you. The default (`"."`) always works. If you point it at a folder that doesn't exist yet (e.g. `"outputs"`), create it first: ```bash theme={null} mkdir outputs ``` **Error: MAGIC\_HOUR\_API\_KEY environment variable not set** **Solution:** Set your API key as an environment variable. See [Environment Variables](/get-started/authentication#environment-variables) in the Authentication guide for platform-specific setup steps. ### HTTP Error Codes If you encounter HTTP errors, here's what they mean: | Error Code | Meaning | Solution | | :--------- | :-------------------- | :-------------------------------------------------------------------------------------------------------------------------------------- | | `400` | Bad Request | The response `message` names the invalid or missing field - fix it and resubmit | | `401` | Unauthorized | Verify your API key is correct and sent as `Authorization: Bearer ` | | `402` | Payment Required | Insufficient credits - [add more credits](https://magichour.ai/dashboard/my-plan) | | `403` | Forbidden | Your key is valid but the model, resolution, or feature is not available on your plan | | `429` | Too Many Requests | You are sending requests too quickly - retry with exponential backoff | | `500` | Internal Server Error | Temporary server issue - retry after a few seconds | | `502` | Bad Gateway | Server temporarily unavailable - retry after 30-60 seconds. If it failed before returning a project `id`, no job was created - resubmit | | `503` | Service Unavailable | Server overloaded - retry with exponential backoff | **For persistent errors:** Contact [support@magichour.ai](mailto:support@magichour.ai) with your project ID. 🎉 Congratulations! You have successfully created your first Magic Hour project. ## Next Steps * **Explore the API Reference:** Learn how to generate and edit videos and images programmatically. [API Reference →](/api-reference/overview) * **Explore Face Swap Video:** See parameter details, pricing, and production patterns in the [Face Swap Video guide](/tools/video/face-swap-video). * **Try All APIs in Google Colab:** [Run our complete cookbook](https://colab.research.google.com/drive/1NTHL_lr_s-qBJ-mSecSXPzRLi9_V5JiU?usp=sharing) with ready-to-run sample code. Just add your API key and start experimenting. * **Use the Web App:** Try more tools and experiment interactively at [magichour.ai](https://magichour.ai). * **Handle Results at Scale:** Set up [webhooks](https://docs.magichour.ai/integration/webhook/overview) to process results async and avoid polling. * **Join the Community:** Get help, share projects, and see what others are building in [Discord](https://discord.gg/JX5rgsZaJp). * **Stay Updated:** Check out the [Changelog](https://docs.magichour.ai/changelog) for new products and API updates. Prefer fast iteration inside your editor? Install this documentation as an MCP server to get contextual help while integrating the Magic Hour API. [Learn more](/integration/model-context-protocol). # Adding API to Your App Source: https://docs.magichour.ai/integration/adding-api-to-your-app Complete guide to integrating Magic Hour APIs into your production application. This guide provides the essential concepts and patterns for integrating Magic Hour APIs into your production application. You'll learn the API workflow, project structure, and production-ready patterns. ## What you'll accomplish By the end of this guide, you'll have: * ✅ Secure API integration setup * ✅ Complete create → poll → download workflow * ✅ Robust error handling * ✅ Production-ready patterns ## Before you start Complete the [Quick Start Guide](/get-started/quick-start) first to get familiar with basic API calls. **Prerequisites:** * API key from [Developer Hub](https://magichour.ai/developer?tab=api-keys) * Your preferred SDK installed or direct HTTP client ready **Security:** Never expose your API key in client-side code. Always keep it secure on your server to prevent unauthorized usage. ## Integration overview Choose the workflow that matches what you are building: * **Start with `generate()`** for quick scripts and prototypes. It creates the job, polls for completion, and can download the output for you. * **Use `create()` with webhooks or background polling** for production apps. It returns immediately so your server can track multiple jobs without holding requests open. This guide uses the production `create()` workflow. If you are still learning the API, complete the [Quick Start](/get-started/quick-start) with `generate()` first. Magic Hour APIs follow a simple 3-step pattern that applies to all content generation: ### Step 1: Create Job **Purpose:** Submit your generation request to the API **Relevant APIs:** * **Video APIs:** `POST /v1/face-swap`, `POST /v1/lip-sync`, `POST /v1/animation`, `POST /v1/text-to-video`, etc. * **Image APIs:** `POST /v1/ai-image-generator`, `POST /v1/face-swap-photo`, `POST /v1/ai-headshot-generator`, etc. * **Audio APIs:** `POST /v1/ai-voice-generator` **Returns:** Project ID and initial status (`queued`) ```python Python SDK theme={null} from magic_hour import Client import os client = Client(token=os.getenv("MAGIC_HOUR_API_KEY")) # 1. Create job print("Creating face swap job...") create_res = client.v1.face_swap.create( name="My face swap", assets={ "image_file_path": "https://upload.wikimedia.org/wikipedia/commons/e/ec/Chris_Cassidy_-_Official_NASA_Astronaut_Portrait_in_EMU_%28cropped%29.jpg", "video_file_path": "https://svs.gsfc.nasa.gov/vis/a010000/a014300/a014327/john_bolten_no_graphics.mp4", "video_source": "file" }, start_seconds=0.0, end_seconds=10.0, ) print(f"Job created with ID: {create_res.id}, status: {create_res.status}") ``` ### Production application pattern **Best for:** Web apps, APIs, multiple concurrent jobs ```python theme={null} # Use create() + polling/webhooks for control create_res = client.v1.face_swap.create(...) job_id = create_res.id # Store job in database for tracking save_job_to_database(job_id, user_id, status="queued") # Use webhooks or background polling return {"job_id": job_id, "status": "processing"} ``` ## Step-by-step integration guide Let's build a complete face swap integration step by step: ### Step 1: Set up the API client **What this does:** Creates a configured Magic Hour client for making API calls ```python Python theme={null} # services/magichour.py from magic_hour import Client import os # Initialize client with environment variable client = Client(token=os.getenv("MAGIC_HOUR_API_KEY")) def get_client(): """Get configured Magic Hour client""" return client ``` ```typescript Node.js theme={null} // services/magichour.js import { Client } from "magic-hour"; // Initialize client with environment variable const client = new Client({ token: process.env.MAGIC_HOUR_API_KEY, }); export function getClient() { return client; } ``` ### Step 2: Create the job **What this does:** Submits a face swap request and gets a project ID to track progress ```python Python theme={null} # services/video_processor.py from .magichour import get_client def create_face_swap(face_image_url, video_url, start_time=0.0, end_time=10.0): """Create a face swap job and return project ID""" client = get_client() # Submit job to Magic Hour API create_res = client.v1.face_swap.create( name="Face swap job", assets={ "image_file_path": face_image_url, "video_file_path": video_url, "video_source": "file" }, start_seconds=start_time, end_seconds=end_time, ) return { "project_id": create_res.id, "status": create_res.status, "credits_charged": create_res.credits_charged } ``` ```typescript Node.js theme={null} // services/videoProcessor.js import { getClient } from "./magichour.js"; export async function createFaceSwap(faceImageUrl, videoUrl, startTime = 0.0, endTime = 10.0) { const client = getClient(); // Submit job to Magic Hour API const createRes = await client.v1.faceSwap.create({ name: "Face swap job", assets: { imageFilePath: "https://upload.wikimedia.org/wikipedia/commons/e/ec/Chris_Cassidy_-_Official_NASA_Astronaut_Portrait_in_EMU_%28cropped%29.jpg", videoFilePath: "https://svs.gsfc.nasa.gov/vis/a010000/a014300/a014327/john_bolten_no_graphics.mp4", videoSource: "file", }, startSeconds: startTime, endSeconds: endTime, }); return { projectId: createRes.id, status: createRes.status, creditsCharged: createRes.creditsCharged, }; } ``` ### Step 3: Monitor job status **What this does:** Checks if the job is complete and handles different status states ```python Python theme={null} def check_job_status(project_id): """Check the current status of a video job""" client = get_client() try: # Get current status from Magic Hour status_res = client.v1.video_projects.get(id=project_id) return { "project_id": project_id, "status": status_res.status, "downloads": status_res.downloads if status_res.status == "complete" else None, "error": status_res.error if status_res.status == "error" else None } except Exception as e: return { "project_id": project_id, "status": "error", "error": {"message": str(e)} } ``` ```typescript Node.js theme={null} export async function checkJobStatus(projectId) { const client = getClient(); try { // Get current status from Magic Hour const statusRes = await client.v1.videoProjects.get({ id: projectId }); return { projectId, status: statusRes.status, downloads: statusRes.status === "complete" ? statusRes.downloads : null, error: statusRes.status === "error" ? statusRes.error : null, }; } catch (error) { return { projectId, status: "error", error: { message: error.message }, }; } } ``` ### Step 4: Download results **What this does:** Downloads the generated video when the job completes successfully ```python Python theme={null} # services/file_manager.py import requests import os from pathlib import Path def download_video(download_info, output_dir="./downloads"): """Download completed video to local storage""" # Create output directory if it doesn't exist Path(output_dir).mkdir(exist_ok=True) # Generate unique filename filename = f"face_swap_{int(time.time())}.mp4" filepath = Path(output_dir) / filename try: # Download the video file response = requests.get(download_info["url"], stream=True, timeout=60) response.raise_for_status() # Save to disk with open(filepath, "wb") as file: for chunk in response.iter_content(chunk_size=8192): file.write(chunk) return { "success": True, "filepath": str(filepath), "size_bytes": filepath.stat().st_size } except Exception as e: return { "success": False, "error": str(e) } ``` ```typescript Node.js theme={null} // services/fileManager.js import fs from "fs"; import path from "path"; export async function downloadVideo(downloadInfo, outputDir = "./downloads") { // Create output directory if it doesn't exist if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } // Generate unique filename const filename = `face_swap_${Date.now()}.mp4`; const filepath = path.join(outputDir, filename); try { // Download the video file const response = await fetch(downloadInfo.url); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } // Save to disk const arrayBuffer = await response.arrayBuffer(); fs.writeFileSync(filepath, Buffer.from(arrayBuffer)); const stats = fs.statSync(filepath); return { success: true, filepath, sizeBytes: stats.size, }; } catch (error) { return { success: false, error: error.message, }; } } ``` ### Step 5: Put it all together **What this does:** Combines all steps into a complete workflow function ```python Python theme={null} # main.py - Complete workflow from services.video_processor import create_face_swap, check_job_status from services.file_manager import download_video import time def process_face_swap(face_image_url, video_url): """Complete face swap workflow""" print("🚀 Starting face swap...") # Step 1: Create the job job = create_face_swap(face_image_url, video_url) print(f"✅ Job created: {job['project_id']} (Cost: {job['credits_charged']} credits)") # Step 2: Wait for completion print("⏳ Waiting for completion...") while True: status = check_job_status(job['project_id']) print(f"Status: {status['status']}") if status['status'] == 'complete': print("🎉 Job completed successfully!") # Step 3: Download result result = download_video(status['downloads'][0]) if result['success']: print(f"📁 Video downloaded: {result['filepath']}") return result['filepath'] else: print(f"❌ Download failed: {result['error']}") return None elif status['status'] == 'error': print(f"❌ Job failed: {status['error']}") return None time.sleep(5) # Wait 5 seconds before checking again # Usage if __name__ == "__main__": result = process_face_swap( "https://example.com/face.jpg", "https://example.com/video.mp4" ) ``` ```typescript Node.js theme={null} // main.js - Complete workflow import { createFaceSwap, checkJobStatus } from "./services/videoProcessor.js"; import { downloadVideo } from "./services/fileManager.js"; export async function processFaceSwap(faceImageUrl, videoUrl) { console.log("🚀 Starting face swap..."); // Step 1: Create the job const job = await createFaceSwap(faceImageUrl, videoUrl); console.log(`✅ Job created: ${job.projectId} (Cost: ${job.creditsCharged} credits)`); // Step 2: Wait for completion console.log("⏳ Waiting for completion..."); while (true) { const status = await checkJobStatus(job.projectId); console.log(`Status: ${status.status}`); if (status.status === "complete") { console.log("🎉 Job completed successfully!"); // Step 3: Download result const result = await downloadVideo(status.downloads[0]); if (result.success) { console.log(`📁 Video downloaded: ${result.filepath}`); return result.filepath; } else { console.log(`❌ Download failed: ${result.error}`); return null; } } else if (status.status === "error") { console.log(`❌ Job failed: ${status.error}`); return null; } await new Promise((resolve) => setTimeout(resolve, 5000)); // Wait 5 seconds } } // Usage processFaceSwap("https://example.com/face.jpg", "https://example.com/video.mp4").then((result) => { console.log("Final result:", result); }); ``` ## Working with input files Magic Hour accepts input files in two ways: ### Option 1: URL references (Recommended) The simplest approach is to pass file URLs: ```json theme={null} { "assets": { "image_file_path": "https://upload.wikimedia.org/wikipedia/commons/e/ec/Chris_Cassidy_-_Official_NASA_Astronaut_Portrait_in_EMU_%28cropped%29.jpg", "video_file_path": "https://svs.gsfc.nasa.gov/vis/a010000/a014300/a014327/john_bolten_no_graphics.mp4" } } ``` **Supported formats:** * **Images**: PNG, JPG, JPEG, JFIF, HEIC, HEIF, WEBP, AVIF, JP2, TIFF, TIF, BMP * **Videos**: MP4, M4V, MOV, WEBM * **Audio**: MP3, WAV, AAC, FLAC, WEBM, WEBA, M4A, OPUS, OGG, OGA, AIFF, AMR See [Handling Inputs and Outputs](/integration/inputs-and-outputs#supported-file-formats) for the canonical format list and special-case support. ### Option 2: Upload to Magic Hour For secure or temporary files, upload directly to Magic Hour storage: Complete guide to file uploads, formats, and storage options ## Understanding job status Every job goes through these states: | Status | Description | Action Required | | :---------- | :---------------------------------- | :---------------- | | `queued` | Job is waiting for available server | ⏳ Keep polling | | `rendering` | Job is being processed | ⏳ Keep polling | | `complete` | Job finished successfully | ✅ Download result | | `error` | Job failed during processing | ❌ Handle error | | `canceled` | Job was manually canceled | 🛑 Job stopped | Recommended polling intervals: * **Images**: Check every 2-3 seconds (usually complete within 30-60 seconds) * **Videos**: Check every 5-10 seconds (can take 2-5 minutes depending on length) ## Error handling When a job fails (`status: "error"`), the response includes detailed error information: ```json theme={null} { "status": "error", "error": { "code": "no_source_face", "message": "Please use an image with a detectable face" } } ``` ### Error handling example ```python Python SDK theme={null} try: create_res = client.v1.face_swap.create(...) # Poll with error handling while True: status_res = client.v1.video_projects.get(id=create_res.id) if status_res.status == "complete": # Success - download result break elif status_res.status == "error": error_code = status_res.error.get("code", "unknown") error_msg = status_res.error.get("message", "Unknown error") # Handle specific errors if error_code == "no_source_face": print("❌ No face detected. Please use a different image.") else: print(f"❌ Error ({error_code}): {error_msg}") break time.sleep(5) except Exception as e: print(f"❌ Request failed: {e}") ``` ```typescript Node SDK theme={null} try { const createRes = await client.v1.faceSwap.create({...}); // Poll with error handling while (true) { const statusRes = await client.v1.videoProjects.get({ id: createRes.id }); if (statusRes.status === "complete") { // Success - download result break; } else if (statusRes.status === "error") { const errorCode = statusRes.error?.code || "unknown"; const errorMsg = statusRes.error?.message || "Unknown error"; // Handle specific errors switch (errorCode) { case "no_source_face": console.log("❌ No face detected. Please use a different image."); break; default: console.log(`❌ Error (${errorCode}): ${errorMsg}`); } break; } await new Promise(resolve => setTimeout(resolve, 5000)); } } catch (error) { console.log(`❌ Request failed: ${error}`); } ``` For `unknown_error` codes, contact [support@magichour.ai](mailto:support@magichour.ai) with your project ID for investigation. ## Status monitoring strategies Choose the right approach based on your application's needs: ### Option 1: Webhooks (Recommended for production) Get real-time notifications when jobs complete. Best for: * ✅ Production applications * ✅ Video processing (longer render times) * ✅ Multiple concurrent jobs * ✅ Better server resource usage Complete webhook implementation guide with examples ### Option 2: Polling (Good for simple use cases) Periodically check job status. Best for: * ✅ Simple integrations * ✅ Single job processing * ✅ Image generation (quick results) **Smart polling example:** ```python Python SDK theme={null} import time def wait_for_completion(client, project_id, project_type="video", timeout_seconds=1800): """Poll with exponential backoff (3s -> 30s max) and a hard timeout.""" get_method = (client.v1.video_projects.get if project_type == "video" else client.v1.image_projects.get) deadline = time.monotonic() + timeout_seconds delay = 3.0 while time.monotonic() < deadline: res = get_method(id=project_id) print(f"Status: {res.status}") if res.status == "complete": return res # Success! if res.status in ("error", "canceled"): raise RuntimeError(f"Job {res.status}: {res.error}") time.sleep(delay) delay = min(delay * 1.5, 30.0) # back off up to 30s between checks raise TimeoutError(f"Job did not complete within {timeout_seconds}s") ``` ```typescript Node SDK theme={null} async function waitForCompletion( client: Client, projectId: string, projectType: "video" | "image" = "video", timeoutMs = 30 * 60 * 1000 ) { const deadline = Date.now() + timeoutMs; let delay = 3000; while (Date.now() < deadline) { const res = projectType === "video" ? await client.v1.videoProjects.get({ id: projectId }) : await client.v1.imageProjects.get({ id: projectId }); console.log(`Status: ${res.status}`); if (res.status === "complete") { return res; // Success! } if (res.status === "error" || res.status === "canceled") { throw new Error(`Job ${res.status}: ${JSON.stringify(res.error)}`); } await new Promise((resolve) => setTimeout(resolve, delay)); delay = Math.min(delay * 1.5, 30_000); // back off up to 30s between checks } throw new Error(`Job did not complete within ${timeoutMs}ms`); } ``` ## Downloading results When a job completes, the `downloads` array is populated with secure, time-limited URLs: ```json theme={null} { "status": "complete", "downloads": [ { "url": "https://video.magichour.ai/id/output.mp4?auth-token=1234", "expires_at": "2024-10-19T05:16:19.027Z" } ] } ``` Download URLs expire after 24 hours. Download files immediately after job completion. ### Download outputs ```python Python SDK theme={null} import requests import os from pathlib import Path def download_result(download_info, output_dir="./downloads"): # Create output directory Path(output_dir).mkdir(exist_ok=True) # Generate filename from URL or use timestamp url = download_info["url"] filename = f"result_{int(time.time())}.mp4" # or extract from URL filepath = Path(output_dir) / filename try: print(f"Downloading to {filepath}...") response = requests.get(url, stream=True, timeout=60) response.raise_for_status() # Stream download for large files with open(filepath, "wb") as file: for chunk in response.iter_content(chunk_size=8192): file.write(chunk) print(f"✅ Downloaded: {filepath} ({filepath.stat().st_size} bytes)") return str(filepath) except requests.exceptions.RequestException as e: print(f"❌ Download failed: {e}") return None ``` ```typescript Node SDK theme={null} import fs from "fs"; import path from "path"; async function downloadResult(downloadInfo: any, outputDir = "./downloads") { // Create output directory if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } // Generate filename const filename = `result_${Date.now()}.mp4`; const filepath = path.join(outputDir, filename); try { console.log(`Downloading to ${filepath}...`); const response = await fetch(downloadInfo.url); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } // Stream download for large files const arrayBuffer = await response.arrayBuffer(); fs.writeFileSync(filepath, Buffer.from(arrayBuffer)); const stats = fs.statSync(filepath); console.log(`✅ Downloaded: ${filepath} (${stats.size} bytes)`); return filepath; } catch (error) { console.log(`❌ Download failed: ${error}`); return null; } } ``` ### Multiple outputs handling Some tools generate multiple files (e.g., multiple images): ```python Python SDK theme={null} # Handle multiple downloads for i, download in enumerate(status_res.downloads): filename = f"output_{i+1}.{'mp4' if 'video' in download.url else 'jpg'}" download_result(download, filename) ``` ```typescript Node SDK theme={null} for (const [i, download] of statusRes.downloads.entries()) { const filename = `output_${i + 1}.${download.url.includes("video") ? "mp4" : "jpg"}`; downloadResult(download, filename); } ``` ## File management ### Cleaning up storage Generated files are stored indefinitely. Clean up completed jobs to manage storage: ```python Python SDK theme={null} client.v1.video_projects.delete(id="cuid") ``` ```typescript Node SDK theme={null} await client.v1.videoProjects.delete({ id: "cuid" }); ``` Deletion is permanent and cannot be undone. Only delete after confirming successful download. ## Development and testing ### Free testing with mock server Avoid credit charges during development by using the mock API server: The mock server returns realistic sample data without processing jobs or charging credits. ```python Python SDK theme={null} from magic_hour import Client from magic_hour.environment import Environment # Use mock server for development client = Client( token="YOUR_API_KEY", # Can use any value for mock mode environment=Environment.MOCK_SERVER ) # All API calls will return mock data result = client.v1.face_swap.create(...) # No credits charged ``` ```typescript Node SDK theme={null} import Client, { Environment } from "magic-hour"; // Use mock server for development const client = new Client({ token: "YOUR_API_KEY", // Can use any value for mock mode environment: Environment.MockServer, }); // All API calls will return mock data const result = await client.v1.faceSwap.create({...}); // No credits charged ``` ### Job cancellation Cancel video jobs (with full refund) via the web dashboard: Visit the project in your library: - **Videos**: `https://magichour.ai/my-library?videoId= {project_id}` - **Images**: `https://magichour.ai/my-library?imageId={project_id}` - **Audio**: `https://magichour.ai/my-library?audioId={project_id}` Cancel Render Button Confirm Cancel Button Cancel Success Notes: * Image jobs cannot be cancelled (they complete too quickly) * API-based cancellation is not currently available * Full credit refund is provided for cancelled video jobs ## Next steps Set up real-time notifications for production apps Explore all available endpoints and parameters Try the Magic Hour APIs with ready-to-run examples Advanced file upload and management techniques *** **Need help?** Join our [Discord community](https://discord.com/invite/JX5rgsZaJp) or email [support@magichour.ai](mailto:support@magichour.ai) # Development and Testing Source: https://docs.magichour.ai/integration/development-and-testing Best practices for developing and testing your Magic Hour API integration without consuming credits. ## Overview Developing with APIs that charge per request requires careful testing strategies. This guide shows you how to build and test your Magic Hour integration effectively while minimizing costs and avoiding production issues. ## Testing Without Using Credits ### Mock Server (Recommended for Development) The Magic Hour SDKs include a mock server that returns realistic sample data without processing jobs or charging credits: ```python Python SDK theme={null} from magic_hour import Client from magic_hour.environment import Environment # Use mock server for development client = Client( token="YOUR_API_KEY", # Can use any value for mock mode environment=Environment.MOCK_SERVER ) # All API calls return mock data instantly result = client.v1.ai_image_generator.create( image_count=1, aspect_ratio="16:9", resolution="1k", style={"prompt": "Test image", "tool": "ai-anime-generator"} ) print(f"Job ID: {result.id}") # Returns mock ID # No credits charged, no actual processing ``` ```typescript Node.js SDK theme={null} import Client, { Environment } from "magic-hour"; // Use mock server for development const client = new Client({ token: "YOUR_API_KEY", // Can use any value for mock mode environment: Environment.MockServer, }); // All API calls return mock data instantly const result = await client.v1.aiImageGenerator.create({ imageCount: 1, aspectRatio: "16:9", resolution: "1k", style: { prompt: "Test image", tool: "ai-anime-generator" }, }); console.log(`Job ID: ${result.id}`); // Returns mock ID // No credits charged, no actual processing ``` **`style.tool` is optional.** It selects an art-style preset; `ai-anime-generator` requests an anime look. Omit it to use the default `general` style. **Benefits:** * ✅ No credit consumption * ✅ Instant responses (no waiting) * ✅ Realistic sample data * ✅ Test error scenarios * ✅ Validate integration logic **When to use:** * Unit testing * Integration testing * Local development * CI/CD pipelines * Prototyping new features ### Environment Configuration Use environment variables to switch between mock and production: ```python Python theme={null} import os from magic_hour import Client from magic_hour.environment import Environment # Switch environments based on ENV variable # Environment.ENVIRONMENT is the production API (https://api.magichour.ai) env = Environment.MOCK_SERVER if os.getenv("ENV") == "development" else Environment.ENVIRONMENT client = Client( token=os.getenv("MAGIC_HOUR_API_KEY"), environment=env ) ``` ```javascript Node.js theme={null} import Client, { Environment } from "magic-hour"; // Switch environments based on NODE_ENV // Environment.Environment is the production API (https://api.magichour.ai) const environment = process.env.NODE_ENV === "development" ? Environment.MockServer : Environment.Environment; const client = new Client({ token: process.env.MAGIC_HOUR_API_KEY, environment, }); ``` **Development Workflow**: Use mock server during development, then switch to production environment for final testing and deployment. ## Error Handling Best Practices ### Handling API Errors Implement comprehensive error handling for all API calls: ```python Python theme={null} import httpx from magic_hour import Client client = Client(token="YOUR_API_KEY") try: result = client.v1.ai_image_generator.create( image_count=1, aspect_ratio="16:9", resolution="1k", style={"prompt": "Test"}, ) except httpx.HTTPStatusError as e: code = e.response.status_code detail = e.response.text[:300] if code == 401: print("❌ Invalid API key. Check your credentials.") elif code == 403: print("❌ Forbidden. Key lacks permission or resource is protected.") elif code == 422: print(f"❌ Invalid parameters: {detail}") elif code == 429: print("❌ Rate limit exceeded. Wait before retrying.") else: print(f"❌ API error ({code}): {detail}") except Exception as e: print(f"❌ Unexpected error: {e}") ``` ```javascript Node.js theme={null} import { Client } from "magic-hour"; const client = new Client({ token: "YOUR_API_KEY" }); try { const result = await client.v1.aiImageGenerator.create({ imageCount: 1, aspectRatio: "16:9", resolution: "1k", style: { prompt: "Test" }, }); } catch (error) { if (error.statusCode === 401) { console.log("❌ Invalid API key. Check your credentials."); } else if (error.statusCode === 422) { console.log(`❌ Invalid parameters: ${error.message}`); // Fix your request parameters } else if (error.statusCode === 429) { console.log("❌ Rate limit exceeded. Wait before retrying."); // Implement exponential backoff } else { console.log(`❌ API error: ${error.message}`); // Log error and alert monitoring system } } ``` ### Handling Job Errors Check for errors during job processing: ```python Python theme={null} import time download_url = None job_id = "your_job_id" # Change this to your job ID while True: status = client.v1.image_projects.get(id=job_id) if status.status == "complete": download_url = status.downloads[0].url break if status.status == "error": err = status.error or {} error_code = err.get("code", "unknown") error_msg = err.get("message", "Unknown error") if error_code == "no_source_face": print("❌ No face detected. Use an image with a visible face.") elif error_code == "invalid_file_format": print("❌ Unsupported file format. Check supported formats.") elif error_code == "file_too_large": print("❌ File too large. Reduce file size or upgrade tier.") elif error_code == "insufficient_credits": print("❌ Not enough credits. Add credits to your account.") else: print(f"❌ Error ({error_code}): {error_msg}") break time.sleep(3) if not download_url: raise RuntimeError("Job did not complete successfully; no download URL available.") ``` ```javascript Node.js theme={null} // Poll for completion with error handling while (true) { const status = await client.v1.imageProjects.get({ id: jobId }); if (status.status === "complete") { // Success - download result const downloadUrl = status.downloads[0].url; break; } else if (status.status === "error") { // Job failed during processing const errorCode = status.error?.code || "unknown"; const errorMsg = status.error?.message || "Unknown error"; // Handle specific error codes switch (errorCode) { case "no_source_face": console.log("❌ No face detected. Use an image with a visible face."); break; case "invalid_file_format": console.log("❌ Unsupported file format. Check supported formats."); break; case "file_too_large": console.log("❌ File too large. Reduce file size or upgrade tier."); break; case "insufficient_credits": console.log("❌ Not enough credits. Add credits to your account."); break; default: console.log(`❌ Error (${errorCode}): ${errorMsg}`); } return null; } await new Promise((resolve) => setTimeout(resolve, 3000)); } ``` ### Common Error Codes | Error Code | Meaning | Solution | | :--------------------- | :------------------------- | :----------------------------------- | | `no_source_face` | No face detected in image | Use image with clear, visible face | | `invalid_file_format` | Unsupported file type | Check supported formats list | | `file_too_large` | File exceeds size limit | Compress file or upgrade tier | | `insufficient_credits` | Not enough credits | Add credits to account | | `invalid_parameters` | Invalid request parameters | Check API reference for valid values | | `unknown_error` | Unexpected error | Contact support with job ID | **Credits Not Charged**: When a job fails with an error, you are never charged credits. ## Status Monitoring Strategies ### Polling with Smart Intervals Adjust polling frequency based on content type: ```python theme={null} def get_poll_interval(project_type, duration=None): """Return appropriate polling interval in seconds""" if project_type == "image": return 3 # Images process quickly elif project_type == "audio": return 3 # Audio processes quickly elif project_type == "video": if duration and duration > 30: return 10 # Longer videos need more time return 5 # Short videos return 5 # Default # Usage poll_interval = get_poll_interval("video", duration=60) time.sleep(poll_interval) ``` ### Implementing Timeouts Always implement maximum wait times: ```python theme={null} import time from datetime import datetime, timedelta def wait_for_completion(client, job_id, timeout_minutes=10): """Wait for job with timeout""" start_time = datetime.now() timeout = timedelta(minutes=timeout_minutes) while datetime.now() - start_time < timeout: status = client.v1.image_projects.get(id=job_id) if status.status == "complete": return status elif status.status == "error": raise Exception(f"Job failed: {status.error}") time.sleep(3) raise TimeoutError(f"Job did not complete within {timeout_minutes} minutes") ``` ## Testing Strategies ### Unit Testing Test your integration logic without hitting the API: ```python theme={null} import unittest from unittest.mock import Mock, patch class TestMagicHourIntegration(unittest.TestCase): @patch('magic_hour.Client') def test_successful_image_generation(self, mock_client): # Mock the API responses mock_create = Mock(return_value=Mock(id="test123", credits_charged=5)) mock_status = Mock(return_value=Mock( status="complete", downloads=[Mock(url="https://upload.wikimedia.org/wikipedia/commons/e/ec/Chris_Cassidy_-_Official_NASA_Astronaut_Portrait_in_EMU_%28cropped%29.jpg")] )) mock_client.v1.ai_image_generator.create = mock_create mock_client.v1.image_projects.get = mock_status # Test your integration function result = generate_image(mock_client, "test prompt") # Verify calls were made mock_create.assert_called_once() mock_status.assert_called_once() # Verify result self.assertEqual(result.id, "test123") ``` ### Integration Testing with Mock Server ```python theme={null} from magic_hour import Client from magic_hour.environment import Environment def test_with_mock_server(): client = Client( token="test-key", environment=Environment.MOCK_SERVER ) # Test the full workflow result = client.v1.ai_image_generator.create(...) assert result.id is not None assert result.credits_charged > 0 # Test status checking status = client.v1.image_projects.get(id=result.id) assert status.status in ["queued", "rendering", "complete"] ``` ### Production Testing Before going live, test with minimal credits: 1. **Use Free Tier Credits**: Test with daily free credits 2. **Small Test Jobs**: Use minimum resolution/duration 3. **Single Job Tests**: Test one job at a time initially 4. **Monitor Credits**: Track credit usage during testing ```python theme={null} # Minimal credit test result = client.v1.ai_image_generator.create( image_count=1, aspect_ratio="1:1", resolution="1k", style={"prompt": "Simple test"} ) # Uses ~5 credits ``` ## Development Workflow ### Recommended Development Process **Phase 1: Local Development** ```bash theme={null} ENV=development python main.py # Uses mock server ``` * Use mock server exclusively * Build integration logic * Test error handling **Phase 2: Real-API Testing** ```bash theme={null} python main.py # Any ENV value other than "development" uses the real API ``` * Test with the real API using minimal credits (smallest resolution and duration) * Validate end-to-end workflow * Test error scenarios **Phase 3: Production** ```bash theme={null} python main.py # Uses your production API key ``` * Deploy with production credentials * Monitor for errors * Set up logging and alerts ### Logging and Debugging Implement comprehensive logging: ```python theme={null} import logging logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", handlers=[logging.FileHandler("magichour.log"), logging.StreamHandler()], ) logger = logging.getLogger(__name__) logger.info("Creating AI image job", extra={"aspect_ratio": "16:9", "image_count": 1}) result = client.v1.ai_image_generator.create( image_count=1, aspect_ratio="16:9", resolution="1k", style={"prompt": "Test"}, name="Logging Example", ) logger.info("Job created", extra={"job_id": result.id, "credits_charged": result.credits_charged}) print("job_id:", result.id) ``` ## Job Cancellation Cancel video jobs to get full credit refunds (image jobs complete too quickly to cancel): Visit the project in your library: - **Videos**: `https://magichour.ai/my-library?videoId= {project_id}` - **Images**: `https://magichour.ai/my-library?imageId={project_id}` - **Audio**: `https://magichour.ai/my-library?audioId={project_id}` Cancel Render Button Confirm Cancel Button Cancel Success Full credit refund is provided for cancelled video jobs. **Important notes:** * Image jobs cannot be cancelled (they complete too quickly) * API-based cancellation is not currently available * Full credit refund is provided for cancelled video jobs * Only works for jobs in `queued` or `rendering` status ## Production Deployment Checklist Before deploying to production: ### Security * ✅ API keys stored in environment variables (not hardcoded) * ✅ API keys not committed to version control * ✅ Different API keys for development/staging/production * ✅ Webhook signatures verified (if using webhooks) ### Error Handling * ✅ All API calls wrapped in try/catch blocks * ✅ Specific error codes handled appropriately * ✅ Retry logic with exponential backoff * ✅ Timeout handling for long-running jobs * ✅ Logging for all errors ### Monitoring * ✅ Credit usage tracking * ✅ Error rate monitoring * ✅ Job completion time tracking * ✅ Failed job alerting * ✅ Download success/failure tracking ### File Management * ✅ Downloaded files stored in reliable storage * ✅ Cleanup of old generated files * ✅ Handling of download URL expiration * ✅ Disk space monitoring ### Performance * ✅ Appropriate polling intervals implemented * ✅ Concurrent job limits configured * ✅ Rate limiting respected * ✅ Connection pooling for multiple requests ## Testing Checklist Before deploying, verify: ### Basic Functionality * ✅ Job creation succeeds * ✅ Status polling works correctly * ✅ File downloads successfully * ✅ Multiple concurrent jobs handle correctly ### Error Scenarios * ✅ Invalid API key handling * ✅ Insufficient credits handling * ✅ Invalid parameters rejection * ✅ Network errors and retries * ✅ Timeout handling ### Edge Cases * ✅ Very large files * ✅ Very small files * ✅ Multiple output files * ✅ Expired download URLs * ✅ Job cancellation ## Debugging Common Issues ### Issue: Jobs Stay in "queued" Status **Possible causes:** * High server load * Invalid input files * Account issues **Solutions:** * Wait longer (up to 5 minutes) * Check input file validity * Verify account has sufficient credits * Contact support if persistent ### Issue: Download URLs Return 404 **Possible causes:** * URLs expired (24+ hours old) * Job was deleted * Invalid job ID **Solutions:** * Request fresh URLs using GET endpoint * Verify job ID is correct * Download files within 24 hours of completion ### Issue: High Credit Usage During Testing **Solutions:** * Switch to mock server for development * Use minimum resolution/duration for tests * Implement proper cleanup of test jobs * Monitor credit usage in Developer Hub ## Next Steps Build your first integration from scratch Advanced file handling techniques Set up webhooks for production Complete endpoint documentation *** **Questions?** Join our [Discord community](https://discord.com/invite/JX5rgsZaJp) or email [support@magichour.ai](mailto:support@magichour.ai) # Creating Your First Integration Source: https://docs.magichour.ai/integration/first-integration Build a complete Magic Hour integration from scratch in this hands-on tutorial. Every generation follows the same lifecycle:
Create job
Receive project ID
Wait
Track completion
Poll project
Download result
Receive webhook
Open the Google Colab cookbook and try every API without local setup. ## What You'll Build In this tutorial, you'll create a working application that generates an AI image using the Magic Hour API. By the end, you'll have: * A complete project structure ready for development * Code that creates a job, monitors its progress, and downloads the result * Proper file handling for inputs and outputs * Error handling for production use * A downloadable GitHub repository to reference **Estimated time**: 15-20 minutes **Prerequisites**: API key from [Developer Hub](https://magichour.ai/developer?tab=api-keys), Python 3.8+ or Node.js 16+ installed ## Choose Your Language ### Step 1: Set Up Your Project Create a new directory and set up your project structure: ```bash theme={null} # Create project directory mkdir magic-hour-tutorial cd magic-hour-tutorial # Create necessary directories mkdir outputs mkdir assets # Create main script file touch main.py # Create environment file for your API key touch .env ``` Your project structure should now look like this: ``` magic-hour-tutorial/ ├── main.py # Your main script ├── .env # API key storage ├── outputs/ # Downloaded results go here └── assets/ # Input files (if needed) ``` ### Step 2: Install Dependencies Install the Magic Hour Python SDK and python-dotenv for managing API keys: ```bash theme={null} pip install magic-hour python-dotenv requests ``` **SDK Documentation**: Full Python SDK docs available at [github.com/magichourhq/magic-hour-python](https://github.com/magichourhq/magic-hour-python) ### Step 3: Configure Your API Key Open `.env` and add your API key: ```bash theme={null} MAGIC_HOUR_API_KEY=your_api_key_here ``` **Security**: Never commit `.env` to version control. Add it to `.gitignore` immediately. ```bash theme={null} echo ".env" >> .gitignore ``` ### Step 4: Write the Integration Code Open `main.py` and add the following code. We'll build it section by section: ```python theme={null} import os import time import requests from pathlib import Path from dotenv import load_dotenv from magic_hour import Client # Load environment variables load_dotenv() # Initialize the Magic Hour client API_KEY = os.getenv("MAGIC_HOUR_API_KEY") if not API_KEY: raise ValueError("MAGIC_HOUR_API_KEY not found in environment variables") client = Client(token=API_KEY) def main(): """Generate an AI image using Magic Hour API""" print("🚀 Starting Magic Hour Integration Tutorial") print("-" * 50) # Step 1: Create the generation job print("\n📝 Creating AI image generation job...") try: create_response = client.v1.ai_image_generator.create( image_count=1, aspect_ratio="16:9", style={ "prompt": "A serene mountain landscape at sunset with vibrant colors", "tool": "ai-anime-generator" }, name="Tutorial Image" ) job_id = create_response.id credits_charged = create_response.credits_charged print(f"✅ Job created successfully!") print(f" Job ID: {job_id}") print(f" Credits charged: {credits_charged}") except Exception as e: print(f"❌ Failed to create job: {e}") return # Step 2: Poll for completion print(f"\n⏳ Waiting for job to complete...") print(" This usually takes 5-15 seconds for images") max_attempts = 60 # Maximum 60 attempts (3 minutes) attempt = 0 while attempt < max_attempts: try: # Check job status status_response = client.v1.image_projects.get(id=job_id) status = status_response.status print(f" Status: {status} (attempt {attempt + 1}/{max_attempts})") if status == "complete": print("✅ Job completed successfully!") # Step 3: Download the result download_url = status_response.downloads[0].url download_image(download_url, job_id) break elif status == "error": error_info = status_response.error print(f"❌ Job failed with error:") print(f" Code: {error_info.get('code', 'unknown')}") print(f" Message: {error_info.get('message', 'No error message')}") return elif status in ["queued", "rendering"]: # Still processing, wait before next check time.sleep(3) attempt += 1 else: print(f"⚠️ Unexpected status: {status}") time.sleep(3) attempt += 1 except Exception as e: print(f"❌ Error checking status: {e}") return if attempt >= max_attempts: print(f"⏰ Timeout: Job did not complete within {max_attempts * 3} seconds") return print("\n✨ Tutorial completed successfully!") print(f"📁 Check the 'outputs/' directory for your generated image") def download_image(url, job_id): """Download the generated image""" print(f"\n📥 Downloading result...") try: # Create outputs directory if it doesn't exist output_dir = Path("outputs") output_dir.mkdir(exist_ok=True) # Download the file response = requests.get(url, stream=True, timeout=30) response.raise_for_status() # Save with job ID in filename filename = f"generated_image_{job_id}.png" filepath = output_dir / filename with open(filepath, "wb") as f: for chunk in response.iter_content(chunk_size=8192): f.write(chunk) file_size = filepath.stat().st_size file_size_mb = file_size / (1024 * 1024) print(f"✅ Downloaded: {filepath}") print(f" Size: {file_size_mb:.2f} MB") except requests.exceptions.RequestException as e: print(f"❌ Download failed: {e}") except Exception as e: print(f"❌ Unexpected error during download: {e}") if __name__ == "__main__": main() ``` ### Step 5: Run Your Integration Execute your script: ```bash theme={null} python main.py ``` You should see output like this: ``` 🚀 Starting Magic Hour Integration Tutorial -------------------------------------------------- 📝 Creating AI image generation job... ✅ Job created successfully! Job ID: clx7uu86w0a5qp55yxz315r6r Credits charged: 5 ⏳ Waiting for job to complete... This usually takes 5-15 seconds for images Status: queued (attempt 1/60) Status: rendering (attempt 2/60) Status: complete (attempt 3/60) ✅ Job completed successfully! 📥 Downloading result... ✅ Downloaded: outputs/generated_image_clx7uu86w0a5qp55yxz315r6r.png Size: 1.23 MB ✨ Tutorial completed successfully! 📁 Check the 'outputs/' directory for your generated image ``` ### Step 1: Set Up Your Project Create a new directory and initialize a Node.js project: ```bash theme={null} # Create project directory mkdir magic-hour-tutorial cd magic-hour-tutorial # Initialize npm project npm init -y # Create necessary directories mkdir outputs mkdir assets # Create main script file touch index.js # Create environment file for your API key touch .env ``` Your project structure should now look like this: ``` magic-hour-tutorial/ ├── index.js # Your main script ├── package.json # npm configuration ├── .env # API key storage ├── outputs/ # Downloaded results go here └── assets/ # Input files (if needed) ``` ### Step 2: Install Dependencies Install the Magic Hour Node SDK and dotenv for managing API keys: ```bash theme={null} npm install magic-hour dotenv ``` **SDK Documentation**: Full Node.js SDK docs available at [github.com/magichourhq/magic-hour-node](https://github.com/magichourhq/magic-hour-node) ### Step 3: Configure Your API Key Open `.env` and add your API key: ```bash theme={null} MAGIC_HOUR_API_KEY=your_api_key_here ``` **Security**: Never commit `.env` to version control. Add it to `.gitignore` immediately. ```bash theme={null} echo ".env" >> .gitignore ``` ### Step 4: Write the Integration Code Open `index.js` and add the following code: ```javascript theme={null} import "dotenv/config"; import { Client } from "magic-hour"; import { writeFileSync, mkdirSync } from "fs"; import { join } from "path"; // Initialize the Magic Hour client const API_KEY = process.env.MAGIC_HOUR_API_KEY; if (!API_KEY) { throw new Error("MAGIC_HOUR_API_KEY not found in environment variables"); } const client = new Client({ token: API_KEY }); async function main() { console.log("🚀 Starting Magic Hour Integration Tutorial"); console.log("-".repeat(50)); try { // Step 1: Create the generation job console.log("\n📝 Creating AI image generation job..."); const createResponse = await client.v1.aiImageGenerator.create({ imageCount: 1, aspectRatio: "16:9", style: { prompt: "A serene mountain landscape at sunset with vibrant colors", tool: "ai-anime-generator", }, name: "Tutorial Image", }); const jobId = createResponse.id; const creditsCharged = createResponse.creditsCharged; console.log("✅ Job created successfully!"); console.log(` Job ID: ${jobId}`); console.log(` Credits charged: ${creditsCharged}`); // Step 2: Poll for completion console.log("\n⏳ Waiting for job to complete..."); console.log(" This usually takes 5-15 seconds for images"); const maxAttempts = 60; // Maximum 60 attempts (3 minutes) let attempt = 0; while (attempt < maxAttempts) { // Check job status const statusResponse = await client.v1.imageProjects.get({ id: jobId }); const status = statusResponse.status; console.log(` Status: ${status} (attempt ${attempt + 1}/${maxAttempts})`); if (status === "complete") { console.log("✅ Job completed successfully!"); // Step 3: Download the result const downloadUrl = statusResponse.downloads[0].url; await downloadImage(downloadUrl, jobId); break; } else if (status === "error") { const errorInfo = statusResponse.error; console.log("❌ Job failed with error:"); console.log(` Code: ${errorInfo?.code || "unknown"}`); console.log(` Message: ${errorInfo?.message || "No error message"}`); return; } else if (status === "queued" || status === "rendering") { // Still processing, wait before next check await new Promise((resolve) => setTimeout(resolve, 3000)); attempt++; } else { console.log(`⚠️ Unexpected status: ${status}`); await new Promise((resolve) => setTimeout(resolve, 3000)); attempt++; } } if (attempt >= maxAttempts) { console.log(`⏰ Timeout: Job did not complete within ${maxAttempts * 3} seconds`); return; } console.log("\n✨ Tutorial completed successfully!"); console.log("📁 Check the 'outputs/' directory for your generated image"); } catch (error) { console.log(`❌ Error: ${error.message}`); } } async function downloadImage(url, jobId) { console.log("\n📥 Downloading result..."); try { // Create outputs directory if it doesn't exist try { mkdirSync("outputs", { recursive: true }); } catch (e) { // Directory already exists } // Download the file const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const arrayBuffer = await response.arrayBuffer(); const buffer = Buffer.from(arrayBuffer); // Save with job ID in filename const filename = `generated_image_${jobId}.png`; const filepath = join("outputs", filename); writeFileSync(filepath, buffer); const fileSizeMB = (buffer.length / (1024 * 1024)).toFixed(2); console.log(`✅ Downloaded: ${filepath}`); console.log(` Size: ${fileSizeMB} MB`); } catch (error) { console.log(`❌ Download failed: ${error.message}`); } } // Run the main function main(); ``` ### Step 5: Update package.json Add `"type": "module"` to your `package.json` to enable ES modules: ```json theme={null} { "name": "magic-hour-tutorial", "version": "1.0.0", "type": "module", "main": "index.js", "dependencies": { "dotenv": "^16.0.0", "magic-hour": "^0.70.0" } } ``` ### Step 6: Run Your Integration Execute your script: ```bash theme={null} node index.js ``` You should see similar output to the Python example showing the job creation, polling, and download process. ## Understanding the Code Let's break down what each part does: ### 1. Job Creation ```python theme={null} create_response = client.v1.ai_image_generator.create(...) ``` * Sends a request to Magic Hour to start generating an image * Returns immediately with a `job_id` and `credits_charged` * The actual generation happens asynchronously on Magic Hour's servers ### 2. Status Polling ```python theme={null} while attempt < max_attempts: status_response = client.v1.image_projects.get(id=job_id) ``` * Periodically checks if the job is complete * Polls every 3 seconds (appropriate for image generation) * Handles different statuses: queued, rendering, complete, error ### 3. File Download ```python theme={null} response = requests.get(url, stream=True) ``` * Downloads the generated image from the provided URL * Uses streaming to handle large files efficiently * Saves to the `outputs/` directory with a unique filename ### 4. Error Handling * Checks for API errors and displays helpful messages * Implements timeouts to prevent infinite loops * Validates API key exists before making requests ## Using the Simpler `generate()` Function The SDK also provides a `generate()` function that handles polling and downloading automatically: ```python Python theme={null} # All-in-one generate function result = client.v1.ai_image_generator.generate( image_count=1, aspect_ratio="16:9", style={ "prompt": "A serene mountain landscape at sunset", "tool": "ai-anime-generator" }, name="Tutorial Image", wait_for_completion=True, download_outputs=True, download_directory="." ) print(f"✅ Complete! Files: {result.downloaded_paths}") ``` ```javascript Node.js theme={null} // All-in-one generate function const result = await client.v1.aiImageGenerator.generate({ imageCount: 1, aspectRatio: "16:9", style: { prompt: "A serene mountain landscape at sunset", tool: "ai-anime-generator", }, name: "Tutorial Image", waitForCompletion: true, downloadOutputs: true, downloadDirectory: ".", }); console.log(`✅ Complete! Files: ${result.downloadedPaths}`); ``` **When to use each approach:** * Use `create()` + polling: Production apps, webhook integration, custom monitoring * Use `generate()`: Quick scripts, testing, simple integrations ## Download the Complete Project Get the full working example from GitHub: Complete Python tutorial project Complete Node.js tutorial project ## Working with Video Generation For video generation, the process is identical but uses different endpoints: ```python Python theme={null} # Create video job create_response = client.v1.face_swap.create( assets={ "image_file_path": "https://upload.wikimedia.org/wikipedia/commons/e/ec/Chris_Cassidy_-_Official_NASA_Astronaut_Portrait_in_EMU_%28cropped%29.jpg", "video_file_path": "https://svs.gsfc.nasa.gov/vis/a010000/a014300/a014327/john_bolten_no_graphics.mp4", "video_source": "file" }, start_seconds=0.0, end_seconds=10.0 ) # Poll using video_projects endpoint status = client.v1.video_projects.get(id=create_response.id) ``` ```javascript Node.js theme={null} // Create video job const createResponse = await client.v1.faceSwap.create({ assets: { imageFilePath: "https://upload.wikimedia.org/wikipedia/commons/e/ec/Chris_Cassidy_-_Official_NASA_Astronaut_Portrait_in_EMU_%28cropped%29.jpg", videoFilePath: "https://svs.gsfc.nasa.gov/vis/a010000/a014300/a014327/john_bolten_no_graphics.mp4", videoSource: "file", }, startSeconds: 0.0, endSeconds: 10.0, }); // Poll using videoProjects endpoint const status = await client.v1.videoProjects.get({ id: createResponse.id }); ``` **Video Processing Times**: Videos take 2-10 minutes depending on length. Use longer poll intervals (5-10 seconds) for video jobs. ## Next Steps Now that you have a working integration: Learn advanced file upload and download techniques Best practices for testing without using credits Set up real-time notifications instead of polling Explore all available endpoints and parameters *** **Need help?** Join our [Discord community](https://discord.com/invite/JX5rgsZaJp) or email [support@magichour.ai](mailto:support@magichour.ai) # Handling Inputs and Outputs Source: https://docs.magichour.ai/integration/inputs-and-outputs Complete guide to uploading input files and downloading generated results from Magic Hour API. ## Overview Most Magic Hour APIs use a file-based workflow: you provide input files (images, videos, audio), the API processes them, and returns generated output files. Some APIs like AI Image Generator or Text-to-Video work with text prompts instead. This guide covers both sides of file handling: **getting your files to Magic Hour** and **retrieving the results**. ## Input Files ### How to Provide Input Files You have three options for providing input files to the API: | Method | Best For | Pros | Cons | | :-------------------------- | :------------------------ | :-------------------------- | :----------------------------------- | | **Public URLs** | Files already hosted | Simple, no upload needed | Requires public hosting | | **Magic Hour Library URLs** | Reusing generated content | No upload, direct reference | Must be from your Magic Hour account | | **Upload to Magic Hour** | Local files, secure files | No hosting required, secure | Extra upload step | ## Option 1: Using Public URLs The simplest method if your files are already hosted somewhere publicly accessible: ```json theme={null} { "assets": { "video_file_path": "https://svs.gsfc.nasa.gov/vis/a010000/a014300/a014327/john_bolten_no_graphics.mp4", "image_file_path": "https://upload.wikimedia.org/wikipedia/commons/e/ec/Chris_Cassidy_-_Official_NASA_Astronaut_Portrait_in_EMU_%28cropped%29.jpg", "audio_file_path": "https://raw.githubusercontent.com/runshouse/Sample_Assets/main/you-are-just-a-line-of-code.mp3" } } ``` **Authenticated URLs**: The URL can be authenticated, as long as Magic Hour can validate the file extension is supported. ### URL Requirements * Must be publicly accessible (or use authenticated URLs that Magic Hour can access) * Must include the file extension in the URL * Should use HTTPS for security * Files must be in supported formats ## Option 2: Using Magic Hour Library URLs You can reference files from your Magic Hour library directly using library URLs. This is useful when you want to reuse previously generated content or files you've uploaded to Magic Hour. ### Library URL Format Magic Hour library URLs follow this pattern: * **Videos**: `https://magichour.ai/my-library?videoId={video_id}` * **Images**: `https://magichour.ai/my-library?imageId={image_id}` * **Audio**: `https://magichour.ai/my-library?audioId={audio_id}` ### Using Library URLs in API Calls ```json theme={null} { "assets": { "video_file_path": "https://magichour.ai/my-library?videoId=cmj86i5yy006x4m0z6znwowjb", "image_file_path": "https://magichour.ai/my-library?imageId=cmjag94dw01mr5s0zqf8ug0or", "audio_file_path": "https://magichour.ai/my-library?audioId=cmj8cx1gn009f1k0z89zg97u9" } } ``` ```python Python theme={null} # Use library URLs for assets result = client.v1.face_swap.create( assets={ "source_file_path": "https://magichour.ai/my-library?imageId=cmjag94dw01mr5s0zqf8ug0or", "video_file_path": "https://magichour.ai/my-library?videoId=cmj86i5yy006x4m0z6znwowjb" } ) ``` ```javascript Node.js theme={null} // Use library URLs for assets const result = await client.v1.faceSwap.create({ assets: { sourceFilePath: "https://magichour.ai/my-library?imageId=cmjag94dw01mr5s0zqf8ug0or", videoFilePath: "https://magichour.ai/my-library?videoId=cmj86i5yy006x4m0z6znwowjb", }, }); ``` **Library Access**: You can only use library URLs for files in your own Magic Hour account. The file IDs are available from your [my-library dashboard](https://magichour.ai/my-library) or from API responses. **Benefits:** * ✅ Reuse previously generated content * ✅ No need to re-upload files * ✅ Direct reference to your Magic Hour files * ✅ Works with any file type (video, image, audio) ## Option 3: Uploading to Magic Hour Storage For local files or files that aren't publicly hosted, upload them to Magic Hour's storage: ### Upload Process The upload workflow has three steps: 1. **Request upload URLs** - Tell Magic Hour what file types you're uploading (specify extension and type) 2. **Upload files** - PUT your files to the provided temporary URLs 3. **Use file paths** - Reference the returned `file_path` in your API calls ### SDK Upload (Simplified) Python and Node.js SDKs provide a helper function: **SDK Version Required**: Python SDK v0.36.0+ or Node SDK v0.37.0+ Go and Rust SDKs: Manual upload process (shown below) ```python Python SDK theme={null} from magic_hour import Client client = Client(token="YOUR_API_KEY") # Upload file and get file_path file_path = client.v1.files.upload_file("/path/to/your/image.jpg") # Use in API calls result = client.v1.face_swap_photo.create( assets={ "source_file_path": file_path, # Use uploaded file "target_file_path": "https://upload.wikimedia.org/wikipedia/commons/e/ec/Chris_Cassidy_-_Official_NASA_Astronaut_Portrait_in_EMU_%28cropped%29.jpg" } ) ``` ```javascript Node.js SDK theme={null} import { Client } from "magic-hour"; const client = new Client({ token: "YOUR_API_KEY" }); // Upload file and get filePath const filePath = await client.v1.files.uploadFile("/path/to/your/image.jpg"); // Use in API calls const result = await client.v1.faceSwapPhoto.create({ assets: { sourceFilePath: filePath, // Use uploaded file targetFilePath: "https://upload.wikimedia.org/wikipedia/commons/e/ec/Chris_Cassidy_-_Official_NASA_Astronaut_Portrait_in_EMU_%28cropped%29.jpg", }, }); ``` ### Manual Upload Process For Go, Rust, or custom implementations: ```go Go SDK theme={null} package main import ( "fmt" "net/http" "os" sdk "github.com/magichourhq/magic-hour-go/client" "github.com/magichourhq/magic-hour-go/resources/v1/files/upload_urls" "github.com/magichourhq/magic-hour-go/types" ) func main() { client := sdk.NewClient(sdk.WithBearerAuth(os.Getenv("MAGIC_HOUR_API_KEY"))) // Step 1: Request upload URL response, err := client.V1.Files.UploadUrls.Create(upload_urls.CreateRequest{ Items: []types.V1FilesUploadUrlsCreateBodyItemsItem{ {Extension: "mp4", Type: types.V1FilesUploadUrlsCreateBodyItemsItemTypeEnumVideo}, }, }) if err != nil { fmt.Println(err) return } // Step 2: Upload file to the URL localPath := "/path/to/file/video.mp4" file, err := os.Open(localPath) if err != nil { fmt.Println(err) return } defer file.Close() req, err := http.NewRequest("PUT", response.Items[0].UploadUrl, file) if err != nil { fmt.Println(err) return } fileClient := &http.Client{} _, err = fileClient.Do(req) if err != nil { fmt.Println(err) return } // Step 3: Use file_path in API calls filePath := response.Items[0].FilePath fmt.Printf("✅ File uploaded: %s\n", filePath) // Now use filePath in your API calls } ``` ```rust Rust SDK theme={null} use magic_hour; let mut client = magic_hour::Client::default() .with_bearer_auth(&std::env::var("MAGIC_HOUR_API_KEY").expect("MAGIC_HOUR_API_KEY must be set")); // Step 1: Request upload URL let response = client.v1().files().upload_urls() .create(magic_hour::resources::v1::files::upload_urls::CreateRequest { items: vec![ magic_hour::models::V1FilesUploadUrlsCreateBodyItemsItem { extension: "mp4".to_string(), type_field: magic_hour::models::V1FilesUploadUrlsCreateBodyItemsItemTypeEnum::Video } ], }).await; // Step 2: Upload file let local_path = "/path/to/file/video.mp4"; let file = std::fs::File::open(local_path).unwrap(); let mut req = reqwest::Client::new() .put(response.items[0].upload_url); req.body(file).send().unwrap(); // Step 3: Use file_path in API calls let file_path = response.items[0].file_path; ``` ```bash cURL theme={null} # Step 1: Request upload URL response=$(curl https://api.magichour.ai/v1/files/upload-urls \ --request POST \ --header 'Authorization: Bearer YOUR_API_KEY' \ --header 'Content-Type: application/json' \ --data '{ "items": [ { "type": "video", "extension": "mp4" } ] }') # Step 2: Upload file to the URL upload_url=$(echo $response | jq -r '.items[0].upload_url') curl $upload_url \ --request PUT \ --data-binary @/path/to/file/video.mp4 # Step 3: Extract file_path for API calls file_path=$(echo $response | jq -r '.items[0].file_path') echo "File uploaded: $file_path" ``` ### Upload Response Format ```json theme={null} { "items": [ { "upload_url": "https://videos.magichour.ai/api-assets/id/video.mp4?", "expires_at": "2024-07-25T16:56:21.932Z", "file_path": "api-assets/id/video.mp4" } ] } ``` **Fields explained:** * `upload_url`: Temporary pre-signed URL to upload the file to (send a `PUT` request) * `expires_at`: When the `upload_url` expires and a new one must be requested * `file_path`: Reference to pass in API calls (e.g. `image_file_path`, `video_file_path`, `audio_file_path`) ### Uploading Multiple Files Request multiple upload URLs in a single call: ```python Python theme={null} response = client.v1.files.upload_urls.create( items=[ {"type": "video", "extension": "mp4"}, {"type": "image", "extension": "jpg"}, {"type": "audio", "extension": "mp3"} ] ) # Upload each file # Order matches request order video_path = response.items[0].file_path image_path = response.items[1].file_path audio_path = response.items[2].file_path ``` ```javascript Node.js theme={null} const response = await client.v1.files.uploadUrls.create({ items: [ { type: "video", extension: "mp4" }, { type: "image", extension: "jpg" }, { type: "audio", extension: "mp3" }, ], }); // Upload each file // Order matches request order const videoPath = response.items[0].filePath; const imagePath = response.items[1].filePath; const audioPath = response.items[2].filePath; ``` **Upload Timing**: Upload files just before using them in API calls. For permanent storage needs, contact [support@magichour.ai](mailto:support@magichour.ai). ## Output Files ### Download URLs When a job completes successfully, the response includes download URLs: ```json theme={null} { "status": "complete", "downloads": [ { "url": "https://videos.magichour.ai/id/output.mp4", "expires_at": "2024-10-19T05:16:19.027Z" } ] } ``` ### Download URL Expiration **24-Hour Expiration**: Download URLs expire after 24 hours. Download files promptly or request fresh URLs using the GET endpoint. To get fresh download URLs for an existing project: ```python Python theme={null} # Get fresh download URLs for completed project status = client.v1.video_projects.get(id="project_id") download_url = status.downloads[0].url ``` ```javascript Node.js theme={null} // Get fresh download URLs for completed project const status = await client.v1.videoProjects.get({ id: "project_id" }); const downloadUrl = status.downloads[0].url; ``` ### Streaming Downloads For production applications, use streaming downloads to handle large files efficiently: ```python Python theme={null} import requests from pathlib import Path import time def download_file(url, output_path, max_retries=3): """Download file with retries and streaming for large files""" # Create output directory Path(output_path).parent.mkdir(parents=True, exist_ok=True) for attempt in range(max_retries): try: print(f"Downloading (attempt {attempt + 1}/{max_retries})...") response = requests.get(url, stream=True, timeout=60) response.raise_for_status() # Stream download for large files with open(output_path, "wb") as f: for chunk in response.iter_content(chunk_size=8192): f.write(chunk) file_size = Path(output_path).stat().st_size print(f"✅ Downloaded: {output_path} ({file_size:,} bytes)") return True except requests.exceptions.RequestException as e: print(f"❌ Download failed: {e}") if attempt < max_retries - 1: wait_time = 2 ** attempt # Exponential backoff print(f" Retrying in {wait_time} seconds...") time.sleep(wait_time) else: print(" Max retries exceeded") return False return False # Usage download_file(download_url, "outputs/result.mp4") ``` ```javascript Node.js theme={null} import { writeFileSync, mkdirSync } from "fs"; import { dirname } from "path"; async function downloadFile(url, outputPath, maxRetries = 3) { // Create output directory try { mkdirSync(dirname(outputPath), { recursive: true }); } catch (e) { // Directory exists } for (let attempt = 0; attempt < maxRetries; attempt++) { try { console.log(`Downloading (attempt ${attempt + 1}/${maxRetries})...`); const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const arrayBuffer = await response.arrayBuffer(); const buffer = Buffer.from(arrayBuffer); writeFileSync(outputPath, buffer); console.log(`✅ Downloaded: ${outputPath} (${buffer.length.toLocaleString()} bytes)`); return true; } catch (error) { console.log(`❌ Download failed: ${error.message}`); if (attempt < maxRetries - 1) { const waitTime = Math.pow(2, attempt) * 1000; // Exponential backoff console.log(` Retrying in ${waitTime / 1000} seconds...`); await new Promise((resolve) => setTimeout(resolve, waitTime)); } else { console.log(" Max retries exceeded"); return false; } } } return false; } // Usage await downloadFile(downloadUrl, "outputs/result.mp4"); ``` ### Handling Multiple Output Files Some APIs generate multiple files (e.g., multiple images): ```python Python theme={null} # Download all outputs for i, download in enumerate(status_response.downloads): filename = f"output_{i+1}.png" filepath = f"outputs/{filename}" download_file(download.url, filepath) print(f"Downloaded: {filename}") ``` ```javascript Node.js theme={null} // Download all outputs for (const [i, download] of statusResponse.downloads.entries()) { const filename = `output_${i + 1}.png`; const filepath = `outputs/${filename}`; await downloadFile(download.url, filepath); console.log(`Downloaded: ${filename}`); } ``` ### Automatic Downloads with `generate()` The SDK's `generate()` function can download files automatically: ```python Python theme={null} result = client.v1.ai_image_generator.generate( image_count=1, aspect_ratio="16:9", style={"prompt": "Cool image", "tool": "ai-anime-generator"}, name="AI Image Generated by Magic Hour", wait_for_completion=True, download_outputs=True, download_directory="." ) # Print where files were saved paths = getattr(result, "downloaded_paths", None) or [] print(f"✅ Downloaded to: {paths}" if paths else "✅ Done (no downloaded_paths returned by SDK)") ``` ```javascript Node.js theme={null} const result = await client.v1.aiImageGenerator.generate({ imageCount: 1, aspectRatio: "16:9", style: { prompt: "Mountain sunset", tool: "ai-anime-generator" }, waitForCompletion: true, downloadOutputs: true, // Auto-download downloadDirectory: ".", // Where to save }); // Files are already downloaded console.log(`Downloaded to: ${result.downloadedPaths}`); ``` ## File Specifications ### Supported File Formats mp4, m4v, mov, webm png, jpg, jpeg, jfif, heic, heif, webp, avif, jp2, tiff, tif, bmp mp3, wav, aac, flac, webm, weba, m4a, opus, ogg, oga, aiff, amr `gif` extension is only supported by face swap API's `video_file_path` field. ### File Size Limits Different subscription tiers have different upload limits: | Tier | Maximum Upload Size | | :----------- | :------------------ | | **Free** | 200 MB | | **Creator** | 2 GB | | **Pro** | 5 GB | | **Business** | 10 GB | **Upgrade for Larger Files**: If you need to process files larger than your current limit, upgrade your subscription tier. ## Retention & Storage Policies ### Uploaded Files (Inputs) **7-Day Retention**: Files uploaded to Magic Hour storage are automatically deleted after 7 days. ```python theme={null} # Upload once, use multiple times within 7 days file_path = client.v1.files.upload_file("image.jpg") # Use in multiple API calls within the 7-day window result1 = client.v1.face_swap_photo.create(assets={"source_file_path": file_path, ...}) result2 = client.v1.ai_clothes_changer.create(assets={"image_file_path": file_path, ...}) ``` **Best practice:** Reuse the same `file_path` for multiple API calls to avoid duplicate uploads. ### Generated Files (Outputs) **Permanent Storage**: Generated files are stored indefinitely in Magic Hour cloud storage. **Download URL Expiration**: Download URLs expire after 24 hours for security, but you can request fresh URLs anytime. **Web Dashboard**: All generated content appears in your [magichour.ai/my-library](https://magichour.ai/my-library) dashboard. ### Getting Fresh Download URLs If your download URL expired, request a new one: ```python Python theme={null} # Get fresh download URLs for an existing project status = client.v1.video_projects.get(id="project_id") fresh_url = status.downloads[0].url ``` ```javascript Node.js theme={null} // Get fresh download URLs for an existing project const status = await client.v1.videoProjects.get({ id: "project_id" }); const freshUrl = status.downloads[0].url; ``` ## Best Practices ### For Uploads ✅ **Do:** * Validate file formats before uploading * Use the SDK upload helpers when available * Reuse uploaded files within the 7-day window * Compress large files to save upload time ❌ **Don't:** * Upload the same file repeatedly (reuse the `file_path`) * Exceed your tier's file size limits * Rely on uploaded files after 7 days ### For Downloads ✅ **Do:** * Download files immediately after job completion * Implement retry logic with exponential backoff * Stream large files to avoid memory issues * Store files in your own storage for permanent access * Verify file integrity after download ❌ **Don't:** * Rely on download URLs after 24 hours * Download the same file multiple times unnecessarily * Ignore download errors without retry logic ## Troubleshooting ### Upload Issues **"Invalid file format" error:** * Check that your file extension is in the supported list * Verify the file isn't corrupted * Ensure the file type matches the extension **"File too large" error:** * Check your subscription tier's upload limit * Compress the file or upgrade your subscription * Split large videos into smaller segments **Upload timeout:** * Check your internet connection * Try uploading during off-peak hours * Consider using a CDN for faster uploads ### Download Issues **"URL expired" error:** * Request fresh download URLs using the GET endpoint * Download files within 24 hours of job completion * Store files in your own storage for permanent access **Download fails or corrupts:** * Implement retry logic with exponential backoff * Verify file integrity (check file size matches) * Use streaming downloads for large files **"404 Not Found" on download:** * Verify the job ID is correct * Ensure the job status is "complete" * Check that the project hasn't been deleted ## Next Steps Build your first integration step-by-step Test without using credits Complete upload API documentation Set up webhooks for production apps *** **Questions?** Join our [Discord community](https://discord.com/invite/JX5rgsZaJp) or email [support@magichour.ai](mailto:support@magichour.ai) # MCP Server Source: https://docs.magichour.ai/integration/model-context-protocol Access Magic Hour API docs through your favorite AI tools The Model Context Protocol (MCP) enables your favorite AI development tools to access Magic Hour's API documentation directly. This integration allows AI assistants to provide accurate, up-to-date information about Magic Hour's APIs, helping you write better code with contextual assistance. ## Benefits * **Real-time API reference**: AI assistants can access the latest API documentation without you needing to copy-paste from docs * **Code generation**: Get accurate code examples for Magic Hour APIs directly in your editor * **Parameter guidance**: Receive intelligent suggestions for API parameters and request structures * **Error troubleshooting**: Get help debugging API integration issues with full context of the documentation ## Setup Instructions Choose your development environment below to add Magic Hour's documentation as an MCP server: To connect the Magic Hour MCP server to Cursor, click the link below. [Install MCP Server](https://cursor.com/en/install-mcp?name=Magic%20Hour\&config=eyJ1cmwiOiJodHRwczovL2RvY3MubWFnaWNob3VyLmFpL21jcCJ9) To manually connect the MCP server: 1. Open MCP Settings 2. In `mcp.json`, add the following: ```json theme={null} { "mcpServers": { "Magic Hour": { "url": "https://docs.magichour.ai/mcp" } } } ``` To connect the Magic Hour MCP server to VS Code, click the button below. [Install MCP Server](https://vscode.dev/redirect/mcp/install?name=MagicHour\&config=%7B%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fdocs.magichour.ai%2Fmcp%22%7D) To manually connect the MCP server: Create a `.vscode/mcp.json` file and add: ```json theme={null} { "servers": { "Magic Hour": { "type": "http", "url": "https://docs.magichour.ai/mcp" } } } ``` To use the Magic Hour MCP server with Claude Code, run the following command: ``` claude mcp add --transport http MagicHour https://docs.magichour.ai/mcp ``` Check out the [Claude Code Documentation](https://docs.anthropic.com/en/docs/claude-code/mcp#installing-mcp-servers) for more information. # Integration Overview Source: https://docs.magichour.ai/integration/overview Understand how the Magic Hour API works and how to integrate it into your application. ## What is the Magic Hour API? Magic Hour API provides programmatic access to AI-powered video, image, and audio generation tools. Instead of using the web interface manually, you can build these capabilities directly into your applications, workflows, and products. ## Why Use the API? The API enables you to: * **Build AI features into your products** - Embed video and image generation in your applications * **Automate content workflows** - Process large batches of media without manual intervention * **Scale your operations** - Handle high-volume generation with enterprise-grade infrastructure * **Integrate with existing systems** - Connect AI generation to your current tech stack * **Create custom experiences** - Build unique user interfaces and workflows on top of our AI models **Example use cases:** * Social media apps that auto-generate content for users * Marketing platforms with automated video creation * E-commerce sites with AI product visualization * Gaming platforms with dynamic avatar generation * Education apps with custom learning materials ## API vs Web App Understanding the relationship between the API and web application: **🌐 Web App ([magichour.ai](https://magichour.ai)):** * Full suite of 100+ AI tools and features * User-friendly interface with templates and presets * New features launch here first * Perfect for creators and individual use **⚡ API (docs.magichour.ai):** * Core popular tools available programmatically * Built for developers and applications * Same underlying AI models as web app * Features added after web app validation **Shared Dashboard:** All API-generated content automatically appears in your [magichour.ai dashboard](https://magichour.ai/my-library), where you can view, manage, and share your creations. ## How the API Works Magic Hour APIs use an **asynchronous processing model**. Unlike typical REST APIs that return results immediately, AI generation takes time, so the workflow follows these steps: ```mermaid theme={null} sequenceDiagram participant App as Your Application participant API as Magic Hour API participant AI as AI Processing App->>API: 1. Submit job (create) API-->>App: Returns job ID immediately API->>AI: Queue job for processing Note over App,AI: Job processes asynchronously (30s - 5min) AI->>AI: Render video/image alt Polling Method loop Every 3-10 seconds App->>API: 2. Check status (poll) API-->>App: Status: queued/rendering/complete end else Webhook Method AI->>API: Processing complete API->>App: 2. Send webhook notification end App->>API: 3. Get download URL API-->>App: Temporary download URL App->>API: Download result ``` ### The Three Steps **1. Submit (Create)** * Send a request to create a video, image, or audio * Receive a job ID immediately (no waiting) * Job enters the processing queue **2. Monitor (Poll or Webhook)** * **Polling**: Periodically check job status using the job ID * **Webhooks**: Receive automatic notifications when job completes * Track progress through status updates **3. Download** * Retrieve the generated file using a secure download URL * URLs are temporary (expire after 24 hours) * Save the result to your storage **Asynchronous Processing:** Jobs don't complete instantly. Always implement status monitoring (polling or webhooks) before attempting to download results. ## Create vs Generate The SDKs provide two ways to interact with the API: ### `create()` - Full Control The `create()` function gives you complete control over the workflow: ```python theme={null} # Step 1: Create job result = client.v1.ai_image_generator.create( image_count=1, aspect_ratio="16:9", style={"prompt": "A sunset over mountains", "tool": "ai-anime-generator"}, name="My image" ) job_id = result.id # Step 2: Poll for completion (you handle this) while True: status = client.v1.image_projects.get(id=job_id) if status.status == "complete": break time.sleep(3) # Step 3: Download (you handle this) download_url = status.downloads[0].url # ... download the file yourself ``` **Best for:** * Fine-grained control over polling intervals * Custom status monitoring logic * Integration with existing job management systems * Advanced error handling and retry logic ### `generate()` - Simplified Workflow The `generate()` function handles everything automatically: ```python theme={null} # All three steps handled automatically result = client.v1.ai_image_generator.generate( image_count=1, aspect_ratio="16:9", style={"prompt": "A sunset over mountains", "tool": "ai-anime-generator"}, name="My image" ) # Returns when complete with file automatically downloaded ``` **Best for:** * Quick integrations and prototyping * Simple use cases with single job processing * Applications that can wait synchronously * Minimal boilerplate code **SDK Requirement:** The `generate()` function requires Python SDK v0.36.0+ or Node SDK v0.37.0+. **When to use each:** * **Use `create()`** for production apps with webhook integration, concurrent job processing, or custom monitoring needs * **Use `generate()`** for scripts, simple integrations, or when you want minimal code ## Integration Approaches ### Approach 1: Synchronous (Simple) Good for: * Scripts and command-line tools * Single job processing * Testing and development ```python theme={null} # Using generate() - blocks until complete result = client.v1.ai_image_generator.generate( image_count=1, aspect_ratio="16:9", style={"prompt": "Test image", "tool": "ai-anime-generator"} ) print(f"Image ready: {result.downloads[0].url}") ``` **Pros:** Simple code, easy to understand\ **Cons:** Application blocks while waiting, not scalable ### Approach 2: Polling (Moderate) Good for: * Background job processing * Applications that can handle wait times * Simple queue-based systems ```python theme={null} # Using create() with polling job = client.v1.ai_image_generator.create( image_count=1, aspect_ratio="16:9", style={"prompt": "Test image", "tool": "ai-anime-generator"} ) # Check periodically in a background task def check_status(): status = client.v1.image_projects.get(id=job.id) if status.status == "complete": download_result(status.downloads[0].url) ``` **Pros:** More control, works without webhooks\ **Cons:** Requires periodic polling, uses resources while waiting ### Approach 3: Webhooks (Production) Good for: * Production applications * High-volume processing * Real-time user notifications * Efficient resource usage ```python theme={null} # Create job job = client.v1.ai_image_generator.create( image_count=1, aspect_ratio="16:9", style={"prompt": "Test image", "tool": "ai-anime-generator"} ) # Your webhook endpoint receives notification when complete # (See webhook integration guide for setup) ``` **Pros:** Real-time notifications, efficient, scalable\ **Cons:** Requires webhook endpoint setup Complete guide to setting up webhooks for production use ## Development Workflow ### Step 1: Start Simple Begin with the `generate()` function to prototype: ```python theme={null} result = client.v1.ai_image_generator.generate( image_count=1, aspect_ratio="1:1", style={"prompt": "Test image", "tool": "ai-anime-generator"} ) ``` ### Step 2: Add Error Handling Handle failures gracefully: ```python theme={null} try: result = client.v1.ai_image_generator.generate(...) print("Success!") except Exception as e: print(f"Error: {e}") ``` ### Step 3: Move to Production Switch to `create()` + webhooks for production: ```python theme={null} # Create job job = client.v1.ai_image_generator.create(...) # Webhook handles completion notification # (no polling needed) ``` ## Testing Without Credits Use the mock server to develop and test without consuming credits: ```python Python SDK theme={null} from magic_hour import Client from magic_hour.environment import Environment # Use mock server - no credits charged client = Client( token="YOUR_API_KEY", environment=Environment.MOCK_SERVER ) # Returns realistic sample data instantly result = client.v1.ai_image_generator.create(...) ``` ```typescript Node SDK theme={null} import Client, { Environment } from "magic-hour"; // Use mock server - no credits charged const client = new Client({ token: "YOUR_API_KEY", environment: Environment.MockServer, }); // Returns realistic sample data instantly const result = await client.v1.aiImageGenerator.create({...}); ``` **Mock Server:** Returns realistic sample data without processing jobs or charging credits. Perfect for development and testing. ## Processing Times Processing time varies significantly by endpoint. See customer-observed **median (p50) end-to-end times**, including queueing, before setting polling intervals and timeouts. Compare rolling 30-day median times for video, image, and audio APIs. Processing times are not an SLA. They vary with input duration and resolution, current queue load, model complexity, file size, and format. Always implement timeout handling. ## Next Steps Choose your path based on your needs: Make your first API call in 3 minutes Production-ready integration patterns Real-time notifications for production apps Complete endpoint documentation *** **Questions?** Join our [Discord community](https://discord.com/invite/JX5rgsZaJp) or email [support@magichour.ai](mailto:support@magichour.ai) # Production Webhook Handler Source: https://docs.magichour.ai/integration/webhook/create-handler Deploy robust webhook handlers for production use This guide covers production-ready webhook handlers with error handling, logging, and best practices. **New to webhooks?** Start with our [Webhook Quickstart](/integration/webhook/overview) for a complete end-to-end tutorial. ## Production Requirements Your production webhook handler should: * ✅ **Handle POST requests** with JSON payloads * ✅ **Return 2xx status codes** for successful processing * ✅ **Process requests quickly** (\< 10 seconds recommended) * ✅ **Handle retries gracefully** (idempotent processing) * ✅ **Log events** for debugging and monitoring * ✅ **Verify signatures** for security ## Retry Behavior **Automatic Retries**: If your endpoint doesn't respond with a 2xx status code, Magic Hour will retry delivery: * **Duration**: Up to 24 hours * **Pattern**: Exponential backoff (1s, 2s, 4s, 8s, ...) * **After 24 hours**: Event marked as failed, no more retries **Disabled Webhooks**: Pending events are skipped and marked as failed after 24 hours. ## Production Handler Examples ```python Python (FastAPI + Database) theme={null} from fastapi import FastAPI, Request, HTTPException, BackgroundTasks from sqlalchemy.orm import Session import logging import json import time app = FastAPI() logger = logging.getLogger(__name__) @app.post("/webhook") async def webhook_handler( request: Request, background_tasks: BackgroundTasks ): try: # Get event data event = await request.json() event_type = event.get('type') payload = event.get('payload', {}) # Log the event logger.info(f"Received webhook: {event_type}", extra={ 'event_type': event_type, 'job_id': payload.get('id'), 'status': payload.get('status') }) # Process in background to respond quickly background_tasks.add_task(process_webhook_event, event) return {"success": True, "timestamp": int(time.time())} except Exception as e: logger.error(f"Webhook processing failed: {str(e)}") raise HTTPException(status_code=500, detail="Internal server error") async def process_webhook_event(event: dict): """Process webhook event in background""" try: event_type = event['type'] payload = event['payload'] job_id = payload['id'] # Update database record with get_db_session() as db: job = db.query(Job).filter(Job.magic_hour_id == job_id).first() if not job: logger.warning(f"Job {job_id} not found in database") return if event_type == 'video.completed': job.status = 'completed' job.download_url = payload['downloads'][0]['url'] job.completed_at = datetime.utcnow() # Notify user (email, push notification, etc.) await notify_user_completion(job.user_id, job) elif event_type == 'video.errored': job.status = 'failed' job.error_message = payload['error']['message'] job.failed_at = datetime.utcnow() # Notify user of failure await notify_user_error(job.user_id, job) db.commit() logger.info(f"Updated job {job_id} status to {job.status}") except Exception as e: logger.error(f"Background processing failed: {str(e)}") ``` ```typescript Node.js (Express + Redis) theme={null} const express = require("express"); const redis = require("redis"); const { createLogger, format, transports } = require("winston"); const app = express(); const redisClient = redis.createClient(); // Setup logging const logger = createLogger({ level: "info", format: format.combine(format.timestamp(), format.errors({ stack: true }), format.json()), transports: [new transports.File({ filename: "webhook.log" }), new transports.Console()], }); app.use(express.json()); app.post("/webhook", async (req, res) => { try { const { type, payload } = req.body; const jobId = payload.id; // Log the event logger.info("Received webhook", { eventType: type, jobId: jobId, status: payload.status, }); // Respond immediately res.status(200).json({ success: true, timestamp: Math.floor(Date.now() / 1000), }); // Process asynchronously processWebhookEvent(type, payload); } catch (error) { logger.error("Webhook processing failed", { error: error.message }); res.status(500).json({ error: "Internal server error" }); } }); async function processWebhookEvent(type, payload) { try { const jobId = payload.id; // Update job status in Redis const jobKey = `job:${jobId}`; switch (type) { case "video.started": await redisClient.hSet(jobKey, { status: "processing", startedAt: new Date().toISOString(), }); break; case "video.completed": await redisClient.hSet(jobKey, { status: "completed", downloadUrl: payload.downloads[0].url, completedAt: new Date().toISOString(), }); // Notify user via WebSocket, email, etc. await notifyUserCompletion(jobId, payload); break; case "video.errored": await redisClient.hSet(jobKey, { status: "failed", error: JSON.stringify(payload.error), failedAt: new Date().toISOString(), }); await notifyUserError(jobId, payload); break; } logger.info(`Updated job ${jobId} status`, { type, status: payload.status }); } catch (error) { logger.error("Background processing failed", { error: error.message }); } } const port = process.env.PORT || 8000; app.listen(port, () => { logger.info(`Webhook server running on port ${port}`); }); ``` ## Best Practices ### 1. Idempotent Processing Handle duplicate events gracefully (Magic Hour may retry): ```python theme={null} # Webhook payloads don't include an event ID, so build a dedup key # from the project ID and event type dedup_key = f"{event['payload']['id']}:{event['type']}" if await is_event_processed(dedup_key): logger.info(f"Event {dedup_key} already processed, skipping") return {"success": True} # Process event and mark as processed await process_event(event) await mark_event_processed(dedup_key) ``` ### 2. Quick Response Times Respond within 10 seconds to avoid timeouts: ```python theme={null} # ✅ Good: Respond immediately, process in background @app.post("/webhook") async def webhook(request: Request, background_tasks: BackgroundTasks): event = await request.json() background_tasks.add_task(process_event, event) return {"success": True} # Respond immediately # ❌ Bad: Long processing blocks response @app.post("/webhook") async def webhook(request: Request): event = await request.json() await long_running_process(event) # This might timeout return {"success": True} ``` ### 3. Comprehensive Logging Log all events for debugging: ```python theme={null} logger.info("Webhook received", extra={ 'event_type': event['type'], 'job_id': event['payload']['id'], 'timestamp': int(time.time()), 'user_agent': request.headers.get('user-agent'), 'ip_address': request.client.host }) ``` ### 4. Error Handling Handle errors gracefully: ```python theme={null} try: await process_webhook(event) except ValidationError as e: logger.error(f"Invalid webhook data: {e}") return {"error": "Invalid data"}, 400 except Exception as e: logger.error(f"Webhook processing failed: {e}") return {"error": "Internal error"}, 500 ``` ## Local Testing Test your handler locally before deploying: ```bash Python theme={null} # Start your server uvicorn main:app --reload --port 8000 # In another terminal, send test event curl -X POST http://localhost:8000/webhook \ -H "Content-Type: application/json" \ -d '{ "type": "video.completed", "payload": { "id": "test-job-123", "status": "complete", "downloads": [ { "url": "https://example.com/video.mp4", "expires_at": "2024-12-01T12:00:00Z" } ] } }' ``` ```bash Node.js theme={null} # Start your server node server.js # In another terminal, send test event curl -X POST http://localhost:8000/webhook \ -H "Content-Type: application/json" \ -d '{ "type": "image.completed", "payload": { "id": "test-job-456", "status": "complete", "downloads": [ { "url": "https://example.com/image.png", "expires_at": "2024-12-01T12:00:00Z" } ] } }' ``` ## Deployment Considerations ### Load Balancing If using multiple servers, ensure webhooks can reach any instance: ```yaml theme={null} # docker-compose.yml services: webhook-handler: image: your-webhook-app replicas: 3 ports: - "8000-8002:8000" ``` ### Health Checks Add a health check endpoint: ```python theme={null} @app.get("/health") async def health_check(): return {"status": "healthy", "timestamp": int(time.time())} ``` ### Monitoring Monitor webhook delivery and processing: ```python theme={null} # Track metrics webhook_counter = Counter('webhooks_received_total', ['event_type']) processing_time = Histogram('webhook_processing_seconds') @app.post("/webhook") async def webhook(request: Request): start_time = time.time() event = await request.json() webhook_counter.labels(event_type=event['type']).inc() # Process event... processing_time.observe(time.time() - start_time) return {"success": True} ``` ## Next Steps Add signature verification for production security Complete list of webhook events and payloads Detailed webhook API documentation New to webhooks? Start with our quickstart ## Common Issues **Webhook timeouts?** * Respond within 10 seconds * Use background tasks for long processing * Check server resources and scaling **Missing events?** * Verify webhook is enabled in Developer Hub * Check endpoint URL is correct and accessible * Review server logs for errors **Duplicate events?** * Implement idempotent processing * Store processed event IDs * Handle retries gracefully Need help? Contact [support@magichour.ai](mailto:support@magichour.ai) # Create a Webhook Source: https://docs.magichour.ai/integration/webhook/create-webhook Set up and test your first webhook end-to-end to receive real-time notifications from Magic Hour. By the end of this guide, you'll have: * Created a webhook endpoint in your application * Registered it with Magic Hour * Tested it with a real API call * Verified webhook delivery works ## What Are Webhooks? Webhooks let you receive real-time notifications when your API requests complete, eliminating the need for polling. Instead of repeatedly checking job status, Magic Hour automatically notifies your application when jobs finish. ```mermaid theme={null} sequenceDiagram participant App as Your Application participant MH as Magic Hour App->>MH: 1. Create video/image job MH-->>App: 2. Job ID returned immediately MH->>MH: 3. Process job in background MH->>App: 4. Send webhook when complete App->>MH: 5. Return 2xx Response ``` **Benefits:** * ✅ Real-time notifications (no polling delays) * ✅ Efficient resource usage (no constant polling) * ✅ Better user experience (instant updates) * ✅ Scalable for high-volume operations ## Step 1: Create Your Webhook Endpoint First, create a simple webhook handler that can receive and process events. **Using Jupyter/Colab?** See the "Colab/Jupyter" tab below for notebook-compatible code, or use [webhook.site](https://webhook.site) for instant testing without any code. ```python Python (FastAPI) theme={null} from fastapi import FastAPI, Request import json app = FastAPI() @app.post("/webhook") async def webhook_handler(request: Request): # Get the event data event = await request.json() # Log the event for testing print(f"Received event: {event['type']}") print(f"Payload: {json.dumps(event['payload'], indent=2)}") # Handle different event types match event['type']: case 'video.started': print('🎬 Video processing started') case 'video.completed': print('✅ Video processing completed') # Download URL available in event['payload']['downloads'] case 'video.errored': print('❌ Video processing failed') case 'image.completed': print('🖼️ Image processing completed') case 'image.errored': print('❌ Image processing failed') # Always return success return {"success": True} if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000) ``` ```javascript Node.js (Express) theme={null} const express = require("express"); const app = express(); app.use(express.json()); app.post("/webhook", (req, res) => { const { type, payload } = req.body; // Log the event for testing console.log(`Received event: ${type}`); console.log(`Payload:`, JSON.stringify(payload, null, 2)); // Handle different event types switch (type) { case "video.started": console.log("🎬 Video processing started"); break; case "video.completed": console.log("✅ Video processing completed"); // Download URL available in payload.downloads break; case "video.errored": console.log("❌ Video processing failed"); break; case "image.completed": console.log("🖼️ Image processing completed"); break; case "image.errored": console.log("❌ Image processing failed"); break; } // Always return success res.status(200).json({ success: true }); }); const port = 8000; app.listen(port, () => { console.log(`🚀 Webhook server running on http://localhost:${port}`); }); ``` ```python Colab/Jupyter theme={null} # Notebook-friendly webhook server from fastapi import FastAPI, Request import json import nest_asyncio import threading import uvicorn import time # Install required packages first: # !pip install fastapi uvicorn nest-asyncio pyngrok # Allow nested event loops (required for notebooks) nest_asyncio.apply() app = FastAPI() @app.post("/webhook") async def webhook_handler(request: Request): # Get the event data event = await request.json() # Log the event for testing print(f"Received event: {event['type']}") print(f"Payload: {json.dumps(event['payload'], indent=2)}") # Handle different event types match event['type']: case 'video.completed': print('✅ Video processing completed') case 'image.completed': print('🖼️ Image processing completed') case _: print(f'Event: {event["type"]}') return {"success": True} # Notebook-friendly server startup def run_server(): uvicorn.run(app, host="0.0.0.0", port=8000, log_level="info") # Start server in background thread server_thread = threading.Thread(target=run_server, daemon=True) server_thread.start() # Wait for server to start time.sleep(2) print("🚀 Webhook server running on http://localhost:8000") # For Colab: Use ngrok to make it public try: from pyngrok import ngrok public_url = ngrok.connect(8000) print(f"📡 Public URL: {public_url}") print(f"Use this URL in Magic Hour: {public_url}/webhook") except ImportError: print("💡 Install pyngrok for public URL: !pip install pyngrok") print("Or use webhook.site for easier testing") ``` ## Step 2: Make Your Endpoint Publicly Accessible Your webhook endpoint needs to be accessible from the internet. Choose one of these options: **Perfect for local development and testing** **Install ngrok:** ```bash theme={null} # macOS brew install ngrok # Windows/Linux - download from https://ngrok.com/download ``` **Start your server:** ```bash Python theme={null} python webhook_server.py # or uvicorn main:app --host 0.0.0.0 --port 8000 ``` ```bash Node.js theme={null} node webhook_server.js ``` **Expose it publicly:** ```bash theme={null} ngrok http 8000 ``` **Copy the HTTPS URL** (e.g., `https://abc123.ngrok.io`) **Perfect for Colab/Jupyter users and quick testing!** No server setup required. 1. Go to [webhook.site](https://webhook.site) 2. Copy the unique URL provided (e.g., `https://webhook.site/abc-123-def`) 3. Use this URL directly in Magic Hour webhook setup 4. View received webhooks in real-time in your browser 5. Perfect for testing and debugging webhook payloads **Pros**: Instant setup, no code needed, great for testing\ **Cons**: Public URL, not suitable for production **For production deployments:** Deploy your webhook handler to: * **Cloud platforms**: AWS Lambda, Google Cloud Functions, Azure Functions * **Server hosting**: DigitalOcean, Heroku, Railway * **Container services**: Docker, Kubernetes Ensure your production server: * Has a valid SSL certificate (HTTPS required) * Can handle incoming POST requests * Returns responses within 10 seconds * Has proper error handling and logging ## Step 3: Register Your Webhook with Magic Hour Go to Magic Hour [Developer Hub](https://magichour.ai/developer?tab=webhooks), and click **Create Webhook** Webhook Table Enter your webhook details: * **Endpoint URL**: Your public HTTPS URL (e.g., `https://abc123.ngrok.io/webhook`) * **Events**: Select the events you want to receive: * `video.started` - When video processing begins * `video.completed` - When video is ready for download * `video.errored` - When video processing fails * `image.started` - When image processing begins * `image.completed` - When image is ready for download * `image.errored` - When image processing fails * `audio.started` - When audio processing begins * `audio.completed` - When audio is ready for download * `audio.errored` - When audio processing fails Create webhook modal Click **Create Webhook** **Important**: Copy and save the webhook secret - you'll need this for security verification later. Webhook Secret Store this secret securely! It's used to verify that webhooks are actually from Magic Hour. ## Step 4: Test Your Webhook End-to-End Now let's verify everything works by making a real API call and watching for the webhook. **If using your own server**: Watch the console logs ```bash theme={null} # Your server should show: 🚀 Webhook server running on http://localhost:8000 ``` **If using Colab/Jupyter**: Watch the cell output for webhook events **If using webhook.site**: Keep the browser tab open to see incoming requests in real-time Create a simple image to trigger webhook events: ```python Python/Colab theme={null} from magic_hour import Client client = Client(token="your-api-key") # Create a simple AI image - this will trigger webhooks result = client.v1.ai_image_generator.generate( image_count=1, aspect_ratio="1:1", style={"prompt": "A cute cat wearing sunglasses", "tool": "ai-anime-generator"} ) print(f"Job created! ID: {result.id}") print("Watch your webhook endpoint for events...") # In Colab, you'll see the webhook events appear in the cell output above ``` ```bash cURL theme={null} curl -X POST "https://api.magichour.ai/v1/ai-image-generator" \ -H "Authorization: Bearer your-api-key" \ -H "Content-Type: application/json" \ -d '{ "image_count": 1, "aspect_ratio": "1:1", "style": { "prompt": "A cute cat wearing sunglasses", "tool": "ai-anime-generator" } }' ``` ```python webhook.site Testing theme={null} # If using webhook.site, you don't need a server # Just make the API call and watch the webhook.site browser tab from magic_hour import Client client = Client(token="your-api-key") result = client.v1.ai_image_generator.generate( image_count=1, aspect_ratio="1:1", style={"prompt": "A cute cat wearing sunglasses", "tool": "ai-anime-generator"} ) print(f"Job created! ID: {result.id}") print("Check your webhook.site browser tab for the webhook delivery!") ``` Within seconds, you should see webhook events in your console or webhook.site: ```json theme={null} { "type": "image.completed", "payload": { "id": "clx7uu86w0a5qp55yxz315r6r", "status": "complete", "downloads": [ { "url": "https://videos.magichour.ai/id/output.png", "expires_at": "2024-10-19T05:16:19.027Z" } ] } } ``` **Success!** 🎉 Your webhook is working end-to-end. ## Understanding Webhook Retries If your endpoint doesn't respond with a 2xx status code, Magic Hour will retry delivery: * **Duration**: Up to 24 hours * **Pattern**: Exponential backoff (1s, 2s, 4s, 8s, ...) * **After 24 hours**: Event marked as failed, no more retries **Disabled Webhooks**: If a webhook is disabled, pending events are skipped and marked as failed after 24 hours. ### Best Practices for Reliable Webhooks ✅ **Do:** * Return 2xx status codes within 10 seconds * Process events asynchronously (respond fast, process later) * Implement idempotent processing (handle duplicate events) * Log all webhook events for debugging ❌ **Don't:** * Perform long-running operations before responding * Return non-2xx codes for successful receipt * Assume events are delivered exactly once * Block the response while processing ## Production Webhook Handler For production, implement robust error handling and background processing: ```python Python (Production) theme={null} from fastapi import FastAPI, Request, BackgroundTasks import logging app = FastAPI() logger = logging.getLogger(__name__) @app.post("/webhook") async def webhook_handler(request: Request, background_tasks: BackgroundTasks): try: event = await request.json() # Log the event logger.info(f"Received webhook: {event['type']}", extra={ 'job_id': event['payload'].get('id'), 'status': event['payload'].get('status') }) # Respond immediately background_tasks.add_task(process_webhook_event, event) return {"success": True} except Exception as e: logger.error(f"Webhook error: {e}") return {"error": "Internal error"}, 500 async def process_webhook_event(event): """Process webhook in background""" try: event_type = event['type'] payload = event['payload'] if event_type == 'video.completed': # Update database await update_job_status(payload['id'], 'completed') # Notify user await notify_user_completion(payload) elif event_type == 'video.errored': # Handle error await update_job_status(payload['id'], 'failed') await notify_user_error(payload) except Exception as e: logger.error(f"Background processing failed: {e}") ``` ```javascript Node.js (Production) theme={null} const express = require("express"); const { createLogger, format, transports } = require("winston"); const app = express(); const logger = createLogger({ level: "info", format: format.json(), transports: [new transports.File({ filename: "webhook.log" }), new transports.Console()], }); app.use(express.json()); app.post("/webhook", async (req, res) => { try { const { type, payload } = req.body; // Log the event logger.info("Received webhook", { eventType: type, jobId: payload.id, status: payload.status, }); // Respond immediately res.status(200).json({ success: true }); // Process asynchronously processWebhookEvent(type, payload); } catch (error) { logger.error("Webhook error", { error: error.message }); res.status(500).json({ error: "Internal error" }); } }); async function processWebhookEvent(type, payload) { try { if (type === "video.completed") { // Update database await updateJobStatus(payload.id, "completed"); // Notify user await notifyUserCompletion(payload); } else if (type === "video.errored") { // Handle error await updateJobStatus(payload.id, "failed"); await notifyUserError(payload); } } catch (error) { logger.error("Background processing failed", { error: error.message }); } } const port = process.env.PORT || 8000; app.listen(port, () => { logger.info(`Webhook server running on port ${port}`); }); ``` ## Testing Locally Before registering with Magic Hour, test your handler locally: ```bash theme={null} # Start your server python webhook_server.py # or node webhook_server.js # In another terminal, send a test event curl -X POST http://localhost:8000/webhook \ -H "Content-Type: application/json" \ -d '{ "type": "video.completed", "payload": { "id": "test-job-123", "status": "complete", "downloads": [ { "url": "https://videos.magichour.ai/test/output.mp4", "expires_at": "2024-12-01T12:00:00Z" } ] } }' ``` You should see the event logged in your server console. ## Webhook Handler Requirements Your webhook endpoint must: ### 1. Accept POST Requests ```python theme={null} @app.post("/webhook") # Must be POST async def webhook_handler(request: Request): ... ``` ### 2. Parse JSON Payload ```python theme={null} event = await request.json() # event = { "type": "...", "payload": {...} } ``` ### 3. Return 2xx Status Code ```python theme={null} return {"success": True} # Returns 200 # Magic Hour interprets this as successful delivery ``` ### 4. Respond Within 10 Seconds ```python theme={null} # ✅ Good: Respond fast, process later background_tasks.add_task(process_event, event) return {"success": True} # ❌ Bad: Long processing blocks response process_event_synchronously(event) # This might timeout! return {"success": True} ``` ## Troubleshooting **Webhook not receiving events?** * ✅ Check your endpoint URL is publicly accessible * ✅ Ensure your server returns HTTP 2xx status codes * ✅ Verify the webhook is enabled in Developer Hub * ✅ Check server logs for errors * ✅ Test with ngrok or webhook.site first **Colab/Jupyter specific issues:** * ✅ Install required packages: `!pip install fastapi uvicorn nest-asyncio pyngrok` * ✅ Make sure the server thread started successfully * ✅ Check if ngrok tunnel is active and accessible * ✅ Try webhook.site as an alternative for quick testing **AsyncIO errors in notebooks?** * ✅ Use the Colab/Jupyter code version with `nest_asyncio.apply()` * ✅ Don't run `uvicorn.run()` directly in notebooks - use the threading approach ## Next Steps Now that your webhook is working: Add signature verification to ensure webhooks are from Magic Hour Learn about all available webhook events and their payloads Complete webhook API documentation Build a complete integration from scratch *** **Need help?** Join our [Discord community](https://discord.com/invite/JX5rgsZaJp) or email [support@magichour.ai](mailto:support@magichour.ai) # Webhook Event Types Source: https://docs.magichour.ai/integration/webhook/event-types Complete reference of all webhook events, payloads, and when they're triggered. ## Event Structure All webhook events follow a consistent structure with two main fields: ```json theme={null} { "type": "event.name", "payload": { // Event-specific data matching API response format } } ``` ### Event Naming Convention Events use dot notation: `{resource}.{action}` * **Resource**: `video`, `image`, `audio` * **Action**: `started`, `completed`, `errored` ### Payload Format The `payload` field contains the same data structure as the corresponding API endpoint: * **Video events**: Match [`GET /v1/video-projects/:id`](/api-reference/video-projects/get-video-details) * **Image events**: Match [`GET /v1/image-projects/:id`](/api-reference/image-projects/get-image-details) * **Audio events**: Match [`GET /v1/audio-projects/:id`](/api-reference/audio-projects/get-audio-details) **Consistent Data**: Webhook payloads use the exact same format as API responses, making it easy to reuse your existing data handling code. ## Video Events Video events are triggered during the lifecycle of video processing jobs. All payloads match the [`GET /v1/video-projects/:id`](/api-reference/video-projects/get-video-details) response format. ### `video.started` **When triggered**: Video processing begins (job moves from `queued` to `rendering`) **Use cases**: * Update UI to show "processing" status * Log job start time for analytics * Send user notification that processing began **Key payload fields**: ```json theme={null} { "type": "video.started", "payload": { "id": "clx7uu86w0a5qp55yxz315r6r", "status": "rendering", "type": "ANIMATION", "created_at": "2024-10-19T05:10:19.027Z", "width": 512, "height": 960, "credits_charged": 450, "downloads": [], "error": null } } ``` **Processing Time**: Video processing can take 30 seconds to several minutes depending on length and complexity. ### `video.completed` **When triggered**: Video processing finishes successfully **Use cases**: * Download the completed video * Update database with completion status * Send user notification with download link * Trigger downstream workflows **Key payload fields**: ```json theme={null} { "type": "video.completed", "payload": { "id": "clx7uu86w0a5qp55yxz315r6r", "status": "complete", "type": "ANIMATION", "credits_charged": 450, "downloads": [ { "url": "https://videos.magichour.ai/clx7uu86w0a5qp55yxz315r6r/output.mp4", "expires_at": "2024-10-19T05:16:19.027Z" } ], "error": null } } ``` **Download URLs Expire**: Video download URLs typically expire after 24 hours. Download and store videos promptly or use the API to get fresh URLs. ### `video.errored` **When triggered**: Video processing fails due to an error **Use cases**: * Log error details for debugging * Retry processing if appropriate * Notify user of failure * Update job status in database **Key payload fields**: ```json theme={null} { "type": "video.errored", "payload": { "id": "clx7uu86w0a5qp55yxz315r6r", "status": "error", "downloads": [], "error": { "code": "invalid_video_file", "message": "The video file contains invalid data. Please try a different file." } } } ``` **Common error codes**: * `invalid_video_file` - Corrupted or unsupported video format * `file_too_large` - Video file exceeds size limits * `insufficient_credits` - Not enough credits to process * `invalid_parameters` - Invalid width, height, or other parameters * `processing_timeout` - Processing took too long and was terminated **No Charges**: You're never charged credits when processing fails with an error. ## Image Events Image events track the lifecycle of image processing jobs. All payloads match the [`GET /v1/image-projects/:id`](/api-reference/image-projects/get-image-details) response format. ### `image.started` **When triggered**: Image processing begins (job moves from `queued` to `rendering`) **Use cases**: * Show processing indicator in UI * Track processing start time * Update job status in database **Key payload fields**: ```json theme={null} { "type": "image.started", "payload": { "id": "clx8abc123def456ghi789", "status": "rendering", "type": "AI_IMAGE_GENERATOR", "created_at": "2024-10-19T05:10:19.027Z", "width": 1024, "height": 1024, "credits_charged": 10, "downloads": [], "error": null } } ``` **Fast Processing**: Some image modes (like AI Image Generator) process so quickly that `image.started` events may not be sent. Always handle cases where you only receive `image.completed`. ### `image.completed` **When triggered**: Image processing finishes successfully **Use cases**: * Download the generated image * Display result to user * Update completion status * Trigger post-processing workflows **Key payload fields**: ```json theme={null} { "type": "image.completed", "payload": { "id": "clx8abc123def456ghi789", "status": "complete", "type": "AI_IMAGE_GENERATOR", "image_count": 1, "credits_charged": 5, "downloads": [ { "url": "https://videos.magichour.ai/clx8abc123def456ghi789/output.png", "expires_at": "2024-10-19T05:16:19.027Z" } ], "error": null } } ``` **Fast Processing**: Most image operations complete within 5-30 seconds, much faster than video processing. ### `image.errored` **When triggered**: Image processing fails due to an error **Use cases**: * Display error message to user * Log error for debugging * Retry with different parameters * Update job status **Key payload fields**: ```json theme={null} { "type": "image.errored", "payload": { "id": "clx8abc123def456ghi789", "status": "error", "downloads": [], "error": { "code": "no_source_face", "message": "Please use an image with a detectable face" } } } ``` **Common error codes**: * `no_source_face` - Face required but not detected in image * `invalid_file_format` - Unsupported image format * `file_too_large` - Image file exceeds size limits * `insufficient_credits` - Not enough credits for operation * `invalid_parameters` - Invalid dimensions or other parameters * `content_policy_violation` - Image violates content guidelines **No Charges**: Failed image processing never consumes credits. ## Audio Events Audio events track the lifecycle of audio processing jobs. All payloads match the [`GET /v1/audio-projects/:id`](/api-reference/audio-projects/get-audio-details) response format. ### `audio.started` **When triggered**: Audio processing begins **Key payload fields**: ```json theme={null} { "type": "audio.started", "payload": { "id": "clx9audio123voice456", "status": "rendering", "type": "AI_VOICE_GENERATOR", "created_at": "2024-10-19T05:10:19.027Z", "credits_charged": 5, "downloads": [], "error": null } } ``` ### `audio.completed` **When triggered**: Audio processing finishes successfully **Key payload fields**: ```json theme={null} { "type": "audio.completed", "payload": { "id": "clx9audio123voice456", "status": "complete", "credits_charged": 5, "downloads": [ { "url": "https://audio.magichour.ai/clx9audio123voice456/output.mp3", "expires_at": "2024-10-19T05:16:19.027Z" } ], "error": null } } ``` ### `audio.errored` **When triggered**: Audio processing fails **Common error codes**: * `text_too_long` - Input text exceeds maximum length * `invalid_voice` - Requested voice not available * `insufficient_credits` - Not enough credits * `processing_timeout` - Audio generation took too long ## Event Handling Patterns ### Basic Event Router ```python theme={null} async def handle_webhook_event(event): event_type = event['type'] payload = event['payload'] match event_type: # Video events case 'video.started': await handle_video_started(payload) case 'video.completed': await handle_video_completed(payload) case 'video.errored': await handle_video_errored(payload) # Image events case 'image.started': await handle_image_started(payload) case 'image.completed': await handle_image_completed(payload) case 'image.errored': await handle_image_errored(payload) # Audio events case 'audio.started': await handle_audio_started(payload) case 'audio.completed': await handle_audio_completed(payload) case 'audio.errored': await handle_audio_errored(payload) case _: print(f"Unknown event type: {event_type}") ``` ### Database Update Pattern ```python theme={null} async def handle_completion_event(event): payload = event['payload'] job_id = payload['id'] # Update job status in database # (the payload has no completed_at field - record your own timestamp) await db.execute( "UPDATE jobs SET status = ?, download_url = ?, completed_at = datetime('now') WHERE magic_hour_id = ?", (payload['status'], payload['downloads'][0]['url'], job_id) ) # Notify user await notify_user_completion(job_id) ``` ### Error Handling Pattern ```python theme={null} async def handle_error_event(event): payload = event['payload'] error = payload['error'] # Log error details logger.error(f"Job {payload['id']} failed", extra={ 'job_id': payload['id'], 'error_code': error['code'], 'error_message': error['message'], 'job_type': payload['type'] }) # Update database await db.execute( "UPDATE jobs SET status = 'failed', error_code = ?, error_message = ? WHERE magic_hour_id = ?", (error['code'], error['message'], payload['id']) ) # Notify user of failure await notify_user_error(payload['id'], error) ``` ## Next Steps Set up your first webhook with end-to-end testing Add signature verification for production security Deploy robust webhook handlers to production Complete webhook API documentation # Webhook Quickstart Source: https://docs.magichour.ai/integration/webhook/overview Set up webhooks and test them end-to-end in under 10 minutes By the end of this guide, you'll have: * Created a webhook endpoint in your application * Registered it with Magic Hour * Tested it with a real API call * Verified webhook delivery works ## How Webhooks Work Webhooks let you receive real-time notifications when your API requests complete, instead of polling for status updates. ```mermaid theme={null} sequenceDiagram participant app as Your Application participant mh as Magic Hour app->>mh: 1. Create video/image job mh-->>app: 2. Job ID returned immediately mh->>mh: 3. Process job in background mh->>app: 4. Send webhook when complete app->>mh: 5. Return 2xx Response ``` ## Step 1: Create Your Webhook Endpoint First, let's create a simple webhook handler that can receive and process events. **Using Jupyter/Colab?** The server examples below are for standalone deployment. For notebook testing: 1. Use [webhook.site](https://webhook.site) for instant webhook testing (no code needed). See the "webhook.site" tab in Step 2 2. Or use the notebook-friendly code in the "Colab/Jupyter" tab below ```python Python (FastAPI) theme={null} from fastapi import FastAPI, Request import json app = FastAPI() @app.post("/webhook") async def webhook_handler(request: Request): # Get the event data event = await request.json() # Log the event for testing print(f"Received event: {event['type']}") print(f"Payload: {json.dumps(event['payload'], indent=2)}") # Handle different event types match event['type']: case 'video.started': print('🎬 Video processing started') case 'video.completed': print('✅ Video processing completed') # Download URL available in event['payload']['downloads'] case 'video.errored': print('❌ Video processing failed') case 'image.completed': print('🖼️ Image processing completed') case 'image.errored': print('❌ Image processing failed') # Always return success return {"success": True} if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000) ``` ```typescript Node.js (Express) theme={null} const express = require("express"); const app = express(); app.use(express.json()); app.post("/webhook", (req, res) => { const { type, payload } = req.body; // Log the event for testing console.log(`Received event: ${type}`); console.log(`Payload:`, JSON.stringify(payload, null, 2)); // Handle different event types switch (type) { case "video.started": console.log("🎬 Video processing started"); break; case "video.completed": console.log("✅ Video processing completed"); // Download URL available in payload.downloads break; case "video.errored": console.log("❌ Video processing failed"); break; case "image.completed": console.log("🖼️ Image processing completed"); break; case "image.errored": console.log("❌ Image processing failed"); break; } // Always return success res.status(200).json({ success: true }); }); const port = 8000; app.listen(port, () => { console.log(`🚀 Webhook server running on http://localhost:${port}`); }); ``` ```python Colab/Jupyter theme={null} # Notebook-friendly webhook server from fastapi import FastAPI, Request import json import nest_asyncio import threading import uvicorn import time # Install required packages first: # !pip install fastapi uvicorn nest-asyncio pyngrok # Allow nested event loops (required for notebooks) nest_asyncio.apply() app = FastAPI() @app.post("/webhook") async def webhook_handler(request: Request): # Get the event data event = await request.json() # Log the event for testing print(f"Received event: {event['type']}") print(f"Payload: {json.dumps(event['payload'], indent=2)}") # Handle different event types match event['type']: case 'video.started': print('🎬 Video processing started') case 'video.completed': print('✅ Video processing completed') case 'video.errored': print('❌ Video processing failed') case 'image.completed': print('🖼️ Image processing completed') case 'image.errored': print('❌ Image processing failed') return {"success": True} # Notebook-friendly server startup def run_server(): uvicorn.run(app, host="0.0.0.0", port=8000, log_level="info") # Start server in background thread server_thread = threading.Thread(target=run_server, daemon=True) server_thread.start() # Wait for server to start time.sleep(2) print("🚀 Webhook server running on http://localhost:8000") # For Colab: Use ngrok to make it public try: from pyngrok import ngrok public_url = ngrok.connect(8000) print(f"📡 Public URL: {public_url}") print(f"Use this URL in Magic Hour: {public_url}/webhook") except ImportError: print("💡 Install pyngrok for public URL: !pip install pyngrok") print("Or use webhook.site for easier testing") ``` ## Step 2: Make Your Endpoint Publicly Accessible Your webhook endpoint needs to be accessible from the internet. For testing, use one of these options: **Install ngrok:** ```bash theme={null} # macOS brew install ngrok # Windows/Linux - download from https://ngrok.com/download ``` **Start your server:** ```bash Python theme={null} python webhook_server.py # or uvicorn main:app --host 0.0.0.0 --port 8000 ``` ```bash Node.js theme={null} node webhook_server.js ``` **Expose it publicly:** ```bash theme={null} ngrok http 8000 ``` **Copy the HTTPS URL** (e.g., `https://abc123.ngrok.io`) **Perfect for Colab/Jupyter users!** No server setup required. 1. Go to [webhook.site](https://webhook.site) 2. Copy the unique URL provided (e.g., `https://webhook.site/abc-123-def`) 3. Use this URL directly in Magic Hour webhook setup 4. View received webhooks in real-time in your browser 5. Perfect for testing and debugging webhook payloads **Pros**: Instant setup, no code needed, great for testing\ **Cons**: Public URL, not suitable for production **For notebook environments with the code above:** 1. **Install dependencies:** ```python theme={null} !pip install fastapi uvicorn nest-asyncio pyngrok ``` 2. **Run the notebook-friendly code** from Step 1 3. **Get your public URL:** * The code will automatically create an ngrok tunnel * Copy the public URL shown (e.g., `https://abc123.ngrok.io/webhook`) 4. **Use the URL** in Magic Hour webhook setup **Note**: Free ngrok URLs change each time you restart. For persistent URLs, sign up for a free ngrok account. ## Step 3: Register Your Webhook Go to Magic Hour [Developer Hub](https://magichour.ai/developer?tab=webhooks), and click **Create Webhook** Webhook Table Enter your webhook details: * **Endpoint URL**: Your public HTTPS URL (e.g., `https://abc123.ngrok.io/webhook`) * **Events**: Select the events you want to receive: * `video.started` - When video processing begins * `video.completed` - When video is ready for download * `video.errored` - When video processing fails * `image.started` - When image processing begins * `image.completed` - When image is ready for download * `image.errored` - When image processing fails * `audio.started` - When audio processing begins * `audio.completed` - When audio is ready for download * `audio.errored` - When audio processing fails Create webhook modal Click **Create Webhook** **Important**: Copy and save the webhook secret - you'll need this for security verification. Webhook Secret Store this secret securely! It's used to verify that webhooks are actually from Magic Hour. ## Step 4: Test Your Webhook End-to-End Now let's verify everything works by making a real API call and watching for the webhook. **If using your own server**: Watch the console logs ```bash theme={null} # Your server should show: 🚀 Webhook server running on http://localhost:8000 ``` **If using Colab/Jupyter**: Watch the cell output for webhook events **If using webhook.site**: Keep the browser tab open to see incoming requests in real-time Create a simple image to trigger webhook events: ```python Python/Colab theme={null} from magic_hour import Client client = Client(token="your-api-key") # Create a simple AI image - this will trigger webhooks result = client.v1.ai_image_generator.generate( image_count=1, aspect_ratio="1:1", style={"prompt": "A cute cat wearing sunglasses", "tool": "ai-anime-generator"} ) print(f"Job created! ID: {result.id}") print("Watch your webhook endpoint for events...") # In Colab, you'll see the webhook events appear in the cell output above ``` ```bash cURL theme={null} curl -X POST "https://api.magichour.ai/v1/ai-image-generator" \ -H "Authorization: Bearer your-api-key" \ -H "Content-Type: application/json" \ -d '{ "image_count": 1, "aspect_ratio": "1:1", "style": { "prompt": "A cute cat wearing sunglasses", "tool": "ai-anime-generator" } }' ``` ```python webhook.site Testing theme={null} # If using webhook.site, you don't need a server # Just make the API call and watch the webhook.site browser tab from magic_hour import Client client = Client(token="your-api-key") result = client.v1.ai_image_generator.generate( image_count=1, aspect_ratio="1:1", style={"prompt": "A cute cat wearing sunglasses", "tool": "ai-anime-generator"} ) print(f"Job created! ID: {result.id}") print("Check your webhook.site browser tab for the webhook delivery!") ``` Within seconds, you should see webhook events in your console: ``` Received event: image.completed 🖼️ Image processing completed Payload: { "id": "clx7uu86w0a5qp55yxz315r6r", "status": "complete", "downloads": [ { "url": "https://videos.magichour.ai/id/output.png", "expires_at": "2024-10-19T05:16:19.027Z" } ] } ``` **Success!** 🎉 Your webhook is working end-to-end. ## Step 5: Download Your Result Your webhook received the download URL. Let's grab the generated image: ```python Python theme={null} import requests # Extract download URL from webhook payload download_url = "https://videos.magichour.ai/id/output.png" # Download the image response = requests.get(download_url) with open("generated_image.png", "wb") as f: f.write(response.content) print("✅ Image downloaded as generated_image.png") ``` ```bash cURL theme={null} # Use the URL from your webhook payload curl -o generated_image.png "https://videos.magichour.ai/id/output.png" echo "✅ Image downloaded as generated_image.png" ``` ## 🎉 Congratulations! You've successfully: * ✅ Created a webhook endpoint * ✅ Registered it with Magic Hour * ✅ Tested it with a real API call * ✅ Received webhook notifications * ✅ Downloaded the generated content ## Next Steps Add signature verification to ensure webhooks are from Magic Hour See all available webhook events and their payloads Deploy your webhook handler to production Explore the complete webhook API reference ## Troubleshooting **Webhook not receiving events?** * ✅ Check your endpoint URL is publicly accessible * ✅ Ensure your server returns HTTP 2xx status codes * ✅ Verify the webhook is enabled in Developer Hub * ✅ Check server logs for errors **Colab/Jupyter specific issues:** * ✅ Install required packages: `!pip install fastapi uvicorn nest-asyncio pyngrok` * ✅ Make sure the server thread started successfully * ✅ Check if ngrok tunnel is active and accessible * ✅ Try webhook.site as an alternative for quick testing **AsyncIO errors in notebooks?** * ✅ Make sure you're using the Colab/Jupyter code version with `nest_asyncio.apply()` * ✅ Don't run `uvicorn.run()` directly in notebooks - use the threading approach shown above **Need help?** Contact support at [support@magichour.ai](mailto:support@magichour.ai) # Securing Your Webhook Handler Source: https://docs.magichour.ai/integration/webhook/secure-handler Verify webhook authenticity with HMAC-SHA256 signature validation to prevent malicious requests. ## Why Webhook Security Matters Without signature verification, anyone who discovers your webhook URL could send fake events to your application, potentially causing: * ❌ Fake completion events triggering incorrect business logic * ❌ Data corruption from processing invalid events * ❌ Unauthorized access to your application * ❌ Security vulnerabilities **✅ Solution**: Verify that every webhook is actually from Magic Hour using cryptographic signatures. **Production Requirement**: Always verify webhook signatures in production. This is critical for security. ## How Signature Verification Works Magic Hour signs every webhook using HMAC-SHA256 with your webhook secret: 1. **Magic Hour creates** a signed payload: `{timestamp}.{json_payload}` 2. **Magic Hour computes** HMAC-SHA256 signature using your webhook secret 3. **Magic Hour sends** the webhook with signature and timestamp in headers 4. **Your app recreates** the same signed payload 5. **Your app computes** the same HMAC-SHA256 signature 6. **Your app compares** signatures - if they match, the webhook is authentic ## Security Headers Every webhook includes two security headers to help you verify authenticity: ### `magic-hour-event-signature` The HMAC-SHA256 signature computed using your webhook secret and the signed payload. **Example:** ```http theme={null} magic-hour-event-signature: d1747eb9491aadeac6173f1c9fd5f4aff87c8981ac53d87b0830199221ff50af ``` **What it is**: A 64-character hexadecimal string representing the HMAC-SHA256 hash. ### `magic-hour-event-timestamp` Unix timestamp (seconds since epoch) when Magic Hour sent the webhook. **Example:** ```http theme={null} magic-hour-event-timestamp: 1729314984 ``` **What it's for**: * Prevents replay attacks by rejecting old webhooks * Recommended tolerance: Accept webhooks within 5 minutes of current time * If timestamp is too old or too far in the future, reject the webhook **Header Case**: HTTP headers are case-insensitive. `Magic-Hour-Event-Signature` and `magic-hour-event-signature` are equivalent. **Optional but Recommended**: Signature verification is not required to receive webhooks, but it's strongly recommended for production to ensure authenticity. **What to do**: Get three pieces of information from the incoming webhook request: 1. **Signature header**: `magic-hour-event-signature` 2. **Timestamp header**: `magic-hour-event-timestamp` 3. **Raw JSON body**: The entire request body as a string (not parsed yet) **Code example:** ```python theme={null} signature = request.headers.get('magic-hour-event-signature') timestamp = request.headers.get('magic-hour-event-timestamp') raw_body = await request.body() # Raw bytes, not parsed JSON ``` **Critical**: You must use the raw request body exactly as received. Do NOT parse the JSON and re-stringify it, as this can change formatting and break signature verification. **What to do**: Combine the timestamp and raw payload into a single string for signature computation. **Format**: `{timestamp}.{raw_json_payload}` **Example signed payload:** ``` 1729314984.{"type":"video.completed","payload":{"id":"clx123","status":"complete","downloads":[{"url":"https://videos.magichour.ai/output.mp4"}]}} ``` **Code example:** ```python theme={null} # Convert raw body to string if it's bytes payload_string = raw_body.decode('utf-8') if isinstance(raw_body, bytes) else raw_body # Create signed payload signed_payload = f"{timestamp}.{payload_string}" ``` **Why**: The signed payload is what Magic Hour used to create the signature. You need to recreate it exactly the same way. **What to do**: Generate an HMAC-SHA256 hash using: * **Key**: Your webhook secret (from Magic Hour Developer Hub) * **Message**: The signed payload you just created **Algorithm**: HMAC-SHA256 (Hash-based Message Authentication Code with SHA-256) ```python Python theme={null} import hmac import hashlib import os # Get your webhook secret from environment variable webhook_secret = os.getenv("MAGIC_HOUR_WEBHOOK_SECRET") def compute_signature(signed_payload: str, secret: str) -> str: """ Compute HMAC-SHA256 signature Args: signed_payload: The timestamp.payload string secret: Your webhook secret from Magic Hour Returns: 64-character hexadecimal signature string """ return hmac.new( secret.encode('utf-8'), # Convert secret to bytes signed_payload.encode('utf-8'), # Convert payload to bytes hashlib.sha256 # Use SHA256 hash ).hexdigest() # Return as hex string # Usage expected_signature = compute_signature(signed_payload, webhook_secret) ``` ```typescript Node.js theme={null} const crypto = require("crypto"); function computeSignature(signedPayload: string, webhookSecret: string): string { return crypto.createHmac("sha256", webhookSecret).update(signedPayload, "utf8").digest("hex"); } // Usage const webhookSecret = process.env.MAGIC_HOUR_WEBHOOK_SECRET; const signature = computeSignature(signedPayload, webhookSecret); ``` ```go Go theme={null} package main import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "os" ) func computeSignature(signedPayload, webhookSecret string) string { mac := hmac.New(sha256.New, []byte(webhookSecret)) mac.Write([]byte(signedPayload)) return hex.EncodeToString(mac.Sum(nil)) } // Usage func main() { webhookSecret := os.Getenv("MAGIC_HOUR_WEBHOOK_SECRET") signature := computeSignature(signedPayload, webhookSecret) } ``` ```rust Rust theme={null} use hmac::{Hmac, Mac}; use sha2::Sha256; use std::env; type HmacSha256 = Hmac; fn compute_signature(signed_payload: &str, webhook_secret: &str) -> String { let mut mac = HmacSha256::new_from_slice(webhook_secret.as_bytes()) .expect("HMAC can take key of any size"); mac.update(signed_payload.as_bytes()); hex::encode(mac.finalize().into_bytes()) } // Usage fn main() { let webhook_secret = env::var("MAGIC_HOUR_WEBHOOK_SECRET") .expect("MAGIC_HOUR_WEBHOOK_SECRET must be set"); let signature = compute_signature(&signed_payload, &webhook_secret); } ``` **What to do**: Compare your computed signature with the signature Magic Hour sent in the header. **Important**: Use a constant-time comparison to prevent timing attacks: ```python theme={null} import hmac import time # Get the signature from headers received_signature = request.headers.get('magic-hour-event-signature') # Compare signatures (constant-time comparison for security) is_valid = hmac.compare_digest(expected_signature, received_signature) if not is_valid: # Signature doesn't match - reject the webhook raise HTTPException(status_code=401, detail="Invalid signature") ``` **Why constant-time comparison**: Regular string comparison (`==`) can leak timing information that attackers could exploit. `hmac.compare_digest()` prevents this. **What to do**: Check that the webhook was sent recently (within 5 minutes is recommended). **Why**: Prevents replay attacks where someone could resend an old valid webhook. ```python theme={null} import time # Get current time current_time = int(time.time()) # Check if timestamp is within 5 minutes (300 seconds) timestamp_age = abs(current_time - int(timestamp)) if timestamp_age > 300: # Timestamp too old or too far in future - reject raise HTTPException(status_code=401, detail="Timestamp too old") ``` **Adjust tolerance**: You can adjust the 300-second (5-minute) window based on your needs, but keep it reasonable for security. ## Complete Secure Handler Implementation ```python Python (FastAPI - Complete) theme={null} from fastapi import FastAPI, Request, HTTPException import hmac import hashlib import time import json import os app = FastAPI() # Get webhook secret from environment WEBHOOK_SECRET = os.getenv("MAGIC_HOUR_WEBHOOK_SECRET") def verify_webhook(signature: str, timestamp: str, raw_body: bytes) -> bool: """ Verify webhook signature and timestamp Args: signature: The magic-hour-event-signature header value timestamp: The magic-hour-event-timestamp header value raw_body: The raw request body as bytes Returns: True if webhook is valid, False otherwise """ # Step 1: Verify timestamp is recent (within 5 minutes) current_time = int(time.time()) timestamp_age = abs(current_time - int(timestamp)) if timestamp_age > 300: # 5 minutes in seconds print(f"❌ Timestamp too old: {timestamp_age} seconds") return False # Step 2: Create signed payload # Format: "{timestamp}.{raw_json_payload}" payload_string = raw_body.decode('utf-8') signed_payload = f"{timestamp}.{payload_string}" # Step 3: Compute expected signature expected_signature = hmac.new( WEBHOOK_SECRET.encode('utf-8'), # Secret as bytes signed_payload.encode('utf-8'), # Signed payload as bytes hashlib.sha256 # SHA256 hash function ).hexdigest() # Convert to hex string # Step 4: Compare signatures (constant-time comparison) is_valid = hmac.compare_digest(signature, expected_signature) if not is_valid: print(f"❌ Signature mismatch") print(f" Received: {signature}") print(f" Expected: {expected_signature}") return is_valid @app.post("/webhook") async def secure_webhook_handler(request: Request): """Secure webhook endpoint with signature verification""" # Extract headers signature = request.headers.get('magic-hour-event-signature') timestamp = request.headers.get('magic-hour-event-timestamp') # Validate headers exist if not signature or not timestamp: raise HTTPException( status_code=401, detail="Missing security headers" ) # Get raw body for signature verification raw_body = await request.body() # Verify webhook authenticity if not verify_webhook(signature, timestamp, raw_body): raise HTTPException( status_code=401, detail="Invalid signature or timestamp" ) # Now it's safe to parse and process the JSON event = json.loads(raw_body) print(f"✅ Verified webhook: {event['type']}") # Process the event match event['type']: case 'video.completed': print("Video is ready!") # Your business logic here case 'image.completed': print("Image is ready!") # Your business logic here case _: print(f"Received: {event['type']}") return {"success": True} # Run the server if __name__ == "__main__": import uvicorn if not WEBHOOK_SECRET: raise ValueError("MAGIC_HOUR_WEBHOOK_SECRET must be set") uvicorn.run(app, host="0.0.0.0", port=8000) ``` ```typescript Node.js (Express) theme={null} const express = require("express"); const crypto = require("crypto"); const app = express(); // IMPORTANT: Use express.raw() or express.text() to get raw body for signature verification // Do NOT use express.json() if you need raw body for signature verification app.use(express.text({ type: "application/json" })); const webhookSecret = process.env.MAGIC_HOUR_WEBHOOK_SECRET; function computeSignature(signedPayload) { return crypto.createHmac("sha256", webhookSecret).update(signedPayload).digest("hex"); } app.post("/webhook", (req, res) => { // Get headers const signature = req.headers["magic-hour-event-signature"]; const timestamp = req.headers["magic-hour-event-timestamp"]; if (!signature || !timestamp) { return res.status(401).json({ error: "Missing security headers" }); } if (!webhookSecret) { return res.status(500).json({ error: "Webhook secret not configured" }); } // Get raw body (req.body is now a string, not parsed JSON) const rawBody = req.body; // Create signed payload and verify signature const signedPayload = `${timestamp}.${rawBody}`; const computedSignature = computeSignature(signedPayload); // Constant-time comparison const signatureBuffer = Buffer.from(signature); const computedBuffer = Buffer.from(computedSignature); if ( signatureBuffer.length !== computedBuffer.length || !crypto.timingSafeEqual(signatureBuffer, computedBuffer) ) { return res.status(401).json({ error: "Invalid signature" }); } // Now it's safe to parse the JSON const event = JSON.parse(rawBody); const { type, payload } = event; // Process webhook event console.log(`✅ Verified webhook: ${type}`); // Your business logic here if (type === "video.completed") { console.log("Video is ready!"); } res.status(200).json({ success: true }); }); ``` ```python Colab/Jupyter theme={null} # Secure webhook handler for notebooks from fastapi import FastAPI, Request, HTTPException import hmac import hashlib import time import json import nest_asyncio import threading import uvicorn # Install: !pip install fastapi uvicorn nest-asyncio nest_asyncio.apply() app = FastAPI() # Your webhook secret (get this from Magic Hour Developer Hub) WEBHOOK_SECRET = "your-webhook-secret-here" # Replace with actual secret def verify_signature(signature: str, timestamp: str, raw_payload: bytes) -> bool: # Check timestamp (within 5 minutes) if abs(int(time.time()) - int(timestamp)) > 300: print("❌ Timestamp too old") return False # Create signed payload signed_payload = f"{timestamp}.{raw_payload.decode('utf-8')}" # Compute expected signature expected = hmac.new( WEBHOOK_SECRET.encode('utf-8'), signed_payload.encode('utf-8'), hashlib.sha256 ).hexdigest() # Compare if not hmac.compare_digest(signature, expected): print("❌ Invalid signature") return False return True @app.post("/webhook") async def secure_webhook(request: Request): signature = request.headers.get('magic-hour-event-signature') timestamp = request.headers.get('magic-hour-event-timestamp') if not signature or not timestamp: print("❌ Missing headers") raise HTTPException(status_code=401, detail="Missing headers") raw_payload = await request.body() # Verify signature if not verify_signature(signature, timestamp, raw_payload): raise HTTPException(status_code=401, detail="Invalid signature") # Parse verified payload event = json.loads(raw_payload) print(f"✅ Verified webhook: {event['type']}") print(f"📦 Payload: {json.dumps(event['payload'], indent=2)}") return {"success": True} # Start server def run_server(): uvicorn.run(app, host="0.0.0.0", port=8000, log_level="info") server_thread = threading.Thread(target=run_server, daemon=True) server_thread.start() print("🔒 Secure webhook server started!") print("📝 Remember to set your WEBHOOK_SECRET variable above") ``` ## Testing Your Secure Handler Test signature verification with a sample webhook: ```bash cURL Test theme={null} # Test with valid signature curl -X POST http://localhost:8000/webhook \ -H "Content-Type: application/json" \ -H "magic-hour-event-signature: abc123..." \ -H "magic-hour-event-timestamp: $(date +%s)" \ -d '{"type":"test.event","payload":{"test":true}}' ``` ```python Python Test Script theme={null} import requests import hmac import hashlib import time import json def create_test_webhook(): webhook_secret = "your-webhook-secret" timestamp = str(int(time.time())) payload = {"type": "test.event", "payload": {"test": True}} payload_json = json.dumps(payload, separators=(',', ':')) # Create signature signed_payload = f"{timestamp}.{payload_json}" signature = hmac.new( webhook_secret.encode('utf-8'), signed_payload.encode('utf-8'), hashlib.sha256 ).hexdigest() # Send test webhook response = requests.post( "http://localhost:8000/webhook", json=payload, headers={ "magic-hour-event-signature": signature, "magic-hour-event-timestamp": timestamp } ) print(f"Status: {response.status_code}") print(f"Response: {response.json()}") create_test_webhook() ``` ## Best Practices ### 1. Environment Variables Store your webhook secret securely: ```bash theme={null} # .env file MAGIC_HOUR_WEBHOOK_SECRET=whsec_abc123def456... ``` ### 2. Error Handling Handle verification failures gracefully: ```python theme={null} try: if not verify_webhook_signature(...): logger.warning("Invalid webhook signature", extra={ 'ip': request.client.host, 'user_agent': request.headers.get('user-agent') }) raise HTTPException(status_code=401, detail="Invalid signature") except Exception as e: logger.error(f"Webhook verification failed: {e}") raise HTTPException(status_code=500, detail="Verification error") ``` ### 3. Logging Log security events for monitoring: ```python theme={null} # Log successful verifications logger.info("Webhook verified successfully", extra={ 'event_type': event['type'], 'timestamp': timestamp, 'signature_valid': True }) # Log failed verifications logger.warning("Webhook verification failed", extra={ 'reason': 'invalid_signature', 'timestamp': timestamp, 'ip_address': request.client.host }) ``` ### 4. Rate Limiting Protect against abuse: ```python theme={null} from slowapi import Limiter, _rate_limit_exceeded_handler from slowapi.util import get_remote_address limiter = Limiter(key_func=get_remote_address) @app.post("/webhook") @limiter.limit("100/minute") # Max 100 webhooks per minute per IP async def webhook_handler(request: Request): # ... verification logic ``` ## Troubleshooting ### Common Issues **"Invalid signature" errors:** * ✅ Ensure you're using the raw JSON payload, not parsed/re-stringified * ✅ Check that your webhook secret is correct * ✅ Verify timestamp format (Unix seconds, not milliseconds) * ✅ Make sure you're concatenating `timestamp.payload` correctly **"Timestamp too old" errors:** * ✅ Check server clock synchronization * ✅ Increase tolerance window if needed (but keep it reasonable) * ✅ Ensure timestamp is in seconds, not milliseconds **Missing headers:** * ✅ Verify webhook is registered correctly in Magic Hour * ✅ Check that your endpoint URL is correct * ✅ Ensure headers are being received (log all headers for debugging) ### Debug Mode Add debug logging to troubleshoot signature issues: ```python theme={null} def debug_signature_verification(signature, timestamp, raw_payload, secret): signed_payload = f"{timestamp}.{raw_payload.decode('utf-8')}" expected = hmac.new(secret.encode(), signed_payload.encode(), hashlib.sha256).hexdigest() print(f"🔍 Debug Info:") print(f" Received signature: {signature}") print(f" Expected signature: {expected}") print(f" Timestamp: {timestamp}") print(f" Signed payload: {signed_payload[:100]}...") print(f" Match: {hmac.compare_digest(signature, expected)}") return hmac.compare_digest(signature, expected) ``` ## Next Steps Learn about all available webhook events and their payloads Deploy secure webhook handlers to production Start with basic webhook setup and testing Complete webhook API documentation Need help with webhook security? Contact [support@magichour.ai](mailto:support@magichour.ai) # Welcome to Magic Hour API Source: https://docs.magichour.ai/introduction Magic Hour API documentation - generate videos, images, and audio using AI. Magic Hour API is an API for AI-powered video and image generation. You can run models for creating videos, images, and audio through a single REST API. ## API vs Web App The Magic Hour API provides programmatic access to our most popular AI tools. Here's how it relates to our web application: **🌐 Web App ([magichour.ai](https://magichour.ai)):** * Full suite of 100+ AI tools and features * User-friendly interface with templates and presets * New features launch here first * Perfect for creators and individual use **⚡ API (docs.magichour.ai):** * Core popular tools available programmatically * Built for developers and applications * Same underlying AI models as web app * Features added after web app validation **Shared Dashboard:** All API-generated content automatically appears in your [magichour.ai dashboard](https://magichour.ai/my-library), where you can view, manage, and share your creations. ## Get Started If you're new to Magic Hour, start here to learn the essentials and make your first API call. First request in 3 minutes. All endpoints in one place. Try every API in the browser. Costs, plans, and credit usage. Lifecycle, polling, and webhooks. Generation, editing, audio visualization, and transformation. Generation, editing, enhancement, and subject replacement. Voice generation and cloning.