Skip to content

Posting

longwei supports posting text statuses, media attachments, polls, scheduled statuses, and sensitive content with content warnings.

import httpx
from longwei import APClient, Visibility

async with httpx.AsyncClient() as client:
    ap = await APClient.create(
        instance="mastodon.social",
        client=client,
        access_token="your_token",
    )

Text status

status = await ap.post_status("Hello, fediverse!")
print(f"Posted: {status.url}")

Visibility.PUBLIC is the default. To change it:

status = await ap.post_status(
    "Only my followers can see this",
    visibility=Visibility.PRIVATE,
)

See Enums — Visibility for all visibility options.


Content warning / spoiler text

status = await ap.post_status(
    "Detailed discussion of the plot twist...",
    spoiler_text="Spoilers for Season 3",
)

The spoiler_text is shown as a content warning; the status body is hidden behind a "Show more" button on most clients.


Sensitive content

Mark a status as sensitive (typically used for explicit media or political content):

status = await ap.post_status(
    "This post contains sensitive content",
    sensitive=True,
)

Media attachments

1. Upload the file

with open("photo.jpg", "rb") as f:
    result = await ap.post_media(
        file=f,
        mime_type="image/jpeg",
        description="A cat sitting on a keyboard",  # alt text for accessibility
    )
media_id = result.id

2. Attach to a status

status = await ap.post_status(
    "Look at this cat!",
    media_ids=[media_id],
)

Multiple attachments

ids = []
for filename, mime_type in [("cat.jpg", "image/jpeg"), ("dog.png", "image/png")]:
    with open(filename, "rb") as f:
        result = await ap.post_media(file=f, mime_type=mime_type)
        ids.append(result.id)

status = await ap.post_status("Cats vs dogs", media_ids=ids)

The maximum number of attachments is server-specific. After APClient.create(), check ap.max_attachments for the current instance's limit.

Focal point

Specify a focal point for cropping previews. Coordinates range from -1.0 to 1.0, with (0.0, 0.0) being the centre:

with open("portrait.jpg", "rb") as f:
    result = await ap.post_media(
        file=f,
        mime_type="image/jpeg",
        description="Portrait photo",
        focus=(0.0, 0.5),   # centre-top
    )

Polls

Attach a poll by passing poll_options (at least 2 choices) and poll_expires_in (duration in seconds). Polls are mutually exclusive with media_ids.

status = await ap.post_status(
    "What is your favourite Python web framework?",
    poll_options=["Django", "FastAPI", "Flask", "Other"],
    poll_expires_in=86400,  # 24 hours
)

Allow voters to pick more than one option with poll_multiple:

status = await ap.post_status(
    "Which languages do you use regularly?",
    poll_options=["Python", "Rust", "Go", "TypeScript"],
    poll_expires_in=172800,  # 48 hours
    poll_multiple=True,
)

Hide the running totals until the poll closes with poll_hide_totals:

status = await ap.post_status(
    "Tabs or spaces?",
    poll_options=["Tabs", "Spaces"],
    poll_expires_in=3600,   # 1 hour
    poll_hide_totals=True,
)

Scheduled statuses

Pass a datetime at least 5 minutes in the future. Naive datetimes are treated as UTC:

from datetime import datetime, timezone

# Using a timezone-aware datetime
scheduled = datetime(2025, 12, 25, 9, 0, 0, tzinfo=timezone.utc)
status = await ap.post_status(
    "Happy holidays!",
    scheduled_at=scheduled,
)

# Naive datetime — longwei will treat it as UTC
scheduled_naive = datetime(2025, 12, 25, 9, 0, 0)
status = await ap.post_status("Happy holidays!", scheduled_at=scheduled_naive)

Complete example

import httpx
from datetime import datetime, timezone
from longwei import APClient, Visibility

async def post_with_image(instance: str, token: str, image_path: str) -> None:
    async with httpx.AsyncClient() as client:
        ap = await APClient.create(
            instance=instance,
            client=client,
            access_token=token,
        )

        # Upload media
        with open(image_path, "rb") as f:
            media = await ap.post_media(
                file=f,
                mime_type="image/jpeg",
                description="An image",
            )

        # Post with attachment and content warning
        status = await ap.post_status(
            "Check out this photo!",
            visibility=Visibility.PUBLIC,
            media_ids=[media.id],
            sensitive=False,
            spoiler_text=None,
        )

        print(f"Posted: {status.url}")
        print(f"Status ID: {status.id}")