> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ugc.inc/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Video Post

> Create a video post

## Endpoint

```
POST https://api.ugc.inc/post/video
```

## Overview

Create and schedule a video post to be published on TikTok or Instagram. The video will be uploaded and posted to the specified account.

## Request Body

<ParamField body="accountId" type="string | null" required>
  Account ID to post from, or `null` for automatic account selection

  **Auto-Selection Behavior:**

  * Set to `null` to automatically select an account
  * With `strict: true`: Selects the least recently posted account
  * With `strict: false`: Selects the account with the closest available time slot to your requested `postTime`
  * Can be filtered using `tag`, `user_group`, and `org_group` parameters
</ParamField>

<ParamField body="tag" type="string">
  Filter accounts by tag (only used when `accountId` is `null`)
</ParamField>

<ParamField body="user_group" type="string">
  Filter accounts by user group (only used when `accountId` is `null`)
</ParamField>

<ParamField body="org_group" type="string">
  Filter accounts by organization group (only used when `accountId` is `null`)
</ParamField>

<ParamField body="post_tag" type="string">
  Custom tag stored on the created post itself (returned as `tag` on the Post object). Distinct from `tag`, which filters account auto-selection.
</ParamField>

<ParamField body="videoUrl" type="string" required>
  URL to the video file (must be publicly accessible)

  Supported formats: MP4, MOV, AVI

  Recommended specs:

  * Resolution: 1080x1920 (9:16 aspect ratio)
  * Duration: 15-60 seconds
  * File size: Under 100MB
</ParamField>

<ParamField body="caption" type="string">
  Post caption/description

  Can include hashtags and emojis

  **Limit:** Maximum 4000 characters
</ParamField>

<ParamField body="captionOverlays" type="CaptionOverlay[]">
  Text overlays to display on the video

  Each overlay is an object:

  * `text` (string) — the overlay text
  * `x` (number) — horizontal center of the overlay as a fraction (0-1) of the video width
  * `y` (number) — vertical center of the overlay as a fraction (0-1) of the video height
  * `fontSize` (number) — text size as a fraction (0-1) of the video height

  Example: `[{ "text": "Wait for it...", "x": 0.5, "y": 0.2, "fontSize": 0.04 }]`
</ParamField>

<ParamField body="socialAudioId" type="string">
  ID of a social audio record to attach to the post

  <Note>
    **Music Support:**

    * ✅ **TikTok:** Fully supported
    * ❌ **Instagram:** Not yet supported
  </Note>

  **How to use:**

  1. First, import the audio using the [Upload Social Audio](/api-reference/endpoint/media-social-audio-upload) endpoint with a TikTok video or music URL
  2. This returns a social audio record with an `id`
  3. Use that `id` as the `socialAudioId` here

  **Example workflow:**

  ```javascript theme={null}
  // Step 1: Import audio from a TikTok video
  const audioResponse = await client.media.createSocialAudio({
    url: 'https://www.tiktok.com/@username/video/1234567890'
  });

  // Step 2: Use the returned ID in your post
  const postResponse = await client.posts.createVideo({
    accountId: 'acc_123456',
    videoUrl: 'https://example.com/video.mp4',
    socialAudioId: audioResponse.data.id
  });
  ```
</ParamField>

<ParamField body="postTime" type="string">
  When to schedule the post (ISO 8601 format)

  If omitted, the post will be published immediately

  Example: `2024-12-31T12:00:00Z`
</ParamField>

<ParamField body="strict" type="boolean" default={false}>
  Scheduling mode - controls timing behavior

  **When `strict: true`:**

  * Post must be scheduled at the exact requested time
  * If using auto-selection (`accountId: null`), picks the least recently posted account
  * Will return an error if the account cannot post at the exact time

  **When `strict: false`:**

  * Post will be scheduled at the nearest available time
  * If using auto-selection (`accountId: null`), picks the account that can post closest to the requested time
  * Automatically adjusts to the next available slot if requested time is unavailable
</ParamField>

<ParamField body="mustPostBy" type="string">
  Latest allowed scheduled time (ISO 8601 format)

  If the post cannot be scheduled before this deadline, the request will return an error.

  When using auto-selection (`accountId: null`), accounts that cannot post before this time are excluded from selection.

  Example: `2024-12-31T23:59:59Z`
</ParamField>

## Response

<ResponseField name="data" type="Post">
  The created post object

  <Expandable title="Post properties">
    <ResponseField name="id" type="string">
      Unique post identifier - use this to track the post status
    </ResponseField>

    <ResponseField name="account_id" type="string">
      ID of the account that will post this
    </ResponseField>

    <ResponseField name="type" type="'video'">
      Post type (always `video` for this endpoint)
    </ResponseField>

    <ResponseField name="status" type="string">
      Initial status: `scheduled` or `pending`
    </ResponseField>

    <ResponseField name="social_id" type="string | null">
      Platform post ID (null until posted)
    </ResponseField>

    <ResponseField name="caption" type="string | null">
      Post caption
    </ResponseField>

    <ResponseField name="tag" type="string | null">
      Custom tag stored on the post (from `post_tag`)
    </ResponseField>

    <ResponseField name="caption_overlays" type="CaptionOverlay[] | null">
      Text overlays to display on the video (`{ text, x, y, fontSize }` each; positions/size are fractions of the video dimensions)
    </ResponseField>

    <ResponseField name="media_urls" type="string[]">
      Array containing the video URL
    </ResponseField>

    <ResponseField name="social_audio_id" type="string | null">
      ID of the attached social audio record
    </ResponseField>

    <ResponseField name="scheduled_at" type="string | null">
      When the post is scheduled to be published (ISO 8601 format)
    </ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://api.ugc.inc/post/video \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "accountId": "acc_123456",
      "videoUrl": "https://storage.example.com/video.mp4",
      "caption": "My awesome video! 🎥 #viral #content",
      "socialAudioId": "audio_xyz789",
      "postTime": "2024-12-31T12:00:00Z"
    }'
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://api.ugc.inc/post/video',
      headers={
          'Authorization': 'Bearer YOUR_API_KEY',
          'Content-Type': 'application/json'
      },
      json={
          'accountId': 'acc_123456',
          'videoUrl': 'https://storage.example.com/video.mp4',
          'caption': 'My awesome video! 🎥 #viral #content',
          'socialAudioId': 'audio_xyz789',
          'postTime': '2024-12-31T12:00:00Z'
      }
  )

  data = response.json()

  if data['ok']:
      print('Post created:', data['data']['id'])
      print('Status:', data['data']['status'])
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.ugc.inc/post/video', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      accountId: 'acc_123456',
      videoUrl: 'https://storage.example.com/video.mp4',
      caption: 'My awesome video! 🎥 #viral #content',
      socialAudioId: 'audio_xyz789',
      postTime: '2024-12-31T12:00:00Z'
    })
  });

  const data = await response.json();

  if (data.ok) {
    console.log('Post created:', data.data.id);
    console.log('Status:', data.data.status);
  }
  ```

  ```typescript React theme={null}
  import { UGCClient } from 'ugcinc';

  const client = new UGCClient({
    apiKey: 'YOUR_API_KEY'
  });

  // Standard: Post with specific account
  const response = await client.posts.createVideo({
    accountId: 'acc_123456',
    videoUrl: 'https://storage.example.com/video.mp4',
    caption: 'My awesome video! 🎥 #viral #content',
    socialAudioId: 'audio_xyz789',
    postTime: '2024-12-31T12:00:00Z'
  });

  // Auto-select with strict=true: picks least recently posted account
  const autoResponse1 = await client.posts.createVideo({
    accountId: null,  // Auto-select
    videoUrl: 'https://storage.example.com/video.mp4',
    caption: 'Auto-posted at exact time',
    postTime: '2024-12-31T10:00:00Z',
    strict: true,  // Must post at exactly 10:00 AM
    tag: 'production'  // Only consider accounts with this tag
  });

  // Auto-select with strict=false: picks account with closest available slot
  const autoResponse2 = await client.posts.createVideo({
    accountId: null,  // Auto-select
    videoUrl: 'https://storage.example.com/video.mp4',
    caption: 'Auto-posted at nearest time',
    postTime: '2024-12-31T14:00:00Z',
    strict: false,  // Posts at nearest available time
    user_group: 'creators'  // Only consider accounts in this user group
  });

  if (response.ok) {
    console.log(`Post created with ID: ${response.data.id}`);
    console.log(`Account used: ${response.data.account_id}`);
    console.log(`Scheduled for: ${response.data.scheduled_at}`);
  }
  ```
</RequestExample>

<ResponseExample>
  ```json Success Response theme={null}
  {
    "ok": true,
    "code": 200,
    "message": "Video post created successfully",
    "data": {
      "id": "post_abc123",
      "account_id": "acc_123456",
      "type": "video",
      "status": "scheduled",
      "social_id": null,
      "caption": "My awesome video! 🎥 #viral #content",
      "media_urls": [
        "https://storage.example.com/video.mp4"
      ],
      "social_audio_id": "audio_xyz789",
      "scheduled_at": "2024-12-31T12:00:00Z"
    }
  }
  ```

  ```json Error Response theme={null}
  {
    "ok": false,
    "code": 400,
    "message": "Invalid video URL or format"
  }
  ```
</ResponseExample>
