How to Publish Social Media Posts From Python

How to Publish Social Media Posts From Python

Post and schedule across 15 networks from Python. Working examples, the per-platform fields that are actually required, and how to avoid surprises.

If your content pipeline is in Python, whether that’s a scraper, a data job or an LLM workflow, publishing shouldn’t mean shelling out to something else.

Install and authenticate

pip install bulkpublish
from bulkpublish import BulkPublish

bp = BulkPublish("bp_your_key_here")

The client also reads BULKPUBLISH_API_KEY from the environment, which is the better habit:

import os
from bulkpublish import BulkPublish

bp = BulkPublish(os.environ["BULKPUBLISH_API_KEY"])

Create a draft first

post = bp.posts.create(
    content="Launching our new product today!",
    channels=[
        {"channelId": 1, "platform": "x"},
        {"channelId": 2, "platform": "linkedin"},
    ],
    status="draft",
)

Drafts are visible in the app, so you can see what your code actually produced before an audience does. Move to "scheduled" once you’re happy.

Find your channel IDs rather than hardcoding them:

channels = bp.channels.list()

Schedule one

post = bp.posts.create(
    content="Launching our new product today!",
    channels=[{"channelId": 1, "platform": "x"}],
    status="scheduled",
    scheduled_at="2026-04-10T09:00:00Z",
    timezone="America/New_York",
)

scheduled_at is ISO-8601 and timezone is an IANA name. Pass the timezone explicitly for anything recurring, otherwise your posting time drifts by an hour twice a year when daylight saving changes.

Per-platform requirements

This is the part that surprises people. Some platforms need fields others don’t, and platform_specific is where they go. YouTube, for example, requires a title of 1 to 100 characters.

post = bp.posts.create(
    content="Behind the scenes on this month's build.",
    channels=[{"channelId": 5, "platform": "youtube"}],
    media_files=[file_id],
    platform_specific={"youtube": {"title": "How we ship every week"}},
    status="scheduled",
    scheduled_at="2026-04-10T09:00:00Z",
)

If a required per-platform field is missing, the post is rejected when you create it rather than failing quietly later. Catch that exception and log it, because in an automated pipeline nobody is watching the screen.

Different text per platform

platform_content takes per-platform overrides, which is how one call publishes to several networks without sending identical text everywhere:

bp.posts.create(
    content="Default text for anything not overridden.",
    platform_content={
        "x": "The short version.",
        "linkedin": "The longer version, with the reasoning behind it.",
    },
    channels=[
        {"channelId": 1, "platform": "x"},
        {"channelId": 2, "platform": "linkedin"},
    ],
    status="draft",
)

Worth using. A pipeline that broadcasts identical text to fifteen networks is the thing that makes automated posting obvious.

Rate limits

FreeProBusiness
API requests/day305,00050,000
API keys1510
Free’s 30 requests a day is for evaluating the API. A real pipeline needs a paid plan, and a loop with no backoff will burn the free allowance in seconds.

If an LLM is writing the content

Two rules that matter more in Python than anywhere, because this is where those pipelines usually live.

Create drafts, not published posts, at least until you’ve watched it for a while. Generated text is confidently wrong in ways a human draft isn’t.

Never let the model state a fact about a product. Prices and limits change, and a model will happily produce a plausible one. Anything factual comes from a source.

The short version

pip install bulkpublish, construct with your key from the environment, call bp.posts.create. Use platform_specific for required per-platform fields like YouTube titles, platform_content for different text per network, pass a timezone with anything scheduled, and start with drafts.