Skip to content

Posting

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

from longwei import APClient, Visibility

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

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

1b. Update media metadata (optional)

After uploading, you can update the description or focal point before posting:

result = await ap.update_media(
    media_id,
    description="Updated alt text",
    focus=(0.0, -0.5),   # centre-bottom
)

Both parameters are optional; omit any you don't want to change.

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)

Managing scheduled statuses

Use the management methods to list, inspect, reschedule, or cancel statuses that have been scheduled with post_status(scheduled_at=...).

List all scheduled statuses

scheduled = await ap.get_scheduled_statuses()
for s in scheduled:
    print(f"{s.id} scheduled for {s.scheduled_at}: {s.params.text}")

Paginate with max_id, min_id, or limit:

page = await ap.get_scheduled_statuses(limit=20, max_id="12300")

Fetch a single scheduled status

s = await ap.get_scheduled_status("12345")
print(s.scheduled_at)

Reschedule

from datetime import datetime, timezone

new_time = datetime(2025, 12, 31, 23, 59, 0, tzinfo=timezone.utc)
s = await ap.update_scheduled_status("12345", scheduled_at=new_time)
print(s.scheduled_at)

Naive datetimes are treated as UTC.

Cancel

await ap.delete_scheduled_status("12345")

Complete example

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 await APClient.create(
            instance=instance,
            access_token=token,
    ) as ap:

        # 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}")