curl --request POST \
--url https://www.genviral.io/api/partner/v1/posts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"caption": "Weekly product recap and what shipped this sprint.",
"media": {
"type": "video",
"url": "https://cdn.example.com/recap.mp4",
"duration_sec": 42
},
"accounts": [{ "id": "YOUR_YOUTUBE_ACCOUNT_ID" }],
"settings": {
"youtube": {
"title": "Weekly product recap",
"type": "public"
}
},
"external_id": "yt-recap-2026-05-21"
}'
curl --request POST \
--url https://www.genviral.io/api/partner/v1/posts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"caption": "3 outfits for the long weekend",
"media": {
"type": "slideshow",
"urls": [
"https://cdn.example.com/look-1.jpg",
"https://cdn.example.com/look-2.jpg",
"https://cdn.example.com/look-3.jpg"
]
},
"accounts": [{ "id": "YOUR_TIKTOK_BYO_ACCOUNT_ID" }],
"tiktok": {
"post_mode": "MEDIA_UPLOAD",
"music_usage_confirmation": true,
"privacy_level": "SELF_ONLY"
},
"external_id": "tiktok-draft-001"
}'
curl --request POST \
--url https://www.genviral.io/api/partner/v1/posts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"caption": "Minimal kitchen organization ideas that actually stick.",
"media": {
"type": "slideshow",
"urls": ["https://cdn.example.com/pin-cover.jpg"]
},
"accounts": [{ "id": "YOUR_PINTEREST_ACCOUNT_ID" }],
"pinterest": {
"board_id": "YOUR_BOARD_ID",
"link": "https://example.com/kitchen-guide",
"tags": ["kitchen organization", "home tips"]
},
"external_id": "pinterest-pin-001"
}'
curl --request POST \
--url https://www.genviral.io/api/partner/v1/posts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"caption": "Behind the scenes from today'\''s shoot.",
"media": {
"type": "video",
"url": "https://cdn.example.com/reel.mp4"
},
"accounts": [{ "id": "YOUR_INSTAGRAM_BYO_ACCOUNT_ID" }],
"settings": {
"instagram": {
"post_type": "post",
"cover_url": "https://cdn.example.com/reel-cover.jpg",
"share_to_feed": false
}
},
"external_id": "instagram-reel-cover-001"
}'
curl --request POST \
--url https://www.genviral.io/api/partner/v1/posts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"caption": "Ship the repeatable thing, not the busywork.",
"accounts": [{ "id": "YOUR_X_ACCOUNT_ID" }],
"media": null,
"settings": {
"x": {
"who_can_reply_post": "everyone"
}
}
}'
Posts
Create Post
Schedule or publish text-only, video, or slideshow posts to connected accounts.
POST
/
api
/
partner
/
v1
/
posts
curl --request POST \
--url https://www.genviral.io/api/partner/v1/posts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"caption": "Weekly product recap and what shipped this sprint.",
"media": {
"type": "video",
"url": "https://cdn.example.com/recap.mp4",
"duration_sec": 42
},
"accounts": [{ "id": "YOUR_YOUTUBE_ACCOUNT_ID" }],
"settings": {
"youtube": {
"title": "Weekly product recap",
"type": "public"
}
},
"external_id": "yt-recap-2026-05-21"
}'
curl --request POST \
--url https://www.genviral.io/api/partner/v1/posts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"caption": "3 outfits for the long weekend",
"media": {
"type": "slideshow",
"urls": [
"https://cdn.example.com/look-1.jpg",
"https://cdn.example.com/look-2.jpg",
"https://cdn.example.com/look-3.jpg"
]
},
"accounts": [{ "id": "YOUR_TIKTOK_BYO_ACCOUNT_ID" }],
"tiktok": {
"post_mode": "MEDIA_UPLOAD",
"music_usage_confirmation": true,
"privacy_level": "SELF_ONLY"
},
"external_id": "tiktok-draft-001"
}'
curl --request POST \
--url https://www.genviral.io/api/partner/v1/posts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"caption": "Minimal kitchen organization ideas that actually stick.",
"media": {
"type": "slideshow",
"urls": ["https://cdn.example.com/pin-cover.jpg"]
},
"accounts": [{ "id": "YOUR_PINTEREST_ACCOUNT_ID" }],
"pinterest": {
"board_id": "YOUR_BOARD_ID",
"link": "https://example.com/kitchen-guide",
"tags": ["kitchen organization", "home tips"]
},
"external_id": "pinterest-pin-001"
}'
curl --request POST \
--url https://www.genviral.io/api/partner/v1/posts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"caption": "Behind the scenes from today'\''s shoot.",
"media": {
"type": "video",
"url": "https://cdn.example.com/reel.mp4"
},
"accounts": [{ "id": "YOUR_INSTAGRAM_BYO_ACCOUNT_ID" }],
"settings": {
"instagram": {
"post_type": "post",
"cover_url": "https://cdn.example.com/reel-cover.jpg",
"share_to_feed": false
}
},
"external_id": "instagram-reel-cover-001"
}'
curl --request POST \
--url https://www.genviral.io/api/partner/v1/posts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"caption": "Ship the repeatable thing, not the busywork.",
"accounts": [{ "id": "YOUR_X_ACCOUNT_ID" }],
"media": null,
"settings": {
"x": {
"who_can_reply_post": "everyone"
}
}
}'
Schedule or publish immediately to one or more connected accounts.
YouTube —
Requires a video post to a YouTube account.
TikTok — top-level
Requires BYO TikTok accounts and media (video or slideshow). Hosted TikTok accounts cannot use
TikTok-specific settings.
For video posts, the merged publish text built from
Pinterest — top-level
Requires at least one Pinterest account and image media.
X —
Instagram —
Both keys map to the same stored settings. If both are sent,
Facebook —
LinkedIn —
Both keys map to the same stored settings. If both are sent,
Bluesky —
No provider-specific fields. Send
Before you post
- Use an API key for a workspace with an active Creator, Professional, or Business subscription.
- Call
GET /accountsand pick account UUIDs. - Check each account’s
capabilities.supported_content_kindsandcaption_limit. - Build the payload below. Only include provider settings for platforms you are actually targeting.
Videos uploaded through
POST /files must be finalized with POST /files/{fileId}/finalize. Use the finalizer’s data.file.url, not the upload destination URL, in
the post payload.Omit
media or set it to null only when every selected account supports text_only.Request body
string
required
Shared caption text. Final length is checked per targeted account after lookup (hosted accounts:
500 chars max).
array
required
One to ten account objects:
[{ "id": "<uuid-from-/accounts>" }].object | null
Optional.
video, slideshow, or null/omit for text-only posts.Show Video
Show Video
string
required
videostring
required
Canonical backend-attested video URL. For Partner uploads, use
data.file.url returned by
POST /files/{fileId}/finalize; upload destinations and arbitrary remote links are rejected.number
Duration in seconds. Also accepts
duration_seconds, duration, durationSec, or
video_duration_sec. Required for non-premium X video posts.number
Optional size in bytes. Also accepts
size.number
Optional width in pixels.
number
Optional height in pixels.
string
Optional MIME type. Alias:
mime.object
Optional nested object that can repeat
width, height, mime_type, and bytes.string
ISO 8601 datetime with timezone offset (for example
2026-05-21T15:00:00Z). Omit or send a
time within 30 seconds of now for immediate publish (status: pending). For true scheduled posts,
the time must be at least 2 minutes in the future.string
Recommended idempotency key (max 128 chars). Same key + same payload replays the original post
(
200, duplicate: true). Same key + different payload returns 409 create_failed. If omitted,
the API dedupes from a SHA-256 fingerprint of the request body instead; a conflicting replay also
returns 409 create_failed.string
TikTok-only. A TikTok post URL used to resolve the track. Rejected when any non-TikTok account is
selected, including Instagram.
Provider settings
Usesettings.<provider> for X, Instagram, Facebook, LinkedIn, Bluesky,
and YouTube.
For TikTok and Pinterest on create, use the top-level tiktok and pinterest
objects — not settings.tiktok or settings.pinterest. Top-level fields run the full
create-time validation (BYO-only TikTok, hosted TikTok rejection, MEDIA_UPLOAD rules, and
field limits).
Only send settings for providers present in your accounts list. Unknown settings keys return
422 invalid_payload.
capabilities.settings_schema from GET /accounts advertises the
public field names accepted for that provider. Use top-level tiktok/pinterest, or
settings.<provider> for the remaining providers. Hosted accounts omit fields their provider
cannot accept. Cross-field and media/account-specific rules are still validated when you create or
update the post.
YouTube — settings.youtube
Requires a video post to a YouTube account.
| Field | Type | Required | Notes |
|---|---|---|---|
title | string | No | Max 100 chars. If omitted, derived from settings.youtube.description, then caption (first non-empty line). |
description | string | No | Overrides caption as the YouTube description when set. |
type | string | No | Visibility: public, private, or unlisted. Default public. Alias: privacy. |
privacy | string | No | Same as type. |
tags | array | No | { "value": "...", "label": "..." } items. Publish uses label values. Total tag length capped at publish time. |
categoryId | string | No | YouTube category ID. |
selfDeclaredMadeForKids | string | No | yes or no. Default no. Boolean alias: madeForKids. |
thumbnail | object | No | Custom thumbnail: { "path": "<image-url>" }. |
TikTok — top-level tiktok
Requires BYO TikTok accounts and media (video or slideshow). Hosted TikTok accounts cannot use
TikTok-specific settings.
For video posts, the merged publish text built from caption, tiktok.title, and
tiktok.description must stay ≤ 2,200 characters combined.
| Field | Type | Notes |
|---|---|---|
title | string | Video: max 2,200 chars. Slideshow: max 90 chars. |
description | string | Slideshow: max 4,000 chars. Video: used as the publish body when caption is empty. |
post_mode | string | DIRECT_POST or MEDIA_UPLOAD (slideshow drafts only). Defaults to DIRECT_POST at publish time when omitted. |
privacy_level | string | PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLY. |
music_usage_confirmation | boolean | Confirms the user has accepted TikTok’s music-usage terms for this post. Preferred Partner API field. |
user_consent | boolean | Legacy alias for music_usage_confirmation. |
disable_comment | boolean | Inverted to allow/deny comments. |
disable_duet | boolean | Video only. |
disable_stitch | boolean | Video only. |
auto_add_music | boolean | Slideshow / photo DIRECT_POST only. Omitted defaults to true (TikTok auto-picks a soundtrack). false still publishes silent. Videos ignore this field. Hosted TikTok ignores it — attach a music URL instead. |
is_your_brand | boolean | Own-brand commercial content. |
is_branded_content | boolean | Third-party promotion. |
video_cover_timestamp_ms | integer | Video DIRECT_POST only. Frame offset in milliseconds for the TikTok thumbnail. |
Pinterest — top-level pinterest
Requires at least one Pinterest account and image media.
| Field | Type | Notes |
|---|---|---|
board_id | string | Pinterest board ID. Required for a successful pin publish — no account default is applied. Max 128 chars. |
title | string | Optional pin title. Max 100 chars. |
link | string | Optional destination URL. Max 2,048 chars. |
tags | string[] | Up to 30 tags, 1–100 chars each. Appended to the pin description; total description must stay ≤ 800 chars. |
X — settings.x or settings.twitter
| Field | Type | Required | Notes |
|---|---|---|---|
who_can_reply_post | string | No | everyone (default), following, mentionedUsers, subscribers, verified. |
community | string | No | X community URL: https://x.com/i/communities/<id>. |
made_with_ai | boolean | No | Default false. |
paid_partnership | boolean | No | Default false. |
Instagram — settings.instagram or settings.instagram-standalone
Both keys map to the same stored settings. If both are sent, instagram-standalone wins.
| Field | Type | Required | Notes |
|---|---|---|---|
post_type | string | Recommended | post (feed/reel) or story. |
is_trial_reel | boolean | No | Trial reel flag for video posts. |
graduation_strategy | string | No | MANUAL or SS_PERFORMANCE when using trial reels. |
collaborators | array | No | [{ "label": "<username>" }]. Ignored for stories. |
cover_url | string | No | Custom Reel cover image URL. Takes priority over thumb_offset_ms. Video post_type: post only. Must be publicly accessible. BYO Instagram only — hosted accounts reject cover settings. |
thumb_offset_ms | integer | No | Reel thumbnail frame offset in milliseconds when cover_url is omitted. Video post_type: post only. BYO Instagram only. |
share_to_feed | boolean | No | Share the Reel to the main feed. Default true. Set false for Reels tab only. BYO Instagram only. |
Facebook — settings.facebook
| Field | Type | Required | Notes |
|---|---|---|---|
url | string | No | Optional link attachment URL on the post. |
LinkedIn — settings.linkedin or settings.linkedin-page
Both keys map to the same stored settings. If both are sent, linkedin-page wins.
| Field | Type | Required | Notes |
|---|---|---|---|
post_as_images_carousel | boolean | No | Post multi-image content as a LinkedIn carousel/PDF. |
carousel_name | string | No | Carousel/PDF title. Default slides. |
Bluesky — settings.bluesky
No provider-specific fields. Send {} or omit.
Settings key reference
| Request key | Alias | Stored as |
|---|---|---|
settings.x | settings.twitter | xSettings |
settings.instagram-standalone | settings.instagram | instagramSettings |
settings.linkedin-page | settings.linkedin | linkedinSettings |
Top-level tiktok | — | tikTokSettings |
Top-level pinterest | — | pinterestSettings |
Limits (quick reference)
- Video: MP4/MOV/M4V/AVI, under 100MB. Hosted accounts: 15–60s when duration is provided.
- Non-premium X video: duration metadata required; max 140 seconds.
- Slideshow: 1–35 images, each under 5MB.
- Caption caps (BYO): X 280 (4,000 for premium accounts); Instagram 2,200; TikTok video 2,200 / photo description 4,000; LinkedIn 3,000; Pinterest 800; YouTube 5,000 bytes; Facebook 63,206.
Response
Fresh creates return201. Idempotent replays return 200.
{
"ok": true,
"code": 201,
"message": "Post scheduled",
"data": {
"id": "11111111-1111-1111-1111-111111111111",
"status": "scheduled",
"scheduled_at": "2026-05-21T15:00:00Z",
"warnings": []
}
}
duplicate: true appears on idempotent replays. warnings are non-blocking (for example missing
video duration metadata).
Examples
curl --request POST \
--url https://www.genviral.io/api/partner/v1/posts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"caption": "Weekly product recap and what shipped this sprint.",
"media": {
"type": "video",
"url": "https://cdn.example.com/recap.mp4",
"duration_sec": 42
},
"accounts": [{ "id": "YOUR_YOUTUBE_ACCOUNT_ID" }],
"settings": {
"youtube": {
"title": "Weekly product recap",
"type": "public"
}
},
"external_id": "yt-recap-2026-05-21"
}'
curl --request POST \
--url https://www.genviral.io/api/partner/v1/posts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"caption": "3 outfits for the long weekend",
"media": {
"type": "slideshow",
"urls": [
"https://cdn.example.com/look-1.jpg",
"https://cdn.example.com/look-2.jpg",
"https://cdn.example.com/look-3.jpg"
]
},
"accounts": [{ "id": "YOUR_TIKTOK_BYO_ACCOUNT_ID" }],
"tiktok": {
"post_mode": "MEDIA_UPLOAD",
"music_usage_confirmation": true,
"privacy_level": "SELF_ONLY"
},
"external_id": "tiktok-draft-001"
}'
curl --request POST \
--url https://www.genviral.io/api/partner/v1/posts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"caption": "Minimal kitchen organization ideas that actually stick.",
"media": {
"type": "slideshow",
"urls": ["https://cdn.example.com/pin-cover.jpg"]
},
"accounts": [{ "id": "YOUR_PINTEREST_ACCOUNT_ID" }],
"pinterest": {
"board_id": "YOUR_BOARD_ID",
"link": "https://example.com/kitchen-guide",
"tags": ["kitchen organization", "home tips"]
},
"external_id": "pinterest-pin-001"
}'
curl --request POST \
--url https://www.genviral.io/api/partner/v1/posts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"caption": "Behind the scenes from today'\''s shoot.",
"media": {
"type": "video",
"url": "https://cdn.example.com/reel.mp4"
},
"accounts": [{ "id": "YOUR_INSTAGRAM_BYO_ACCOUNT_ID" }],
"settings": {
"instagram": {
"post_type": "post",
"cover_url": "https://cdn.example.com/reel-cover.jpg",
"share_to_feed": false
}
},
"external_id": "instagram-reel-cover-001"
}'
curl --request POST \
--url https://www.genviral.io/api/partner/v1/posts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"caption": "Ship the repeatable thing, not the busywork.",
"accounts": [{ "id": "YOUR_X_ACCOUNT_ID" }],
"media": null,
"settings": {
"x": {
"who_can_reply_post": "everyone"
}
}
}'
Errors
| Code | When |
|---|---|
422 invalid_payload | Schema validation failed (includes a fields array naming each offending path) |
400 invalid_json | Request body is not valid JSON |
400 validation_failed | Caption, media, music, TikTok/Pinterest, or schedule rules failed |
400 unknown_accounts | Account ID outside key scope |
400 media_unreachable | Media URL not reachable |
400 video_verification_required | Video must complete POST /files with a stable idempotency key → presigned PUT → POST /files/{fileId}/finalize with matching metadata and another stable key; retry with data.file.url |
400 invalid_music_url | Invalid or non-TikTok music URL |
401 | Missing or invalid API key |
402 subscription_required | No active Creator, Professional, or Business subscription |
402 insufficient_credits | Buy a credit pack at https://www.genviral.io/billing?tab=credits, then retry |
403 subscription_required | Hosted TikTok posting without an active TikTok virtual subscription |
403 tier_not_allowed | Scheduler tier blocked |
409 create_failed | Same external_id or dedupe fingerprint reused with a different create payload |
500 create_failed | Unexpected upstream failure |
