Publish and schedule posts with the Python SDK
client.uploads creates posts, checks their status, and retries or publishes them. All arguments are keyword-only, and every method returns a dict.
from socifyr import Socifyr
client = Socifyr()How do I publish a text post from Python?
ack = client.uploads.text(
connections=["x-myhandle", "linkedin-mybrand"],
text="Shipped v2 today.",
)
print(ack["requestId"], ack["totalChannels"])Signature:
text(*, connections: list[str], text: str, scheduled_at: str | None = None,
timezone: str | None = None, first_comment: str | dict | None = None,
save_draft: bool | None = None) -> dictconnections are slugs such as x-myhandle. See connection concepts. first_comment is a string or {"text": ..., "linkUrl": ..., "imageUrl": ...}.
What does a create call return?
Every create method (text, photos, video) sends async: true. That means the API queues the post and returns an acknowledgement instead of waiting:
{
"success": True,
"requestId": "9f2c7a1e-3d44-4b2a-8e1f-0a5c2d7e9f10",
"usage": {"count": 4, "limit": 100, "remaining": 96},
"message": "Upload processing started",
"scheduledAt": None,
"totalChannels": 2,
}There is no content ID in this ack. Use requestId with get_status; the status response includes uploadId. For a scheduled post, message is "Upload scheduled" and scheduledAt is set. usage.limit of -1 means unlimited.
Two other ack shapes exist:
| Case | Ack |
|---|---|
save_draft=True | {"success": True, "draftId": "..."} |
Key has requireApproval | {"success": True, "uploadId": "...", "status": "pending_approval", "message": "..."} |
How do I publish photos or a video?
photos and video take media by library ID or public URL. They cannot upload a file. See Media.
client.uploads.photos(
connections=["instagram-mybrand"],
medias=["6a04f715d3d7d93575e65d13", "https://cdn.example.com/b.jpg"],
text="Two photos from the shoot",
)
client.uploads.video(
connections=["youtube-mybrand", "tiktok-me"],
medias=["6a04f715d3d7d93575e65d14"],
text="Behind the scenes",
)Both accept connections, medias, text and scheduled_at only. For titles, descriptions, platformOptions or documents, use the TypeScript SDK or the REST API.
How do I schedule a post with the Python SDK?
client.uploads.text(
connections=["linkedin-mybrand"],
text="Tomorrow at 9am",
scheduled_at="2026-12-01T09:00:00Z",
timezone="America/New_York",
)scheduled_at is ISO-8601. timezone exists on text only. photos and video accept scheduled_at but not timezone.
How do I bulk schedule posts from a CSV?
res = client.uploads.bulk_schedule(rows=[
{"text": "Morning", "connections": ["x-me"], "scheduledAt": "2026-12-01T09:00:00Z"},
{"text": "Evening", "connections": ["x-me"], "scheduledAt": "2026-12-01T18:00:00Z"},
])
print(res["created"], res["failed"])Or pass a CSV string with the header text,connections,scheduledAt,title:
with open("posts.csv", encoding="utf-8") as f:
res = client.uploads.bulk_schedule(csv=f.read())Returns {"created": int, "failed": [{"row": int, "error": str}]}. A bad row is reported in failed and does not stop the rest. Row keys are camelCase (scheduledAt). Text posts only.
How do I check a post's status?
status = client.uploads.get_status(ack["requestId"])
print(status["status"]) # pending, processing, publishing, completed, partial_failure, failed
for ch in status["channels"]:
print(ch["platform"], ch["result"])get_status also accepts a content ID. There is no wait helper, so poll in a loop:
import time
def wait(client, request_id, interval=3, timeout=300):
deadline = time.time() + timeout
while time.time() < deadline:
s = client.uploads.get_status(request_id)
if s["status"] in ("completed", "failed", "partial_failure"):
return s
time.sleep(interval)
raise TimeoutError(request_id)How do I list, get, retry or publish a draft?
page = client.uploads.list(page=1, limit=20, status="failed")
for item in page["data"]:
print(item["id"], item["status"])
# "from" is a Python keyword, so pass date filters through a dict
recent = client.uploads.list(**{"from": "2026-11-01", "to": "2026-11-30"})
detail = client.uploads.get("6b1f0c2a9d3e4f5a6b7c8d9e")
client.uploads.retry("6b1f0c2a9d3e4f5a6b7c8d9e") # re-queue failed channels
client.uploads.publish_draft("6b1f0c2a9d3e4f5a6b7c8d9e") # publish a saved draftlist forwards any keyword arguments as query parameters (page, limit, status, platform, type, from, to). It returns {"data": [...], "meta": {"total", "page", "limit", "totalPages"}}. retry and publish_draft return the API ack.
What can I not do with the Python uploads resource?
No cancel, publish-now, update draft, archive, bulk delete, document posts, file upload, or approval calls. Use the TypeScript SDK or the REST API.
FAQ
Why is there no id in the ack?
The Python client forces async: true, and the async ack only carries requestId. Fetch the ID with get_status(...)["uploadId"].
Can I post to a slug that does not exist?
No. The API answers 400 and the SDK raises ValidationError. List valid slugs with list_connections.