Python SDK

Posts

Create, schedule, and manage social media posts with the Late Python SDK

The Posts resource allows you to create, schedule, update, and manage social media posts across all connected platforms.

Type-Safe Enums

The SDK provides enums for platforms, statuses, and media types. Using enums gives you autocomplete, type checking, and prevents typos:

from late import Platform, PostStatus, MediaType

# Enums work like strings - these are equivalent:
Platform.TWITTER == "twitter"  # True
PostStatus.SCHEDULED == "scheduled"  # True

Supported Platforms

PlatformEnumString ID
Twitter/XPlatform.TWITTER"twitter"
InstagramPlatform.INSTAGRAM"instagram"
FacebookPlatform.FACEBOOK"facebook"
LinkedInPlatform.LINKEDIN"linkedin"
TikTokPlatform.TIKTOK"tiktok"
YouTubePlatform.YOUTUBE"youtube"
PinterestPlatform.PINTEREST"pinterest"
RedditPlatform.REDDIT"reddit"
BlueskyPlatform.BLUESKY"bluesky"
ThreadsPlatform.THREADS"threads"
Google BusinessPlatform.GOOGLE_BUSINESS"googlebusiness"

Post Statuses

StatusEnumDescription
draftPostStatus.DRAFTSaved but not scheduled
scheduledPostStatus.SCHEDULEDScheduled for future publishing
publishingPostStatus.PUBLISHINGCurrently being published
publishedPostStatus.PUBLISHEDSuccessfully published
failedPostStatus.FAILEDPublishing failed
partialPostStatus.PARTIALSome platforms failed, others succeeded

Create a Post

Basic Post

from late import Late, Platform
from datetime import datetime, timedelta

client = Late(api_key="your_api_key")

# Schedule a post for 1 hour from now
response = client.posts.create(
    content="Hello from the Late Python SDK!",
    platforms=[
        {"platform": Platform.TWITTER, "accountId": "acc_123"}
    ],
    scheduled_for=datetime.now() + timedelta(hours=1)
)

print(response.post.field_id)

Publish Immediately

post = client.posts.create(
    content="This goes live right now!",
    platforms=[{"platform": Platform.TWITTER, "accountId": "acc_123"}],
    publish_now=True
)

Save as Draft

post = client.posts.create(
    content="Draft for later review",
    platforms=[{"platform": Platform.TWITTER, "accountId": "acc_123"}],
    is_draft=True
)

Multi-Platform Post

post = client.posts.create(
    content="Cross-posting to multiple platforms!",
    platforms=[
        {"platform": Platform.TWITTER, "accountId": "tw_123"},
        {"platform": Platform.LINKEDIN, "accountId": "li_456"},
        {"platform": Platform.FACEBOOK, "accountId": "fb_789"}
    ],
    scheduled_for=datetime.now() + timedelta(days=1),
    timezone="America/New_York"
)

Post with Media

from late import MediaType

# First upload your media
media_result = client.media.upload("product.jpg")
image_url = media_result.files[0].url

# Create post with media
post = client.posts.create(
    content="Check out our new product!",
    platforms=[{"platform": Platform.INSTAGRAM, "accountId": "ig_123"}],
    media_items=[
        {"type": MediaType.IMAGE, "url": image_url}
    ],
    scheduled_for=datetime.now() + timedelta(hours=2)
)

Platform-Specific Content

Customize content per platform:

post = client.posts.create(
    content="Default content for all platforms",
    platforms=[
        {
            "platform": Platform.TWITTER,
            "accountId": "tw_123",
            "customContent": "Short version for Twitter! #late"
        },
        {
            "platform": Platform.LINKEDIN,
            "accountId": "li_456",
            "customContent": "Professional version with more detail for LinkedIn..."
        }
    ],
    scheduled_for=datetime.now() + timedelta(hours=1)
)

YouTube Post

post = client.posts.create(
    content="Video description goes here...",
    title="My YouTube Video Title",
    platforms=[{"platform": Platform.YOUTUBE, "accountId": "yt_123"}],
    media_items=[{"type": MediaType.VIDEO, "url": video_url}],
    tags=["tech", "tutorial", "python"],  # YouTube tags
    scheduled_for=datetime.now() + timedelta(hours=3)
)

TikTok Post

TikTok requires tiktok_settings with specific disclosure and consent fields.

from late import TikTokPrivacyLevel

post = client.posts.create(
    content="TikTok video caption!",
    platforms=[{"platform": Platform.TIKTOK, "accountId": "tt_123"}],
    media_items=[{"type": MediaType.VIDEO, "url": video_url}],
    tiktok_settings={
        "privacy_level": TikTokPrivacyLevel.PUBLIC_TO_EVERYONE,  # Required
        "allow_comment": True,                                    # Required
        "allow_duet": True,                                       # Required for videos
        "allow_stitch": True,                                     # Required for videos
        "content_preview_confirmed": True,                        # Required, must be True
        "express_consent_given": True,                            # Required, must be True
    },
    scheduled_for=datetime.now() + timedelta(hours=1)
)

TikTok Settings Fields:

FieldTypeRequiredDescription
privacy_levelTikTokPrivacyLevelYesPUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, or SELF_ONLY
allow_commentboolYesAllow comments on the post
allow_duetboolVideosAllow duets (required for video posts)
allow_stitchboolVideosAllow stitches (required for video posts)
content_preview_confirmedboolYesMust be True
express_consent_givenboolYesMust be True
draftboolNoSend to Creator Inbox as draft
descriptionstringNoLong description for photo posts (max 4000 chars)
video_cover_timestamp_msintNoThumbnail frame timestamp in ms
auto_add_musicboolNoLet TikTok add music (photos only)

List Posts

Basic Listing

# Get all posts (paginated)
response = client.posts.list()

for post in response.posts:
    print(f"{post.field_id}: {post.content[:50]}...")

# Pagination info
print(f"Page {response.pagination.page} of {response.pagination.pages}")

Filter Posts

from late import PostStatus

# Get scheduled posts only
scheduled = client.posts.list(status=PostStatus.SCHEDULED)

# Filter by platform
twitter_posts = client.posts.list(platform=Platform.TWITTER)

# Filter by date range
recent = client.posts.list(
    date_from="2024-01-01",
    date_to="2024-01-31"
)

# Filter by profile
profile_posts = client.posts.list(profile_id="profile_123")

# Pagination
page_2 = client.posts.list(page=2, limit=25)

# Include hidden posts
all_posts = client.posts.list(include_hidden=True)

Get a Single Post

response = client.posts.get("post_123")

print(f"Content: {response.post.content}")
print(f"Status: {response.post.status}")
print(f"Scheduled for: {response.post.scheduledFor}")

Update a Post

Only draft, scheduled, failed, and partial posts can be updated. Published posts cannot be modified.

# Update content
client.posts.update(
    "post_123",
    content="Updated post content!"
)

# Reschedule
client.posts.update(
    "post_123",
    scheduled_for=datetime.now() + timedelta(days=2),
    timezone="Europe/London"
)

# Update multiple fields
client.posts.update(
    "post_123",
    content="New content",
    title="New title",
    tags=["updated", "tags"]
)

Delete a Post

# Delete a draft or scheduled post
response = client.posts.delete("post_123")
print(response.message)

Published posts cannot be deleted. If you deleted a scheduled post that consumed upload quota, the quota is automatically refunded.

Retry Failed Posts

# Retry a failed post
response = client.posts.retry("post_123")
print(response.message)

Bulk Upload from CSV

Upload multiple posts from a CSV file:

# Validate first (dry run)
validation = client.posts.bulk_upload("posts.csv", dry_run=True)

if validation["success"]:
    print(f"Validation passed: {validation['totalRows']} posts ready")
    # Actually create the posts
    result = client.posts.bulk_upload("posts.csv")
    print(f"Created {result['created']} posts")
else:
    print("Validation errors:")
    for error in validation["errors"]:
        print(f"  Row {error['row']}: {error['message']}")

CSV Format:

content,platform,account_id,scheduled_for,title,media_url,tags
"Hello world!",twitter,acc123,2024-12-25T10:00:00Z,,,
"LinkedIn post",linkedin,acc456,2024-12-25T11:00:00Z,My Title,,tag1|tag2
"With media",instagram,acc789,2024-12-25T12:00:00Z,,https://example.com/image.jpg,

Async Methods

All methods have async versions:

import asyncio
from late import Late, Platform, PostStatus

async def manage_posts():
    async with Late(api_key="your_api_key") as client:
        # List
        posts = await client.posts.alist(status=PostStatus.SCHEDULED)

        # Get
        post = await client.posts.aget("post_123")

        # Create
        new_post = await client.posts.acreate(
            content="Async post!",
            platforms=[{"platform": Platform.TWITTER, "accountId": "..."}],
            publish_now=True
        )

        # Update
        await client.posts.aupdate("post_123", content="Updated!")

        # Delete
        await client.posts.adelete("post_123")

        # Retry
        await client.posts.aretry("post_123")

asyncio.run(manage_posts())

Error Handling

from late.client.exceptions import (
    LateAPIError,
    LateNotFoundError,
    LateValidationError
)

try:
    post = client.posts.get("invalid_id")
except LateNotFoundError:
    print("Post not found")
except LateValidationError as e:
    print(f"Validation error: {e.message}")
except LateAPIError as e:
    print(f"API error: {e.message}")

API Reference

posts.create()

Create a new post.

ParameterTypeRequiredDefaultDescription
contentstrYesPost content/text
platformslist[dict]YesList of platform targets (see below)
titlestrNoNoneTitle for YouTube, Pinterest
media_itemslist[dict]NoNoneList of media with type and url
scheduled_fordatetime | strNoNoneWhen to publish
publish_nowboolNoFalsePublish immediately
is_draftboolNoFalseSave as draft
timezonestrNo"UTC"IANA timezone
tagslist[str]NoNoneTags (YouTube: max 100 chars each)
hashtagslist[str]NoNoneHashtags
mentionslist[str]NoNoneMentions
crossposting_enabledboolNoTrueEnable crossposting
metadatadictNoNoneCustom metadata
tiktok_settingsdictNoNoneTikTok-specific settings
queued_from_profilestrNoNoneProfile ID if using queue

Platform object structure:

{
    "platform": Platform.TWITTER,   # Required (Platform enum or string)
    "accountId": "acc_123",         # Required
    "customContent": "...",         # Optional: per-platform content
    "customMedia": [...],           # Optional: per-platform media
    "scheduledFor": "...",          # Optional: per-platform schedule
    "platformSpecificData": {...}   # Optional: platform-specific options
}

posts.list()

List posts with optional filters.

ParameterTypeRequiredDefaultDescription
pageintNo1Page number (1-based)
limitintNo10Posts per page (max 100)
statusPostStatus | strNoNoneFilter by status
platformPlatform | strNoNoneFilter by platform
profile_idstrNoNoneFilter by profile ID
created_bystrNoNoneFilter by creator user ID
date_fromstrNoNoneFilter from date (YYYY-MM-DD)
date_tostrNoNoneFilter to date (YYYY-MM-DD)
include_hiddenboolNoNoneInclude hidden posts

posts.update()

Update an existing post.

ParameterTypeRequiredDescription
post_idstrYesID of the post to update
contentstrNoNew content
titlestrNoNew title
platformslist[dict]NoNew platform targets
media_itemslist[dict]NoNew media items
scheduled_fordatetime | strNoNew scheduled time
timezonestrNoNew timezone
tagslist[str]NoNew tags
hashtagslist[str]NoNew hashtags
mentionslist[str]NoNew mentions
metadatadictNoNew metadata
tiktok_settingsdictNoNew TikTok settings

Response Types

All methods return typed Pydantic models:

MethodReturn Type
posts.list()PostsListResponse
posts.get()PostGetResponse
posts.create()PostCreateResponse
posts.update()PostUpdateResponse
posts.delete()PostDeleteResponse
posts.retry()PostRetryResponse

PostCreateResponse

response = client.posts.create(...)

response.message      # str: "Post created successfully"
response.post         # Post model
response.post.field_id
response.post.content
response.post.status  # Status enum
response.post.scheduledFor
response.post.platforms
response.post.mediaItems

PostsListResponse

response = client.posts.list()

response.posts        # list[Post]
response.pagination   # Pagination model
response.pagination.page
response.pagination.limit
response.pagination.total
response.pagination.pages