---
title: "Create a Reddit story project | GPT-Video API reference"
description: "Creates a Reddit story project: a post read by a synthetic voice over a background video, with a title card and captions."
url: "https://www.gpt-video.com/developers/api/create-reddit-story-project"
operation: "create_reddit_story_project"
method: "POST"
path: "/v1/projects/reddit-story"
scope: "clips:write"
---

Do not assume endpoints have parameters other than the ones explicitly specified.

# Create a Reddit story project

Operation id `create_reddit_story_project`. Request `POST /v1/projects/reddit-story`.

Creates a Reddit story project: a post read by a synthetic voice over a background video, with a title card and captions.

Beta. This endpoint is supported and can gain new fields. If a change would break your code, we tell you before it takes effect and list it in the changelog.

## About

Send the post, who wrote it and where, the voice and the language: list_voices (`GET /v1/reference/voices`) names the voices of each language. The background video is optional and can be changed later. Everything can be changed with update_reddit_story_project (`PATCH /v1/projects/{projectId}/reddit-story`). Nothing is built and no credits are used until you call build_reddit_story_video (`POST /v1/projects/{projectId}/reddit-story/build`). Send an Idempotency-Key: a retry without one creates a second project.

## Example use

A channel that posts story videos sends the text of a post, the author shown on the title card and a gameplay background. It picks a voice from list_voices (`GET /v1/reference/voices`) that speaks the language of the post.

## Endpoint

```text
POST https://api.gpt-video.com/v1/projects/reddit-story
```

- Scope `clips:write`.
- Rate limit 60 requests a minute for each key and 120 for each account, shared by every `write` call. See [rate limits](/developers/rate-limits.md).
- `Idempotency-Key` is optional.

## Parameters

### Headers

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Idempotency-Key` | string | Optional | Send the same key on every retry of this request. From 1 to 255 visible characters, such as a UUID. See [idempotency](/developers/pagination-and-idempotency.md). |

### Body

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `postTitle` | string | Required | The title of the post, shown on the title card and read first. From 1 to 300 characters. From 1 to 300 characters. Matches `\S`. |
| `postBody` | string | Required | The text of the post, read by the voice. From 1 to 8000 characters. From 1 to 8000 characters. Matches `\S`. |
| `author` | string | Required | The author shown on the title card, such as u/thrown_away. From 1 to 40 characters. From 1 to 40 characters. |
| `subreddit` | string | Required | The community shown on the title card, such as r/stories. From 1 to 40 characters. From 1 to 40 characters. |
| `voice` | string | Required | The voice that reads the story, as list_voices (`GET /v1/reference/voices`) names it. It must be a voice of the story's language. One of `en-aria`, `en-jenny`, `en-guy`, `en-christopher`, `it-elsa`, `it-isabella`, `it-diego`, `it-giuseppe`. |
| `language` | string | Required | The language of the story and of its voice. One of `en`, `it`. |
| `captionStyle` | string | Required | The caption style. get_setting_options (`GET /v1/reference/setting-options`) lists every style with what it looks like. One of `bold-karaoke`, `word-pop`, `boxed`, `outline`. |
| `backgroundAssetId` | string or null | Required | The background video, as complete_upload (`POST /v1/uploads/{uploadId}/complete`) returned it; null for none. Matches `^ast_n?[0-9a-f]{20}$`. |
| `vocabulary` | string | Optional | Names, brands and jargon the transcription should spell right, separated by commas. At most 311 characters. At most 311 characters. |
| `title` | string | Optional | A name for the project, shown in the GPT-Video app. Leave it out to get a default name. From 1 to 120 characters. Matches `\S`. |

Send only the fields listed here. Any other field is refused with UNKNOWN_PARAMETER.

## Request

Create a Reddit story project.

```bash title="curl"
# Create the key once. Send this same key again if you retry this request.
IDEMPOTENCY_KEY=$(uuidgen)
curl -X POST "https://api.gpt-video.com/v1/projects/reddit-story" \
  -H "Authorization: Bearer $GPT_VIDEO_API_KEY" \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "postTitle": "I said no to my boss'\''s weekend trip",
    "postBody": "My boss planned a team trip on my only free weekend of the month. I said no, and now the team thinks I am the problem.",
    "author": "u/weekend_keeper",
    "subreddit": "r/stories",
    "voice": "en-jenny",
    "language": "en",
    "captionStyle": "bold-karaoke",
    "backgroundAssetId": "ast_8ac5d8b97546145cdbf6"
  }'
```

```ts title="TypeScript"
// Create the key once. Send this same key again if you retry this request.
const idempotencyKey = crypto.randomUUID();
const res = await fetch("https://api.gpt-video.com/v1/projects/reddit-story", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GPT_VIDEO_API_KEY}`,
    "Idempotency-Key": idempotencyKey,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "postTitle": "I said no to my boss's weekend trip",
    "postBody": "My boss planned a team trip on my only free weekend of the month. I said no, and now the team thinks I am the problem.",
    "author": "u/weekend_keeper",
    "subreddit": "r/stories",
    "voice": "en-jenny",
    "language": "en",
    "captionStyle": "bold-karaoke",
    "backgroundAssetId": "ast_8ac5d8b97546145cdbf6"
  }),
});
const data = await res.json();
console.log(data);
```

```python title="Python"
import os
import uuid

import requests

# Create the key once. Send this same key again if you retry this request.
idempotency_key = str(uuid.uuid4())
r = requests.post(
    "https://api.gpt-video.com/v1/projects/reddit-story",
    headers={
        "Authorization": f"Bearer {os.environ['GPT_VIDEO_API_KEY']}",
        "Idempotency-Key": idempotency_key,
    },
    json={
        "postTitle": "I said no to my boss's weekend trip",
        "postBody": "My boss planned a team trip on my only free weekend of the month. I said no, and now the team thinks I am the problem.",
        "author": "u/weekend_keeper",
        "subreddit": "r/stories",
        "voice": "en-jenny",
        "language": "en",
        "captionStyle": "bold-karaoke",
        "backgroundAssetId": "ast_8ac5d8b97546145cdbf6"
    },
)
data = r.json()
print(data)
```

```json title="MCP (Full)"
{
  "method": "tools/call",
  "params": {
    "name": "create_reddit_story_project",
    "arguments": {
      "postTitle": "I said no to my boss's weekend trip",
      "postBody": "My boss planned a team trip on my only free weekend of the month. I said no, and now the team thinks I am the problem.",
      "author": "u/weekend_keeper",
      "subreddit": "r/stories",
      "voice": "en-jenny",
      "language": "en",
      "captionStyle": "bold-karaoke",
      "backgroundAssetId": "ast_8ac5d8b97546145cdbf6"
    }
  }
}
```

## Response

A successful call answers HTTP 201. Returns a [Project object](/developers/objects.md#project) in `project`.

### Fields to check

| Name | Type | Description |
| --- | --- | --- |
| `project.id` | string | The project id. It starts with prj_. |
| `project.status` | string | draft has not been analyzed yet, processing is being analyzed, ready has clips, failed means the last analysis failed and errorMessage says why. |

### Response example

```json title="HTTP 201"
{
  "project": {
    "id": "prj_e94a1c6b07d35f28c1b4",
    "kind": "redditstory",
    "title": "Reddit story",
    "status": "draft",
    "sourceAssetId": null,
    "compositionId": null,
    "settings": null,
    "missingSettings": [],
    "errorMessage": null,
    "splitScreen": null,
    "streamer": null,
    "redditStory": {
      "postTitle": "I said no to my boss's weekend trip",
      "postBody": "My boss planned a team trip on my only free weekend of the month. I said no, and now the team thinks I am the problem.",
      "author": "u/weekend_keeper",
      "subreddit": "r/stories",
      "voice": "en-jenny",
      "language": "en",
      "backgroundAssetId": "ast_8ac5d8b97546145cdbf6",
      "captionStyle": "bold-karaoke",
      "vocabulary": null
    },
    "fakeText": null,
    "vibeEdit": null,
    "createdAt": "2026-09-01T11:00:00.000Z",
    "updatedAt": "2026-09-01T11:00:00.000Z"
  }
}
```

**All response fields**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `project` | Project object | Required | Described in [Project object](/developers/objects.md#project). |

**More examples**

#### A voice of another language

`POST /v1/projects/reddit-story`

```json title="Request body"
{
  "postTitle": "I said no to my boss's weekend trip",
  "postBody": "My boss planned a team trip on my only free weekend of the month. I said no, and now the team thinks I am the problem.",
  "author": "u/weekend_keeper",
  "subreddit": "r/stories",
  "voice": "it-elsa",
  "language": "en",
  "captionStyle": "bold-karaoke",
  "backgroundAssetId": null
}
```

```json title="HTTP 400"
{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "Voice it-elsa is not available in en. Call list_voices to see the voices you can use.",
    "hint": "Fix the request as the message says, then send it again.",
    "requestId": "req_b0e7d3a9c6f2418e5d0b7a31"
  }
}
```

#### A background video that does not exist

`POST /v1/projects/reddit-story`

```json title="Request body"
{
  "postTitle": "I said no to my boss's weekend trip",
  "postBody": "My boss planned a team trip on my only free weekend of the month. I said no, and now the team thinks I am the problem.",
  "author": "u/weekend_keeper",
  "subreddit": "r/stories",
  "voice": "en-guy",
  "language": "en",
  "captionStyle": "bold-karaoke",
  "backgroundAssetId": "ast_0b1e5c9d3a7f40628e2d"
}
```

```json title="HTTP 404"
{
  "error": {
    "code": "NOT_FOUND",
    "message": "Asset ast_0b1e5c9d3a7f40628e2d not found",
    "hint": "Check the id. An id that belongs to another account is not found either.",
    "requestId": "req_4c1e8b5f2d9a06c73e1b8d52"
  }
}
```

## Errors

| Code | Status | When | What to do |
| --- | --- | --- | --- |
| [`NOT_FOUND`](/developers/errors.md#NOT_FOUND) | 404 | A background video that does not exist | Change the request, then send it again. |
| [`CONFLICT`](/developers/errors.md#CONFLICT) | 409 | The background is not a video, or the file of the background video was deleted to free up storage. The message says which one. | Read the resource again, then decide. Do not send the same request again. |

Every call can also answer these errors. [`VALIDATION_FAILED`](/developers/errors.md#VALIDATION_FAILED), [`UNKNOWN_PARAMETER`](/developers/errors.md#UNKNOWN_PARAMETER), [`UNAUTHORIZED`](/developers/errors.md#UNAUTHORIZED), [`SCOPE_MISSING`](/developers/errors.md#SCOPE_MISSING), [`IDEMPOTENCY_IN_PROGRESS`](/developers/errors.md#IDEMPOTENCY_IN_PROGRESS), [`PAYLOAD_TOO_LARGE`](/developers/errors.md#PAYLOAD_TOO_LARGE), [`IDEMPOTENCY_KEY_REUSED`](/developers/errors.md#IDEMPOTENCY_KEY_REUSED), [`RATE_LIMITED`](/developers/errors.md#RATE_LIMITED), [`INTERNAL`](/developers/errors.md#INTERNAL), [`UNAVAILABLE`](/developers/errors.md#UNAVAILABLE).

## Cost

No credits are used. The build is charged when you call build_reddit_story_video (`POST /v1/projects/{projectId}/reddit-story/build`).

## Related operations

- [`update_reddit_story_project`](/developers/api/update-reddit-story-project.md) (`PATCH /v1/projects/{projectId}/reddit-story`) Update a Reddit story project
- [`build_reddit_story_video`](/developers/api/build-reddit-story-video.md) (`POST /v1/projects/{projectId}/reddit-story/build`) Build a Reddit story video
- [`estimate_analysis`](/developers/api/estimate-analysis.md) (`POST /v1/projects/{projectId}/analyze/estimate`) Estimate the credits of an analysis or a build
- [`complete_upload`](/developers/api/complete-upload.md) (`POST /v1/uploads/{uploadId}/complete`) Complete an upload
- [`list_voices`](/developers/api/list-voices.md) (`GET /v1/reference/voices`) List the voices

## Questions

### Why must I choose the author and the community?

They are text in the video, on the title card. Nothing is filled in for you, so the video shows only what you chose.

### Which voices can I use?

list_voices (`GET /v1/reference/voices`) lists them with their language. The voice must be one of the story's language; another one is refused with VALIDATION_FAILED.
