Effects and catalogue
An effect is one finished, tuned generation recipe: a slug such as golden-hour-pan, the inputs it needs, the variants it can produce and a fixed price for each. The catalogue grows every week.
List effects
Section titled “List effects”curl "https://api.moan.hot/v1/effects?type=video&limit=20" \ -H "Authorization: Bearer $MOANHOT_API_KEY"| Query | Meaning |
|---|---|
type |
video, image or storyboard |
category |
One of the catalogue categories, as listed on each effect |
orientation |
straight or gay |
enabled |
true (default) returns only effects enabled for your account. false returns the whole catalogue |
published_after |
Only effects published after this RFC 3339 time. Use it to sync new releases |
limit, starting_after, ending_before |
Cursor pagination, 1 to 100 per page (default 20) |
The response is a list object:
{ "object": "list", "data": [ { "id": "eff_...", "slug": "...", "...": "..." } ], "has_more": true }To get the next page, pass the last item’s id as starting_after.
GET /effects/{slug} returns a single effect. An unknown or retired slug answers 404 effect_not_found.
Anatomy of an effect
Section titled “Anatomy of an effect”| Field | What it is |
|---|---|
slug |
The stable identifier you send to POST /generations |
type |
video, image or storyboard |
name, description |
Localised text: en always, plus ru, de and es where available |
category, orientation |
Catalogue facets for your own filters |
inputs[] |
Input slots: name (the key in inputs), kind, required, min_short_side_px |
variants[] |
Every priced combination this effect offers (below) |
blend |
true for two-person effects: two input photos, two people, one video |
locations[] |
Storyboards only: key, localised name, poster_url, preview_mp4_url. Otherwise null |
popularity_tier |
top10, trending, new or null |
success_rate |
Share of generations that succeeded over the last 30 days |
assets |
Sell-ready media (below) |
enabled |
Whether your account can create it |
published_at, retires_at |
Release date, and a retirement date when one is scheduled |
Variants
Section titled “Variants”A variant is one combination of axes with one fixed price. Video effects use duration, resolution, audio and prep; image effects use resolution only (1mp or 2mp); storyboards use location and resolution.
| Axis | Values |
|---|---|
duration |
5s, 10s, 15s for video |
resolution |
512p, 640p, 768p for video; 1mp, 2mp for images; 432p, 576p, 720p for storyboards |
audio |
true adds a generated soundtrack |
prep |
true runs frame preparation before animation, for inputs that need it |
location |
Storyboards: the key of one of the effect’s locations[] |
Each variant also has a variant_key (a stable name for the combination, beach.720p for a storyboard), price_micros and typical_seconds, the median time from request to result over the last 30 days. Send the axes, not the key, in POST /generations. A combination that is not listed answers 422 variant_unavailable.
Storyboards
Section titled “Storyboards”A storyboard turns one photo into a short film: several scenes shot in one location, with the person from the photo in every scene, cut together into a single MP4. You choose the location and the resolution; each pair is its own variant with its own price.
The locations are listed on the effect itself, because each storyboard has its own and new ones arrive with catalogue releases. Read them, with their posters and previews, from GET /effects/{slug}:
curl "https://api.moan.hot/v1/effects/STORYBOARD_SLUG" \ -H "Authorization: Bearer $MOANHOT_API_KEY"{ "slug": "STORYBOARD_SLUG", "type": "storyboard", "locations": [ { "key": "beach", "name": { "en": "Beach" }, "poster_url": "https://...", "preview_mp4_url": "https://..." }, { "key": "rooftop", "name": { "en": "Rooftop" }, "poster_url": "https://...", "preview_mp4_url": null } ], "variants": [ { "variant_key": "beach.432p", "location": "beach", "resolution": "432p", "price_micros": 1500000, "typical_seconds": 900 }, { "variant_key": "beach.720p", "location": "beach", "resolution": "720p", "price_micros": 2500000, "typical_seconds": 900 } ]}To list every storyboard and its locations at once, call GET /effects?type=storyboard. A storyboard takes far longer than a video effect, often well over half an hour, so prefer a webhook to polling. See Generations for the request.
Assets you can show your users
Section titled “Assets you can show your users”Every effect carries media ready for your own catalogue page:
poster_urlandposter_sfw_url: still posters. Use the SFW poster on public or indexable pages.preview_mp4_url,preview_webp_url: short looping previews.preview_mp4_urlis always an MP4, ornullwhen the preview exists only in another format; that preview is then inrenditions[].example_inputs[]: sample input photos,{position, url}(position2is the second photo of a blend), so you can show input next to output.renditions[]: the same preview at several sizes and formats (mp4,webm,webp,jpg), withwidthandheight.
Asset URLs are public, content-addressed and safe to cache forever. Hotlink them or copy them to your own CDN.
Prices
Section titled “Prices”GET /prices returns every effect and variant price in the current price-list version, plus the next version and its effective_at when a change is scheduled. See Pricing and billing.
Enabling effects
Section titled “Enabling effects”New effects can be enabled for your account automatically or after you review them, as you choose in the dashboard. A create for an effect you have not enabled answers 403 effect_disabled.
Following new releases
Section titled “Following new releases”Subscribe a webhook endpoint to these events to keep your catalogue in sync without polling:
| Event | When |
|---|---|
effect.published |
A new effect is live and enabled for your account. data is the full effect |
effect.retired |
An effect is scheduled to stop. data.retires_at is at least 14 days ahead |
After retires_at, creates for that slug answer 404 effect_not_found, and generations still queued for it end failed with effect_retired (never charged).
If you prefer polling, call GET /effects?published_after=<last sync time> once a day.