# Lottie Animation Preview & Validator (PNG, GIF, Bodymovin) (`lakadev/lottie-animation-preview-validator`) Actor

Use when an AI coding agent or a developer must see and check a Lottie animation (Bodymovin JSON or .lottie): PNG keyframes, contact sheet, short GIF, plus a structured report of errors and features unsupported on iOS, Android or web. No browser. $0.01 per animation.

- **URL**: https://apify.com/lakadev/lottie-animation-preview-validator.md
- **Developed by:** [Djam](https://apify.com/lakadev) (community)
- **Categories:** Developer tools, Agents, Videos
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 animation analyzeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Lottie Animation Preview & Validator (PNG, GIF, Bodymovin)

**See your Lottie animation and the features known not to work on iOS, Android and web: PNG key frames, a GIF and a machine-readable error report, from a Lottie JSON or a dotLottie file, with no browser.** At $0.01 per animation, and nothing for failures.

Built for **AI coding agents** that write or edit Lottie (Claude Code, Codex, Cursor, through the Apify MCP server), for **designers** who hand over After Effects exports, and for **mobile and web developers** who ship them. A model cannot see an animation in a JSON file. This Actor shows it, and says what is wrong.

Give it a **Lottie animation** (Bodymovin JSON exported from **After Effects**, or a **dotLottie** `.lottie` file), inline or by URL. For each one it returns:

- a **contact sheet** (one PNG with the key frames: 0 %, 25 %, 50 %, 75 %, 100 % by default) and each key frame as its own PNG: **Lottie to PNG**;
- a short **GIF** of the whole animation: **Lottie to GIF**;
- a **structured report** a model can read, which makes this a **Lottie validator**, a Lottie JSON validator an AI agent can act on: size, frame rate, duration, number of layers, errors and warnings with a stable code, the JSON path and the layer name, conformance to the official `lottie-spec` JSON Schema, and the features known **not to work in lottie-web, lottie-android or lottie-ios**. It reads any **JSON animation** exported by Bodymovin; when the renderer cannot draw a file (rare, see Limits), the row still has the report.

It is a **Lottie preview** for the loop of an agent or a developer: *write a Lottie, see it, fix it*. It needs no browser and no GPU. One action, one row per animation.

### Quick start

Minimal input (an animation pasted inline; everything else has a default):

```json
{"lottieJson": [{"v": "5.7.0", "fr": 30, "ip": 0, "op": 60, "w": 200, "h": 200, "layers": []}]}
```

Or by URL:

```json
{"urls": ["https://example.com/animation.json"]}
```

Minimal output, one row per animation (the full real example is in the Output section below):

```json
{"index": 0, "status": "ok", "durationSeconds": 2.0, "summary": {"errors": 0, "warnings": 1, "infos": 0}, "issues": [{"severity": "warning", "code": "ALL_FRAMES_BLANK"}], "compatibility": {"web": [], "android": [], "ios": []}, "preview": {"sheetUrl": "https://api.apify.com/v2/key-value-stores/<id>/records/anim-0-sheet.png", "gifUrl": "https://api.apify.com/v2/key-value-stores/<id>/records/anim-0.gif"}}
```

(Real run, shortened: this empty animation draws nothing, so the report says so. A row has more fields, and `preview.frames` lists each key frame.)

The images are drawn by [ThorVG](https://www.thorvg.org/), a Lottie renderer that is close to lottie-web but **not identical to the pixel**. Use the preview to check what is drawn and where, and the report to know what other players will ignore.

### When to use it

- **An AI agent just wrote or edited a Lottie JSON** and needs to look at the result before it answers: call the Actor with `lottieJson`, download the contact sheet, open the image, fix the JSON from `issues`.
- **Check a file before shipping it** to iOS, Android and web: the `compatibility` field lists, per player, the features it ignores (expressions, merge paths, luma mattes, layer effects, some mask modes, text on a path...).
- **Find why an animation is blank or wrong**: empty layers, layers with opacity 0, layers outside their time range, missing assets or parents, a precomposition that contains itself, invalid keyframes, external images that are not embedded.
- **Make a GIF or PNG thumbnail** of a Lottie or dotLottie file for a README, a ticket or a review.

It does not fix the file: the report says what to fix, a model or a person does it.

### For designers: from an export to a preview

You exported a `.json` (Bodymovin) or a `.lottie` and want to see it, with the compatibility report for iOS, Android and web?

- **A `.json` file**: open it in a text editor, copy all of it and paste it in **Lottie JSON (inline)** (up to 5 MB). Then remove the example URL from **Animation URLs**: it is prefilled, so otherwise `bodymovin.json` is analyzed too, as row 0, and billed.
- **A file that is online**: give its public URL in **Animation URLs** (up to 10 MB), for example the raw link of a file in a repository or a public share link that downloads the file itself. This is the only way for a `.lottie`.

Run the Actor, open the contact sheet and the GIF from the output tab, and read `compatibility` for iOS, Android and web.

### The agent loop

1. Call the Actor (`call-actor` on the Apify MCP server, or the API) with the animation: `lottieJson` for a file you just wrote, `urls` for one that is online.
2. Read the dataset row: `status`, then `issues` (errors first) and `compatibility`.
3. Download `preview.sheetUrl` and open the image, for example `curl -o sheet.png "<sheetUrl>"`. The key frames (`preview.frames[].url`) and the GIF (`preview.gifUrl`) are public links too.
4. Correct the JSON from `issues` (each one has a `code`, `paths` as JSON pointers, `layerNames` and a `message` that says what to do) and call again.

```json
{
  "actor": "lakadev/lottie-animation-preview-validator",
  "input": {"lottieJson": [{"v": "5.7.0", "fr": 30, "ip": 0, "op": 60, "w": 200, "h": 200, "layers": []}], "frames": [0, 50, 100]}
}
```

For MCP calls, keep to **5 animations or fewer per call**: `call-actor` waits at most about 45 seconds. If it times out, the run continues; read its result with the run identifier. Running an Actor requires an Apify token (or an x402 or Skyfire payment where available).

### Price

Pay per event, no platform usage billed on top:

| Event | Price | When |
|---|---|---|
| Animation analyzed | **$0.01** | the animation was read and rendered (row status `ok`) |
| Actor start | $0.00005 | once per run (Apify's standard start event) |

**Nothing is charged** for `render_failed` (the animation was read and reported but no image could be produced, even when the defect is in your file), `invalid_input`, `fetch_failed`, `robots_disallowed`, `too_large`, `invalid_file` or `not_lottie`. If your maximum cost per run is reached, the run stops cleanly at the first row it can no longer pay for: the rows already written are kept.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `urls` | array of strings | none (the form is prefilled with the `bodymovin.json` example) | Public `http`/`https` URLs of a `.json` Lottie or a `.lottie` (dotLottie), up to 10 MB each. |
| `lottieJson` | array | `[]` | Animations pasted inline, each an object or the JSON text. Up to 5 MB each; use `urls` above that. |
| `frames` | array of integers | `[0, 25, 50, 75, 100]` | 1 to 9 percentages of the duration, 0 to 100. |
| `size` | integer | `512` | Longest side of each key frame, 64 to 1024 px. |
| `gif` | boolean | `true` | Also make the GIF. |
| `gifSize` | integer | `320` | Longest side of the GIF, 64 to 480 px. |
| `background` | string | `#FFFFFF` | `#RRGGBB` or `transparent` (PNG with alpha; the GIF has no transparency and uses white). |

At least one animation in `urls` or `lottieJson`, at most **20** in total. The output has one row per input item, in order (`urls` first, then `lottieJson`), duplicates included. An invalid input (more than 20 animations, `frames` out of range, `size` out of bounds, a bad `background`...) fails the run with a clear message, before anything is written or charged.

The file type is detected from the content, not the extension: a ZIP is read as dotLottie (versions 1 and 2), anything else as JSON.

```json
{"urls": ["https://raw.githubusercontent.com/airbnb/lottie-web/bede03d25d232826e0c9dca1733d542d8a7754fb/test/animations/bodymovin.json"], "frames": [0, 50, 100], "size": 512, "background": "#FFFFFF"}
```

### Output

One row per animation in the default dataset; the PNG and GIF files are in the default key-value store (`anim-<index>-sheet.png`, `anim-<index>-f<percent>.png`, `anim-<index>.gif`). Real run of the example above (shortened: 2 of 3 frames, paths and layer names cut to 2).

```json
{
  "index": 0,
  "source": "https://raw.githubusercontent.com/airbnb/lottie-web/bede03d25d...d8a7754fb/test/animations/bodymovin.json",
  "sourceType": "url",
  "format": "json",
  "status": "ok",
  "error": null,
  "name": "Bm_fontAnim",
  "lottieVersion": "5.9.6",
  "width": 1820,
  "height": 275,
  "frameRate": 30,
  "inPoint": 0,
  "outPoint": 103,
  "totalFrames": 103,
  "durationSeconds": 3.433,
  "sizeBytes": 1102932,
  "counts": {
    "layers": 332,
    "precompositions": 23,
    "imageAssets": 0,
    "textLayers": 0,
    "fonts": 0,
    "expressions": 0,
    "effects": 0,
    "masks": 3
  },
  "specValid": true,
  "summary": {
    "errors": 0,
    "warnings": 1,
    "infos": 2
  },
  "issues": [
    {
      "severity": "warning",
      "code": "UNSUPPORTED_FEATURE",
      "feature": "merge-paths",
      "unsupportedIn": [
        "ios",
        "web"
      ],
      "count": 11,
      "paths": [
        "/assets/5/layers/12/shapes/0/it/2",
        "/assets/5/layers/13/shapes/0/it/2"
      ],
      "layerNames": [
        "boca",
        "O2original 2"
      ],
      "message": "Merge paths are ignored by lottie-ios and lottie-web; lottie-android draws them only when the app enables merge paths. Flatten the shapes instead. First seen in layer \"boca\" (/assets/5/layers/12/shapes/0/it/2). (11 occurrences in total.)"
    },
    {
      "severity": "info",
      "code": "LARGE_FILE",
      "feature": null,
      "unsupportedIn": [],
      "count": 1,
      "paths": [
        ""
      ],
      "layerNames": [
        null
      ],
      "message": "The animation JSON is 1102932 bytes (over 1 MB); it is heavy to download and parse on mobile. Simplify shapes or compress images."
    },
    {
      "severity": "info",
      "code": "MANY_LAYERS",
      "feature": null,
      "unsupportedIn": [],
      "count": 1,
      "paths": [
        ""
      ],
      "layerNames": [
        null
      ],
      "message": "The animation has 332 layers (over 100); it may be slow on mobile. Merge or remove layers."
    }
  ],
  "issuesTruncated": false,
  "compatibility": {
    "web": [
      "merge-paths"
    ],
    "android": [],
    "ios": [
      "merge-paths"
    ]
  },
  "preview": {
    "renderer": "ThorVG 1.0.4",
    "background": "#FFFFFF",
    "sheetUrl": "https://api.apify.com/v2/key-value-stores/<id>/records/anim-0-sheet.png",
    "gifUrl": "https://api.apify.com/v2/key-value-stores/<id>/records/anim-0.gif",
    "frames": [
      {
        "percent": 0,
        "frame": 0.0,
        "timeSeconds": 0.0,
        "width": 512,
        "height": 77,
        "blank": false,
        "url": "https://api.apify.com/v2/key-value-stores/<id>/records/anim-0-f0.png"
      },
      {
        "percent": 50,
        "frame": 51.0,
        "timeSeconds": 1.7,
        "width": 512,
        "height": 77,
        "blank": false,
        "url": "https://api.apify.com/v2/key-value-stores/<id>/records/anim-0-f50.png"
      }
    ]
  }
}
```

#### Status of a row

| `status` | Meaning | Billed |
|---|---|---|
| `ok` | read and rendered, all requested images written | yes |
| `render_failed` | read and reported, but no image: the renderer refused the file, crashed or timed out (30 s), or a blocking error (`PRECOMP_CYCLE`) stopped it | no |
| `invalid_input` | the `urls` item is not a valid `http`/`https` URL (no credentials), or the `lottieJson` item is neither an object nor a text | no |
| `fetch_failed` | resolution, connection, TLS or time-out, final HTTP status not 2xx, more than 5 redirects, or a non-public address | no |
| `robots_disallowed` | the site's `robots.txt` forbids the URL | no |
| `too_large` | a limit below was exceeded | no |
| `invalid_file` | neither readable JSON nor a readable dotLottie | no |
| `not_lottie` | JSON that is not a Lottie animation (`layers`, `w`, `h`, `fr`, `ip`, `op` missing or invalid, or `op` not after `ip`) | no |

`error` is `null` for `ok`, else one English sentence with the cause. The metadata fields, `counts`, `specValid`, `compatibility` and `preview` are `null` when the status is neither `ok` nor `render_failed` (`preview` is also `null` for `render_failed`). Key frame `p` % shows the frame `ip + p/100 x (op - ip - 1)`: 100 % is the last frame. `blank` is true when the whole image is transparent before the background is applied.

#### Issues

At most 100 issues per row, one per (`code`, `feature`) with a `count` and the first 5 `paths` (JSON pointers). Errors first, then warnings, then infos.

| Severity | Code | Meaning |
|---|---|---|
| error | `LAYER_MISSING_FIELD` | a layer lacks `ty`, `ip`, `op` or a field its type needs (`ks`, `refId`, `sc`/`sw`/`sh`, `shapes`, `t`) |
| error | `MASK_MISSING_FIELD` | a mask has no path `pt` (the renderer can crash on it) |
| error | `MISSING_ASSET` | a precomposition or image layer refers to an asset that does not exist |
| error | `MISSING_PARENT` | a layer's `parent` matches no layer `ind` |
| error | `PRECOMP_CYCLE` | a precomposition contains itself, directly or not (no preview) |
| error | `INVALID_KEYFRAMES` | animated property (`a` = 1) whose keyframes are empty, malformed, or not in increasing time |
| error | `RENDER_FAILED` | the renderer refused the file, crashed or timed out |
| warning | `UNSUPPORTED_FEATURE` | a feature of the table below; `feature` and `unsupportedIn` say which and where |
| warning | `EXTERNAL_IMAGE` | image asset that is not a `data:` URL (not downloaded: the preview omits it) |
| warning | `LARGE_IMAGE` | embedded image of more than 16 million pixels |
| warning | `FONT_NOT_EMBEDDED` | a font used by a text layer has neither an embedded `fPath` (`data:` URL) nor glyphs in `chars`; the preview draws the text with DejaVu Sans instead |
| warning | `LAYER_EMPTY` | shape layer with no geometry or no style |
| warning | `LAYER_INVISIBLE` | layer that draws content but has a fixed opacity of 0 (null layers and layers used as a `parent` are not reported) |
| warning | `LAYER_OUT_OF_TIME` | top-level layer never shown (outside the animation's `ip`/`op`) |
| warning | `LAYER_OUT_OF_FRAME` | named top-level layer drawn entirely outside the canvas at every sampled frame where it is active: the key frames and 12 frames spread over the animation (only layers with a unique name are judged) |
| warning | `UNKNOWN_LAYER_TYPE` | `ty` is not an integer from 0 to 15 |
| warning | `DUPLICATE_LAYER_INDEX` | two layers of a list share the same `ind` |
| warning | `ALL_FRAMES_BLANK` | every key frame is empty |
| info | `LAYER_HIDDEN` | layer with `hd` = true |
| info | `SPEC_NONCONFORMANT` | the document does not conform to lottie-spec 1.0.1; `paths` gives the first 5 layers that fail the layer schema and `count` how many (path `""` when the layers are fine and the problem is elsewhere) |
| info | `LARGE_FILE` | animation over 1 MB |
| info | `MANY_LAYERS` | more than 100 layers |
| info | `HIGH_FRAME_RATE` | frame rate over 60 |
| info | `LONG_ANIMATION` | duration over 30 s |

`specValid` is a yes/no answer from the official JSON Schema: many older Bodymovin 4.x exports are non-conformant but play everywhere, so the Actor does not list schema errors. Its own rules above give the useful ones.

#### Compatibility table (`UNSUPPORTED_FEATURE`)

Source: [airbnb/lottie `supported-features.md`](https://github.com/airbnb/lottie/blob/a84a05f/supported-features.md), commit `a84a05f` of 25 March 2026 (MIT). Only the cells marked unsupported are used; unknown cells are not asserted. `web` means unsupported in all three web renderers (SVG, Canvas, HTML); `ios` means unsupported in both iOS engines (Core Animation and Main Thread). An empty list in `compatibility` means "no incompatibility known from this table", not "compatible".

| `feature` | Detected as | Not supported in |
|---|---|---|
| `expressions` | a property with a non-empty expression (`x`) | android, ios |
| `layer-effects` | an effect on a layer other than drop shadow (25) and Gaussian blur (29) | android, ios |
| `gaussian-blur` | Gaussian blur effect (29) | ios |
| `merge-paths` | a merge-paths shape (`mm`) | ios, web |
| `auto-orient` | layer with `ao` = 1 | android, ios |
| `luma-matte` | layer with `tt` = 3 or 4 | android, ios |
| `mask-intersect` | mask mode `i` | web |
| `mask-lighten-darken-difference` | mask mode `l`, `d` or `f` | android, ios, web |
| `mask-expansion` | mask with an animated or non-zero expansion | android, ios |
| `text-path` | text layer with a path | android, ios |
| `text-range-selector` | text layer with animators or range selectors | android, ios |
| `text-glyphs` | `chars` (glyph data) present | ios |

lottie-android draws merge paths only when the app enables them. Windows (lottie-windows, no longer maintained) is not covered.

### Limits

- Inputs: `http`/`https` URLs of up to **10 MB**, inline animations up to **5 MB**, at most **20** animations per run, at most **5,000** layers and 1,000,000 JSON values per animation (`too_large` beyond). Names copied into the report are cut at 200 characters. A dotLottie archive has at most 1,000 entries, an animation of at most 10 MB once unpacked and 40 MB in total; it is read in memory, never written to disk.
- **dotLottie**: only the animation chosen by the manifest (`initial.animation`, else the first one) is read; other animations, themes, state machines, slots and fonts of the archive are ignored. Images stored in the archive are embedded before drawing.
- **External images are never downloaded**: the preview omits them and `EXTERNAL_IMAGE` says so. No LottieFiles API is called.
- **Not supported in this version**: Standby mode and an MCP server of its own (call the Actor with `call-actor`), Telegram stickers (TGS), SVG, After Effects `.aep` files, MP4/WebM/APNG/animated WebP output, GIFs at real frame rate, images over 1024 px, comparing several renderers or two versions of a file, performance measures on a device, automatic fixing, React Native, Flutter, Skottie or ThorVG compatibility, blend modes (`bm`), and the list of every schema error.
- Text whose font is not embedded is drawn with a built-in DejaVu Sans (and reported), not with the font you meant. Your own fonts cannot be uploaded in this version.
- The rendering engine (ThorVG 1.0.4) can fail on some files (about 1 in 14 in our checks): the row is then `render_failed`, with the report, and is not billed. Previews are close to lottie-web but not identical; compare with a real player when it matters.
- After 3 render time-outs in one run, the remaining animations get `render_failed` without being rendered ("skipped after 3 render timeouts in this run"); none is billed.
- The GIF has at most 48 images, covers the whole animation, and loops forever.
- The rendering of each animation runs in a separate process with a memory limit and a 30 s time limit, so a file that crashes the renderer or asks for a huge image only fails its own row.
- Memory: 1,024 MB by default (a quarter of a CPU core): about 1 to 2 s per ordinary animation, a few more for heavy ones. Minimum 512 MB.
- The Actor does not run JavaScript and uses no browser or proxy.

### Legal and good manners

URLs are fetched with the User-Agent `LakadevLottieBot/1.0 (+https://apify.com/lakadev/lottie-animation-preview-validator)`. The Actor reads and follows the `robots.txt` of each origin (RFC 9309), including the origin of every redirect, before any file request, never sends cookies, and refuses non-public addresses (loopback, private, link-local, cloud metadata...) at every redirect: it connects to the address it checked. It does not bypass any protection. The content comes from you, who answer for it. No personal data is kept outside the storages of the run.

### Licenses and notices

The Actor's code is MIT licensed (`LICENSE`). Third-party components are listed in `THIRD_PARTY_NOTICES.md`:

- **ThorVG** 1.0.4 (MIT), the rendering engine;
- **thorvg-python** 1.1.3 (LGPL-2.1), the Python binding. It is used **unmodified**, as a separate package imported dynamically, so that it can be replaced. Source: https://github.com/laggykiller/thorvg-python;
- **Lottie Animation Format JSON Schema** 1.0.1, Lottie Animation Community (LF Projects), https://github.com/lottie/lottie-spec (Community Specification License 1.0);
- the compatibility table is derived from **airbnb/lottie** (MIT); the fallback font is **DejaVu Sans** (Bitstream Vera license).

### FAQ

**Is the preview exactly what lottie-web shows?** No. It is drawn by ThorVG, which is close to lottie-web but not identical to the pixel. Trust it for what is drawn, where and when; trust `compatibility` for what other players ignore.

**It says `compatibility` is empty. Is my animation compatible?** Not proven. The table is short and sourced; it lists what is known to fail, nothing more.

**Why is `specValid` false on a file that plays everywhere?** Many older Bodymovin exports do not follow the current specification. That is why it is only an `info` issue.

**Am I charged for failures?** No. Only rows with status `ok` are billed.

**Can I send a file larger than 5 MB?** Put it online and use `urls` (up to 10 MB).

# Actor input Schema

## `urls` (type: `array`):

Public http or https URLs of a Lottie .json file or a .lottie (dotLottie) file, up to 10 MB each. Use this for files that are already online or larger than 5 MB. 20 animations at most with lottieJson. For MCP calls, keep to 5 animations or fewer per run so results arrive within the 45 s tool timeout.

## `lottieJson` (type: `array`):

Paste the animation JSON here (an object, or the JSON text). For files over 5 MB, use `urls`. Each item is one animation; use this when an agent has just written the file and it is not online.

## `frames` (type: `array`):

Which moments to render, as percentages of the animation from 0 to 100 (1 to 9 integers). Default 0, 25, 50, 75, 100. Use more values to inspect a short transition, fewer to save time.

## `size` (type: `integer`):

Longest side of each key frame PNG, from 64 to 1024 pixels. Default 512. Raise it to read small details; lower it for faster, lighter results.

## `gif` (type: `boolean`):

Default true. Also produce a short GIF of the whole animation (at most 48 images). Turn off when only key frames and the report are needed.

## `gifSize` (type: `integer`):

Longest side of the GIF, from 64 to 480 pixels. Default 320. Only used when the GIF is on.

## `background` (type: `string`):

Background of the images: a color as #RRGGBB (default #FFFFFF) or transparent for PNG with an alpha channel (the GIF then uses white). Use a dark color to check light shapes.

## Actor input object example

```json
{
  "urls": [
    "https://raw.githubusercontent.com/airbnb/lottie-web/bede03d25d232826e0c9dca1733d542d8a7754fb/test/animations/bodymovin.json"
  ],
  "lottieJson": [],
  "frames": [
    0,
    25,
    50,
    75,
    100
  ],
  "size": 512,
  "gif": true,
  "gifSize": 320,
  "background": "#FFFFFF"
}
```

# Actor output Schema

## `results` (type: `string`):

One row per animation, in input order, as JSON items (overview view).

## `files` (type: `string`):

Keys of the PNG key frames, contact sheets and GIFs in the default key-value store (anim-<index>-sheet.png, anim-<index>-f<percent>.png, anim-<index>.gif).

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "urls": [
        "https://raw.githubusercontent.com/airbnb/lottie-web/bede03d25d232826e0c9dca1733d542d8a7754fb/test/animations/bodymovin.json"
    ],
    "frames": [
        0,
        25,
        50,
        75,
        100
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("lakadev/lottie-animation-preview-validator").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "urls": ["https://raw.githubusercontent.com/airbnb/lottie-web/bede03d25d232826e0c9dca1733d542d8a7754fb/test/animations/bodymovin.json"],
    "frames": [
        0,
        25,
        50,
        75,
        100,
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("lakadev/lottie-animation-preview-validator").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "urls": [
    "https://raw.githubusercontent.com/airbnb/lottie-web/bede03d25d232826e0c9dca1733d542d8a7754fb/test/animations/bodymovin.json"
  ],
  "frames": [
    0,
    25,
    50,
    75,
    100
  ]
}' |
apify call lakadev/lottie-animation-preview-validator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,lakadev/lottie-animation-preview-validator"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/PmbqKu9Ot0WMlcSbF/builds/QvIl5pwD6vJb4Lrya/openapi.json
