# Post Engineer: AI video generator and social scheduler (`made_by_ryan/post-engineer-video`) Actor

Turn a hook, body and call to action into a short video with an AI persona (or faceless), then publish it to YouTube, Instagram, LinkedIn, Threads or Bluesky.

- **URL**: https://apify.com/made_by_ryan/post-engineer-video.md
- **Developed by:** [Ryan Carlos](https://apify.com/made_by_ryan) (community)
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## 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

## Post Engineer: AI video generator and social scheduler

Turn a short script (hook, body, call to action) into a vertical video presented by an AI persona, or a faceless video, and publish it to YouTube, Instagram, LinkedIn, Threads or Bluesky. One run creates one video.

This Actor is a thin client for the [Post Engineer API](https://post-engineer.com/api-documentation.txt). Generation runs on Post Engineer and is paid with Post Engineer tokens, not Apify compute.

### Before you run it

1. Create an account at [post-engineer.com](https://post-engineer.com) and connect the social accounts you want to post to.
2. Create an API key at [post-engineer.com/api-keys](https://post-engineer.com/api-keys). It starts with `pe_live_`.
3. Make sure the account has tokens. A faceless video costs 1 token, a persona video 2 or 3, depending on face quality.

### Input

| Field | Required | What it does |
|---|---|---|
| `apiKey` | to create a video | Your Post Engineer API key, stored as a secret input. Without it the run is a **dry run**: it validates the input and returns the request it would send, with nothing generated, published or charged. |
| `title` | yes | Label of the scheduled post. |
| `hook`, `body`, `cta` | yes | The script. 60 to 200 words in total. It is validated before any token is spent. |
| `providers` | yes | Any of `youtube`, `instagram`, `linkedin`, `bluesky`, `threads`. |
| `personaId` | no | AI persona that presents the video. Leave empty for a faceless video. |
| `voiceId` | without a persona | Narration voice, an id from `GET /api/persona/voices` (prefilled with `chatterbox:kokoro_af_heart`). With a persona it overrides the persona's voice. |
| `faceless` | no | No face on screen. Defaults to `true` when there is no persona; a video without a persona must be faceless. |
| `facelessMaterial` | no | `broll` (stock footage) or `svg` (animated scenes). |
| `fullLipsync` | no | The persona talks for the whole video instead of only the hook. Needs a persona; not with `faceless`. |
| `accounts` | no | Account ids per provider, e.g. `{"youtube": ["<id>"]}`. Defaults to the persona's publishing accounts. |
| `mode` | no | `asap` (default) publishes when the video is ready; `scheduled` uses `schedule`. |
| `schedule` | for `scheduled` | `{"startAt": "2026-12-01T09:00:00Z", "times": ["18:00"], "timezone": "America/Sao_Paulo"}`. Slots must be 3 hours to 30 days ahead. |
| `waitForCompletion` | no | Default `true`: keep the run open until the video is generated or fails. |
| `timeoutMinutes` | no | How long to wait, 1 to 60 (default 30). |
| `apiUrl` | no | Only for self-hosted Post Engineer. Must be https (http only for localhost). |

#### Example

```json
{
  "apiKey": "pe_live_...",
  "title": "Why short videos win",
  "hook": "Most people scroll past your posts in under two seconds.",
  "body": "Short videos fix that. ...",
  "cta": "Follow for one tip a day.",
  "providers": ["youtube"],
  "voiceId": "chatterbox:kokoro_af_heart"
}
```

### Output

One dataset item (also saved as the `OUTPUT` record):

```json
{
  "scheduleId": "6f1c...",
  "mode": "asap",
  "replayed": false,
  "warnings": [],
  "slots": [
    {
      "slotId": "a2b9...",
      "slotAt": "2026-11-02T18:00:00.000Z",
      "title": "Why short videos win",
      "taskId": "c41e...",
      "status": "generating",
      "videoStatus": "complete",
      "videos": ["https://post-engineer.com/api/persona/video-download/..."]
    }
  ]
}
```

`videoStatus` is one of:

- `complete`: the video was generated.
- `failed` (with `error`): generation failed; Post Engineer refunds the tokens.
- `unknown` (with `error`): the engine lost the task and the slot status could not confirm the outcome. Check the post in Post Engineer before re-running, so you do not pay for a second video.
- `timeout`: the video was still processing when `timeoutMinutes` ran out.

It is absent when `waitForCompletion` is `false`.

### Retries and failures

- The Apify run id is sent as the idempotency key, so a retried run replays the accepted job instead of charging twice (`replayed: true`).
- Invalid input, a rejected script, missing tokens and invalid keys fail the run with the API's message. The API key never appears in logs or output.

### Self-hosting

Post Engineer is open source (Apache-2.0): [github.com/Ryan-Carloso/post-engineer](https://github.com/Ryan-Carloso/post-engineer). Point `apiUrl` at your own deployment.

# Actor input Schema

## `apiKey` (type: `string`):

Create one at https://post-engineer.com/api-keys (starts with pe_live\_). Without a key the run is a dry run: it validates the input and shows the request it would send, with nothing generated, published or charged. With a key, generating a video spends tokens from that account.

## `title` (type: `string`):

Label of the scheduled post.

## `hook` (type: `string`):

Opening line. Hook, body and CTA together must be 60 to 200 words; the script is validated before any token is spent.

## `body` (type: `string`):

Main script.

## `cta` (type: `string`):

Closing line.

## `providers` (type: `array`):

One or more of: youtube, instagram, linkedin, bluesky, threads. Connect the accounts first at https://post-engineer.com.

## `personaId` (type: `string`):

AI persona that presents the video (lip-sync on the hook). Leave empty for a faceless video.

## `voiceId` (type: `string`):

Narration voice: an id from GET /api/persona/voices. Required without a persona; with a persona it overrides the persona's voice.

## `faceless` (type: `boolean`):

No face on screen, only b-roll or animated svg (cheapest). Defaults to true when no persona is set.

## `facelessMaterial` (type: `string`):

What fills a faceless video: stock b-roll or animated svg scenes.

## `fullLipsync` (type: `boolean`):

The persona talks for the whole video instead of only the hook (most expensive). Needs a persona; not with faceless.

## `accounts` (type: `object`):

Optional, e.g. {"youtube": \["<account id>"]}. Defaults to the persona's publishing accounts.

## `mode` (type: `string`):

asap publishes as soon as the video is ready; scheduled uses the schedule below.

## `schedule` (type: `object`):

Only for mode scheduled: {"startAt": "2026-12-01T09:00:00Z", "times": \["18:00"], "timezone": "America/Sao_Paulo"}. startAt is an ISO datetime; slots must be 3 hours to 30 days ahead.

## `waitForCompletion` (type: `boolean`):

Keep the run open until the video is generated (or fails) and return its status.

## `timeoutMinutes` (type: `integer`):

How long to wait for the video before returning status timeout.

## `apiUrl` (type: `string`):

Only for self-hosted Post Engineer. Must be https (http is allowed only for localhost).

## Actor input object example

```json
{
  "title": "Why short videos win",
  "hook": "Most people scroll past your posts in under two seconds.",
  "body": "Short vertical videos stop the scroll because they show a face, a clear idea and movement in the first moments. You do not need a camera, a studio or an editor to make them every day. Write the idea once as a hook, a body and a call to action, and let an AI persona present it for you, with captions and b-roll that match what you say.",
  "cta": "Follow for one practical tip like this every day.",
  "providers": [
    "youtube"
  ],
  "voiceId": "chatterbox:kokoro_af_heart",
  "mode": "asap",
  "waitForCompletion": true,
  "timeoutMinutes": 30
}
```

# Actor output Schema

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

One dataset item with scheduleId, mode, replayed, warnings and slots (slotId, taskId, status, videoStatus, videos).

## `output` (type: `string`):

The same result saved in the default key-value store.

# 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 = {
    "title": "Why short videos win",
    "hook": "Most people scroll past your posts in under two seconds.",
    "body": "Short vertical videos stop the scroll because they show a face, a clear idea and movement in the first moments. You do not need a camera, a studio or an editor to make them every day. Write the idea once as a hook, a body and a call to action, and let an AI persona present it for you, with captions and b-roll that match what you say.",
    "cta": "Follow for one practical tip like this every day.",
    "providers": [
        "youtube"
    ],
    "voiceId": "chatterbox:kokoro_af_heart"
};

// Run the Actor and wait for it to finish
const run = await client.actor("made_by_ryan/post-engineer-video").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 = {
    "title": "Why short videos win",
    "hook": "Most people scroll past your posts in under two seconds.",
    "body": "Short vertical videos stop the scroll because they show a face, a clear idea and movement in the first moments. You do not need a camera, a studio or an editor to make them every day. Write the idea once as a hook, a body and a call to action, and let an AI persona present it for you, with captions and b-roll that match what you say.",
    "cta": "Follow for one practical tip like this every day.",
    "providers": ["youtube"],
    "voiceId": "chatterbox:kokoro_af_heart",
}

# Run the Actor and wait for it to finish
run = client.actor("made_by_ryan/post-engineer-video").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 '{
  "title": "Why short videos win",
  "hook": "Most people scroll past your posts in under two seconds.",
  "body": "Short vertical videos stop the scroll because they show a face, a clear idea and movement in the first moments. You do not need a camera, a studio or an editor to make them every day. Write the idea once as a hook, a body and a call to action, and let an AI persona present it for you, with captions and b-roll that match what you say.",
  "cta": "Follow for one practical tip like this every day.",
  "providers": [
    "youtube"
  ],
  "voiceId": "chatterbox:kokoro_af_heart"
}' |
apify call made_by_ryan/post-engineer-video --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,made_by_ryan/post-engineer-video"
        }
    }
}
```

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/Ir5TemMsdfPF7OzwK/builds/jULVeKoWB31X29Kvw/openapi.json
