PostZen
Media

Upload a file directly

POST
/v1/media/upload-direct

Uploads a file in the request itself and returns a public URL, for cases where a two-step presign is inconvenient and the file is small — chiefly direct message attachments, where Meta fetches the URL itself. The stored object is deleted automatically after seven days, so it is not a substitute for POST /v1/media/presign when publishing posts. Maximum 25MB. Requires a read-write API key.

Authorization

bearerAuth
AuthorizationBearer <token>

PostZen API key.

In: header

Request Body

multipart/form-data

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/v1/media/upload-direct" \  -F file="string"
{  "url": "https://media.postzen.dev/direct-uploads/0001787068800000-6f0d0296-6a1c-4d5b-9a2e-3f8c1b7d4e90/receipt.jpg",  "filename": "receipt.jpg",  "contentType": "image/jpeg",  "size": 184320}
{  "error": "JSON body is required"}
{  "error": "Unauthorized"}
{  "error": "API key requires read_write permission"}
{  "error": "rate_limited",  "retryAfter": 30}
{  "error": "Failed to create post"}

Create a presigned media upload URL POST

Creates a presigned URL for uploading an image, video, GIF, or PDF to PostZen-hosted storage. Upload the file with an HTTP `PUT` to `uploadUrl`, then reference `publicUrl` in post `mediaItems`. This endpoint requires a read-write API key.

Bulk upload posts from CSV POST

Validates and creates up to 500 posts from a CSV file. This endpoint requires a read-write API key. Each data row targets every connected account for the selected platforms on one profile. Set `dryRun=true` to validate without billing checks, media ingestion, or writes. A mixed success/failure response uses HTTP 207; all-success and all-failure responses use HTTP 200. | CSV column | Purpose | | --- | --- | | `post_content` | Required base post text. | | `platforms` | Required comma-separated platforms: `instagram`, `facebook`, `threads`, `tiktok`, `linkedin`, `x`, `youtube`, `pinterest`, `bluesky`, or `telegram`; `twitter` aliases `x`. | | `profiles` | Required single profile id or unique profile name. | | `schedule_time` | Required column. Use `YYYY-MM-DD HH:mm[:ss]` in `tz`, or ISO 8601 with an explicit offset. May be empty for draft, publish-now, or queue rows. | | `tz` | IANA timezone; defaults to `UTC`. | | `media_urls` | Comma-separated HTTP(S) URLs, up to 10 per row and 50 distinct URLs per upload. | | `is_draft`, `publish_now`, `use_queue` | Mutually exclusive boolean mode flags. With none set, the row is scheduled. | | `title`, `tags`, `hashtags`, `mentions`, `visibility` | General title, comma-separated tags, appended hashtags/mentions, and YouTube visibility. | | `custom_content_<platform>` | Platform-specific content override; `custom_content_twitter` targets X. | | `youtube_title`, `youtube_description` | YouTube title and description override. | | `facebook_first_comment`, `linkedin_first_comment` | First comments. | | `instagram_content_type`, `instagram_collaborators`, `instagram_first_comment` | Instagram post type, collaborators, and first comment. | | `tiktok_privacy`, `tiktok_allow_comments`, `tiktok_allow_duet`, `tiktok_allow_stitch`, `tiktok_brand_partner`, `tiktok_organic_brand`, `tiktok_draft`, `tiktok_description` | TikTok publishing options. | | `telegram_parse_mode`, `telegram_disable_web_page_preview`, `telegram_disable_notification`, `telegram_protect_content` | Telegram publishing options. | | `pinterest_title`, `pinterest_link`, `pinterest_board_id` | Pinterest pin options. |