# Quegly Quegly is a social media scheduling platform for creators, businesses and agencies. Schedule and publish posts to Instagram, TikTok, YouTube, Facebook, X, Threads, LinkedIn®, Bluesky, Pinterest, and Telegram from one dashboard. Quegly combines a visual content calendar, bulk upload, comment-to-DM auto-replies for Instagram and Facebook, team workspaces, per-platform analytics, and a public REST API and MCP server designed for AI agents and automation. Primary website: https://quegly.com Documentation: https://quegly.com/developers Documentation index for AI agents: https://quegly.com/llms.txt Full documentation in one file: https://quegly.com/llms-full.txt ## What Quegly is Quegly is a SaaS product. Users sign up, connect their social media accounts (Instagram, Facebook, YouTube, TikTok, X, Threads, LinkedIn®, Bluesky, Pinterest) via OAuth and their Telegram channels or groups through Quegly's bot, and schedule posts from a visual calendar. Posts can include text, images (up to 10), or video (1 per post). The dashboard handles per-platform constraints automatically: vertical YouTube videos under 60 seconds are auto-classified as Shorts, 2-10 Instagram images become a carousel, single Instagram videos become a Reel. ## When to use Quegly Use Quegly when a user wants to: - Schedule social media posts to Instagram, TikTok, YouTube, Facebook, X, Threads, LinkedIn®, Bluesky, Pinterest, or a Telegram channel or group - Upload a batch of videos (up to 30 at once) and spread them across a calendar - Plan a content calendar visually with week or month views - Automatically DM people who comment a keyword (like "LINK") on their Instagram or Facebook posts - Run several brands or clients, each in its own workspace with its own team - Track per-platform engagement metrics (views, likes, comments, watch time, retention, etc.) - Build automated publishing workflows or AI agent integrations via the REST API or the MCP server ## Plans Three subscription tiers. All plans include the same features. They differ only in workspace count. | Plan | Monthly | Yearly | Workspaces | | ------- | -------- | ----------- | ---------- | | Creator | $19.99 | $17.99/mo | 1 | | Pro | $39.99 | $35.99/mo | 3 | | Agency | $79.99 | $71.99/mo | 6 | Yearly billing saves about 10%. There are no free trials. Full pricing: https://quegly.com/pricing Quegly for iPhone (https://quegly.com/app, on the App Store at https://apps.apple.com/app/id6815466696) sells the same three plans through the App Store. A plan covers the account on the web and on iPhone, wherever it was bought. All plans include: - Posting to all 10 platforms (Instagram, TikTok, YouTube, Facebook, X, Threads, LinkedIn®, Bluesky, Pinterest, Telegram; Pinterest is rolling out, so not every account can connect it yet) - Unlimited connected social accounts - Up to 10 posts a day per workspace (5 on X/Twitter) - Bulk upload (up to 30 files at once) - Visual content calendar (week + month views) - Comment-to-DM auto-replies on Instagram and Facebook - Up to 10 team members per workspace - Per-platform analytics dashboard - Public REST API access Per-workspace limits: 10 posts per day (X/Twitter limited to 5 per day), 90-day scheduling window, 10 API keys per account. ## Core features ### Visual content calendar Week and month views. Posts appear on the day they are scheduled. Past-date posts are read-only. ### Bulk upload Drop up to 30 photos or videos onto the calendar at once. They wait in an upload tray, and dragging one onto a day starts a post for that day. This is how a month of content gets scheduled in one sitting. ### Composer Create posts from a single panel: - Per-post caption with optional per-platform overrides ("All" mode shares one caption across platforms; "Individual" mode lets you write a different caption per platform) - Native OS color emoji picker - Media upload: 1 to 10 images (JPEG/PNG, max 25 MB each) OR 1 video (MP4/MOV, max 500 MB). Cannot mix images and video in the same post. - Custom video covers: pick any frame of the video, or upload your own image - Instagram options: collaborators (up to 3), tagged accounts on Reels (up to 20), trial Reels (shown to non-followers first, then shared with followers by hand or automatically if they perform well), and Add to Story or Story only - Facebook Add to Story or Story only, Facebook Reel collaborators (other Pages, up to 10), YouTube title and privacy, TikTok privacy, interaction and disclosure settings, X media tags, Threads topic tags, LinkedIn® visibility and swipeable photo carousels, Pinterest board, title and link - Three publishing modes: schedule (future), post now (immediate), draft (no schedule) - Per-platform constraints validated upfront (YouTube needs a video; X is capped at 4 images; Bluesky photos are 2 MB at most; a Pinterest Pin needs a board and takes at most 5 photos; Telegram videos are 50 MB at most; etc.) ### Comment-to-DM auto-replies (Instagram and Facebook) When someone comments on a post, Quegly can send them a private DM automatically: the "comment LINK and I'll send you the link" flow. - Set up per connected account in the dashboard, in the Instagram Reply and Facebook Reply tabs. The settings cover every post that account published through Quegly. Posts published elsewhere are not answered. - Triggers: an exact keyword (the whole comment is the word, like LINK), a keyword anywhere in the comment as a whole word, or every comment. Keywords are not case-sensitive. - The DM: write several versions and Quegly picks one at random for each person, so replies don't all read the same. - Optional public reply under the comment (for example "Check your DMs!"), with its own rotating versions. It can be turned off. - Meta's rules apply: one private reply per comment, sent within 7 days of the comment, and only to people (a Facebook Page that comments can't be messaged). - Configured in the dashboard only; the public API does not expose auto-reply settings. ### Workspaces and teams (multi-tenancy) Each workspace owns its own social accounts, posts, media, and integrations. Workspaces are slug-based: `https://quegly.com//...`. Plans include 1, 3, or 6 workspaces (Creator/Pro/Agency). Invite up to 10 team members per workspace by email; they work in the workspace without anyone sharing social media passwords. Archived workspaces have a 30-day lifecycle before deletion. ### Per-platform engagement analytics Per-post metrics for each platform a post was published to. Backed by a 1-hour read-through cache and a 30-day refresh window. Posts older than 30 days keep their last captured snapshot. Available metrics by platform: - **Instagram**: views, reach, likes, comments, saved, shares, total_interactions, watch_time_seconds (Reels), avg_view_duration_seconds (Reels) - **Facebook**: views, likes, reactions, comments, shares, post_clicks - **YouTube**: views, likes, comments, favorites, shares, watch_time_seconds, avg_view_duration_seconds, avg_view_percentage, subscribers_gained, subscribers_lost - **TikTok**: views, likes, comments, shares - **X**: views, impressions, likes, comments, retweets, quotes, bookmarks, url_link_clicks (30-day window only), user_profile_clicks (30-day window only) - **Threads**: views, likes, comments (replies), reposts, quotes, shares - **LinkedIn®**: views, reach, likes, reactions, comments, reposts, shares (sent in LinkedIn® messages), saved, url_link_clicks, watch_time_seconds (video posts), once the member allows LinkedIn® post stats; empty until then - **Bluesky**: likes, comments (replies), reposts, quotes, bookmarks, read from Bluesky's public API with no permission needed; Bluesky has no views, reach or watch time - **Pinterest**: views (impressions), likes (reactions), comments, saved, post_clicks, url_link_clicks, for business accounts only (a personal account's Pins read 0); Pinterest's numbers come in a day or two late - **Telegram**: none. Telegram gives bots no views or other numbers for a post, so a Telegram post's metrics are always empty ### Public REST API (v1) Programmatic access to scheduling, uploads, accounts, workspaces, and analytics: - `GET /v1/workspaces`. List the workspaces the key can act on (discovery for all-scope keys) - `POST /v1/posts`. Create and schedule a post (single call fans out to multiple platforms via account_ids) - `GET /v1/posts`. List posts in a date range - `GET /v1/posts/{id}`. Get a single post - `PUT /v1/posts/{id}`. Update a scheduled post - `DELETE /v1/posts/{id}`. Delete a scheduled post - `POST /v1/posts/{id}/publish-now`. Publish immediately - `POST /v1/upload`. Upload media file (multipart; videos accept an optional `thumbnail` field) - `POST /v1/upload-from-url`. Upload media from a public URL - `POST /v1/media/{id}/thumbnail`. Set/replace a video's poster image (multipart `thumbnail`, or JSON `{ "url": "https://..." }` to fetch a public image; JPEG/PNG <=5MB). Also refreshes the cover on posts still scheduled/incomplete (response includes `posts_updated`). Videos only. - `DELETE /v1/media/{id}/thumbnail`. Remove a video's poster image (idempotent) - `GET /v1/accounts`. List connected social accounts in the workspace - `GET /v1/pinterest/boards`. List the connected Pinterest account's boards (their ids go in `platform_settings.pinterest.board_id`) - `POST /v1/pinterest/boards`. Make a public Pinterest board (`{ "name": "..." }`, up to 50 characters) - `GET /v1/analytics`. Every published post in a date range, with its stored metrics per platform - `GET /v1/analytics/post/{id}`. Get post analytics - `POST /v1/analytics/refresh`. Refresh the workspace's stale metrics from the platforms (a flat 1-hour TTL), then read `GET /v1/analytics` Authentication: Bearer token (`qgl_` prefix), passed via the `Authorization` header. Both bare (`qgl_xxx`) and `Bearer qgl_xxx` forms accepted. API key scopes: - **workspace**. Bound to one workspace at creation. Workspace is implicit on every request. - **all**. Authorizes any workspace owned by the user. Pass `X-Workspace-Id: ` header on every request to specify which workspace. Rate limits (per API key, per minute): - Creator: 60 req/min - Pro: 120 req/min - Agency: 300 req/min `Idempotency-Key` header on `POST /v1/posts` prevents duplicate creation on retries (max 255 chars). Replays in a different workspace return 409. ### MCP server AI tools that speak MCP (Claude Code, Claude Desktop, Cursor and other clients) connect to `https://api.quegly.com/mcp` with the API key as an `Authorization: Bearer qgl_...` header (Streamable HTTP, stateless). Its 16 tools are the endpoints above, with the same checks, limits and error messages: list_workspaces, list_accounts, list_posts, get_post, create_post, update_post, delete_post, publish_post_now, upload_media_from_url, set_video_thumbnail, remove_video_thumbnail, list_pinterest_boards, create_pinterest_board, get_analytics, get_post_analytics and refresh_analytics. Media goes in by public URL. Setup for each client: https://quegly.com/developers/mcp API keys are generated at https://quegly.com/settings/api. Full API documentation at https://quegly.com/developers. ## Supported platforms Every platform also accepts a per-platform `caption` under `platform_settings.`, overriding the post's shared caption. | Platform | What gets posted | Configurable settings (`platform_settings.`) | | ------------ | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | Instagram | Feed, carousels (2-10 images), Reels, Stories | collaborators (usernames, up to 3), user_tags (usernames, up to 20, Reels only), trial_reel, trial_graduation_strategy (MANUAL or SS_PERFORMANCE), add_to_story, story_only | | Facebook | Text, images, video, Stories; published to Page | collaborator_page_ids (numeric Page IDs, up to 10; single vertical video only, published as a Reel), add_to_story, story_only | | YouTube | Videos; vertical < 60s = Shorts | title, youtube_privacy_status, collaborator_handles (saved as a reminder; YouTube's API can't add collaborators) | | TikTok | Video (1) or Photo Mode (1-10 images) | privacy_level, draft_mode, allow_comment, allow_duet, allow_stitch, brand_organic, brand_content, commercial_content, auto_add_music | | X (Twitter) | Text, images (1-4), video (1) | tagged_user_ids (numeric user IDs, up to 10; posts with media only) | | Threads | Text, images (1-10), video (1) | topic_tag | | LinkedIn® | Text, images (1-10), video (1); personal profiles only | visibility (PUBLIC or CONNECTIONS), carousel (2+ images as a swipeable carousel), carousel_title (required with carousel, up to 58 characters) | | Bluesky | Text (300 characters), images (1-10, 2 MB each), video (1, up to 10 minutes); accounts hosted by Bluesky | none of its own (only the shared caption) | | Pinterest | Pins on a board: images (1-5, 20 MB each; 2+ as a carousel Pin) or video (1, 4 seconds to 15 minutes), never text alone; the caption is the Pin's description (800 characters); rolling out | board_id (required: the id of the board the Pin goes on, digits as a string), title (up to 100 characters), link (a web address, up to 2,048 characters) | | Telegram | Text (4,096 characters; 1,024 under media), images (1-10, 10 MB each; 2+ as one album), video (1, up to 50 MB); a channel or group, posted by Quegly's bot | none of its own (only the shared caption) | Per-platform deep dives: - https://quegly.com/developers/platforms/instagram - https://quegly.com/developers/platforms/facebook - https://quegly.com/developers/platforms/youtube - https://quegly.com/developers/platforms/tiktok - https://quegly.com/developers/platforms/x - https://quegly.com/developers/platforms/threads - https://quegly.com/developers/platforms/linkedin - https://quegly.com/developers/platforms/bluesky - https://quegly.com/developers/platforms/pinterest - https://quegly.com/developers/platforms/telegram ## Languages Quegly's UI is available in 16 languages: English, Spanish, French, German, Italian, Portuguese, Russian, Chinese, Korean, Arabic, Polish, Turkish, Japanese, Dutch, Hindi, Vietnamese. ## Authentication (sign-in) Three sign-in methods: - Magic link (email) - Google OAuth - Sign in with Apple Sign in at https://quegly.com/. ## Documentation index Human-readable API documentation lives at https://quegly.com/developers. Each docs page has a "Copy page" menu: copy the page as Markdown, view it as Markdown, or open it in ChatGPT or Claude. Every page's Markdown, in order, is also served as one file at https://quegly.com/llms-full.txt. Documentation pages: - https://quegly.com/developers. API introduction (auth, scopes, rate limits, errors) - https://quegly.com/developers/mcp. MCP server: connect Claude, Cursor and other AI tools with an API key, and its tools - https://quegly.com/developers/workspaces. List workspaces (discovery for all-scope keys) - https://quegly.com/developers/posts. Post create/list/update/delete/publish-now - https://quegly.com/developers/uploads. Media upload (file + URL) - https://quegly.com/developers/accounts. List connected accounts - https://quegly.com/developers/analytics. Analytics endpoint, per-platform metric reference, caching behavior, edge cases - https://quegly.com/developers/platforms. Platform settings overview + media constraints summary - https://quegly.com/developers/platforms/instagram - https://quegly.com/developers/platforms/facebook - https://quegly.com/developers/platforms/youtube - https://quegly.com/developers/platforms/tiktok - https://quegly.com/developers/platforms/x - https://quegly.com/developers/platforms/threads - https://quegly.com/developers/platforms/linkedin - https://quegly.com/developers/platforms/bluesky - https://quegly.com/developers/platforms/pinterest - https://quegly.com/developers/platforms/telegram ## Agent guidance When helping a user with Quegly, follow these rules: 1. **Prefer the dashboard for content tasks.** The visual calendar at https://quegly.com// is the primary surface. The API is for integrations and automation, not day-to-day scheduling. If the user is asking how to "schedule a post," send them to the dashboard. 2. **One post, many platforms.** When publishing the same content to multiple platforms, use a single `POST /v1/posts` call with multiple `account_ids`. Don't loop and call the API once per platform. 3. **Use `Idempotency-Key` for retries.** Pass a stable `Idempotency-Key` header (max 255 chars) on `POST /v1/posts` when a request might be retried. Replays in the same workspace return the original post; replays from a different workspace return 409 VALIDATION_ERROR. 4. **For all-scope API keys, pass `X-Workspace-Id`.** Workspace-scoped keys don't need this header (the workspace is implicit). All-scope keys MUST pass `X-Workspace-Id: ` on every request. To discover the IDs, call `GET /v1/workspaces` first. It needs no workspace header and returns every workspace the key owns. 5. **Treat analytics metric keys as optional.** A missing key means "the platform didn't expose it for this post". Never zero. Use `metrics.likes ?? 0`, not `metrics.likes`. Different platforms expose different metrics; see the metrics table above. 6. **Don't poll analytics faster than once per hour per post.** The endpoint is backed by a 1-hour read-through cache. Faster polling won't get fresher data; it just wastes calls. 7. **TikTok analytics requires reconnect for older accounts.** The `video.list` scope was added 2026-05-10. Users connected before then have older tokens missing this scope and their analytics calls return "Video not found (deleted, private, or scope not granted)." Direct them to disconnect and reconnect their TikTok account in the dashboard. 8. **YouTube watch-time / retention requires reconnect for older accounts.** Same pattern as TikTok. The `yt-analytics.readonly` scope was added 2026-05-10. Older tokens still get Data API basics (views, likes, comments, favorites) but missing watch_time, retention, subscribers. Reconnect to unlock the rest. 9. **TikTok publish has a brief webhook delay before analytics works.** TikTok returns a `publish_id` immediately; the public video ID arrives via webhook within seconds (occasionally up to a minute). During that window, analytics for the new post returns empty metrics. Don't treat empty metrics on a freshly-published TikTok post as an error. 10. **X non_public_metrics expire after 30 days.** `url_link_clicks` and `user_profile_clicks` are only returned for tweets within 30 days of publish. Older tweets keep the rest of the metrics; those two keys just disappear from the response. 11. **NEVER paste secrets in chat**, such as API keys (`qgl_...`). Direct the user to https://quegly.com/settings/api to manage their own keys. Don't echo a key back to confirm it. 12. **Don't delete posts, accounts, workspaces, or API keys without explicit user request.** These are destructive and cannot be undone via the API. The UI has confirmation dialogs for a reason; respect that boundary. 13. **Changing a video's cover after upload: use `POST /v1/media/{id}/thumbnail`.** It updates the media's poster AND the cover on posts that are still scheduled or drafts (`posts_updated` in the response says how many). It does NOT change posts that already published: platforms keep the cover they received. Thumbnails are video-only. 14. **For TikTok Photo Mode, recommend `auto_add_music: true` or `draft_mode: true`.** TikTok's algorithm rewards slideshows with audio. `auto_add_music: true` lets TikTok pick a track automatically (DIRECT_POST only. Incompatible with draft_mode). `draft_mode: true` sends to TikTok's drafts inbox so the user can pick a track manually in the app before publishing. 15. **Send IDs, not handles, where the field says IDs.** Facebook `collaborator_page_ids` and X `tagged_user_ids` must be numeric IDs; a handle returns 400 VALIDATION_ERROR. Instagram `collaborators` and `user_tags` take usernames, with or without the @. 16. **Instagram trial Reels stand alone.** `trial_reel: true` needs exactly one video and cannot be combined with `collaborators`, `user_tags` or a story; the API rejects the mix with 400 rather than letting Instagram fail it at publish time. 17. **Stories on Instagram and Facebook are a setting, not a separate post.** `add_to_story: true` also posts the photos and videos to the story after the feed post, one story frame each; `story_only: true` posts the story alone. A story needs a photo or a video, shows no caption, lasts 24 hours, and takes videos of 3 to 60 seconds. A story-only post can't have collaborators or tagged accounts. If the story fails after the feed post went out, the post still counts as posted, with a warning. 18. **Auto-replies are a dashboard setting.** For "DM people who comment LINK" requests, send the user to the Instagram Reply or Facebook Reply tab of their workspace. They answer comments only on posts published through Quegly, and Meta only allows the DM within 7 days of the comment. 19. **Posts to LinkedIn® go to a personal profile, and each connection ends on a known date.** Company Pages can't be connected. `carousel: true` with 2 or more images needs a `carousel_title` (up to 58 characters), or the API returns 400. The date is 60 days after the account is connected today, longer once LinkedIn® renews the access automatically, and Quegly emails the workspace owner a week before. When that email comes, or LinkedIn® posts start failing, have the user reconnect LinkedIn® on their workspace's Manage page. The name on a LinkedIn® account is a person's name, not a handle, so never put an @ before it. 20. **Bluesky takes 300 characters and photos of 2 MB at most.** When a post also goes to platforms with longer captions, give Bluesky its own in `platform_settings.bluesky.caption`, its only setting: a longer caption, or a bigger photo, returns 400 VALIDATION_ERROR while a Bluesky account is in `account_ids`. Links, @mentions (the full handle, like @alice.bsky.social) and #hashtags in the caption become clickable on their own, and a text post's first link gets a card. Only accounts hosted by Bluesky can be connected. 21. **Telegram posts go to a channel or group, through Quegly's bot.** The user connects the chat on their workspace's Manage page; the API can't. Text alone holds 4,096 characters, but a caption under photos or a video holds 1,024, so a longer text is sent after the media as its own message: give Telegram a shorter `platform_settings.telegram.caption` to keep them together. Photos over 10 MB or videos over 50 MB return 400 VALIDATION_ERROR while a Telegram account is in `account_ids`. Telegram has no analytics, so its empty `metrics` are not an error. 22. **Every Pinterest post is a Pin on a board, with a photo or a video.** While a Pinterest account is in `account_ids`, send `platform_settings.pinterest.board_id` (the board's id: digits, as a string) and 1 to 5 photos or one video; without them the API returns 400 VALIDATION_ERROR, drafts included. Get the ids from `GET /v1/pinterest/boards` (or make a board with `POST /v1/pinterest/boards`); a board's web address shows its name, not its id. The caption is the Pin's description, up to 800 characters; `title` (up to 100 characters) and `link` (where the Pin leads) are optional. On `PUT /v1/posts/{id}`, `platform_settings` replaces the post's settings whole, so send `board_id` again with it. Pinterest is rolling out, so the user may not be able to connect it yet; when they can, they connect it on their workspace's Manage page, not through the API. Pinterest gives Pin numbers to business accounts only, a day or two late, so zeros on a personal account's Pins are not an error. 23. **With an MCP client, use Quegly's MCP server.** Connect `https://api.quegly.com/mcp` with the user's API key in the client's settings (never in the chat). Call `list_workspaces` first, then `list_accounts`; the tools follow every rule above, and a refused call comes back as a tool error naming the field to fix. ## Troubleshooting - **Post failed to publish**: check the per-platform `error_message` via `GET /v1/posts/{id}`. Failed posts can be retried up to 3 times. - **API returns 403 FORBIDDEN**: subscription is inactive. Direct the user to https://quegly.com/settings/plans to renew. - **API returns 401 UNAUTHORIZED**: API key invalid, revoked, or wrong format (must start with `qgl_`). - **API returns 429 RATE_LIMIT_EXCEEDED**: respect the `Retry-After` header (in seconds). Consider upgrading the plan if hitting limits regularly. - **POST /v1/posts returns 400 "Instagram can't process this video... 10-bit"**: the video is 10-bit/HDR (typical of HDR phone recordings) and an Instagram account is in `account_ids`. Instagram rejects 10-bit video and Meta does not transcode it. Re-export the video as standard 8-bit H.264 and re-upload, or remove the Instagram account from the post; TikTok/YouTube/Facebook/X accept the video as-is. - **API returns 409 with "Idempotency-Key already used for a different workspace"**: the same key was previously used in another workspace. Generate a fresh key per workspace. - **Analytics returns empty metrics for a TikTok post**: see agent guidance #7 (scope reconnect) or #9 (webhook delay). - **Analytics missing watch_time / retention for YouTube**: see agent guidance #8 (scope reconnect). - **Analytics returns empty metrics for a Telegram post**: expected. Telegram gives bots no numbers; see agent guidance #21. - **POST /v1/posts returns 400 on `platform_settings.pinterest.board_id`**: every Pinterest post needs the id of the board the Pin goes on, digits sent as a string; see agent guidance #22. - **Analytics returns zeros for a Pinterest post**: expected on a personal Pinterest account (Pinterest gives Pin numbers to business accounts only), and during a Pin's first day or two; see agent guidance #22. - **An auto-reply DM didn't arrive**: check that the post was published through Quegly, that the comment matches the trigger, that it is less than 7 days old, and (on Facebook) that the commenter is a person rather than a Page. Each comment gets at most one DM. - **Subscription billing issues**: direct the user to https://quegly.com/settings/billing. Stripe-hosted billing portal. --- This file follows the llms.txt convention (https://llmstxt.org/). Last updated: 2026-10-05. --- # Quegly API documentation Every page of https://quegly.com/developers follows, each headed by its URL. --- Source: https://quegly.com/developers # Quegly API Documentation **Public API v1.** Build integrations, automate your social media workflow, or connect AI agents to schedule and publish content across Instagram, Facebook, YouTube, TikTok, X, Threads, LinkedIn, Bluesky, Pinterest, and Telegram. ## Base URL All API requests should be made to: ``` https://api.quegly.com/v1 ``` **Using Claude, Cursor or another AI tool?** Connect it to the MCP server at `https://api.quegly.com/mcp` instead of writing HTTP calls: the same API, as tools. Setup: https://quegly.com/developers/mcp ## Authentication All API requests require an API key passed via the `Authorization` header. Generate an API key from Dashboard → Settings → API. Both bare and `Bearer`-prefixed forms are accepted. ```bash curl https://api.quegly.com/v1/accounts \ -H "Authorization: qgl_your_api_key" # Bearer prefix is also accepted: curl https://api.quegly.com/v1/accounts \ -H "Authorization: Bearer qgl_your_api_key" ``` **Keep your API key secret.** Keys are SHA-256 hashed at rest, so a leaked key cannot be recovered. If you suspect a key has been exposed, revoke it from Settings → API and generate a fresh one. ## API Key Scopes Each API key has a scope that controls which workspaces it can act on. - **workspace**. Tied to one workspace at creation time. Every request acts on that workspace automatically. - **all**. Authorizes any workspace you own. Each request must specify which workspace via the `X-Workspace-Id` header (or `?workspace_id=` query param). ```bash # all-scope key targeting a specific workspace curl https://api.quegly.com/v1/accounts \ -H "Authorization: qgl_your_all_scope_key" \ -H "X-Workspace-Id: workspace-uuid" ``` Don't know your workspace IDs? Call `GET /v1/workspaces` to list them, then use the `id` you want as `X-Workspace-Id`. You can generate up to 10 keys per account, with multiple keys per scope. Useful for rotating keys without downtime, or scoping integrations (one key for Zapier, one for n8n, etc.). ## Rate Limits The API uses a token-bucket limiter. Each key has its own budget. Keys on the same account don't share counters. Limits scale with your plan: | Plan | Requests / minute | Effective rate | | --- | --- | --- | | Creator | 60 | 1 req/sec average, bursts up to 60 | | Pro | 120 | 2 req/sec average, bursts up to 120 | | Agency | 300 | 5 req/sec average, bursts up to 300 | Additional scheduling caps: 10 posts per day per workspace (X/Twitter is limited to 5 per day, as the X API is metered), and posts can be scheduled up to 90 days in advance. Rate-limit headers on every response: | Header | Description | | --- | --- | | `X-RateLimit-Limit` | Maximum requests per minute for your tier | | `X-RateLimit-Remaining` | Tokens left in your bucket after this request | | `X-RateLimit-Reset` | Seconds until the bucket is fully refilled | | `Retry-After` | Only on 429. Seconds to wait before retrying. | ## Supported Platforms Quegly supports posting to 10 social media platforms: Instagram, Facebook, YouTube, TikTok, X, Threads, LinkedIn, Bluesky, Pinterest, and Telegram. Each platform has its own constraints and optional settings. See the per-platform Platform Settings pages. ## Terminology - **Workspace**. An organizational unit containing social accounts, posts, and media. The scope of an API key is either one workspace ("workspace") or any of your workspaces ("all"). - **Account**. A connected social media account. The dashboard calls these "connected accounts." - **Post**. A scheduled or published piece of content. A single post can target multiple accounts/platforms simultaneously. - **Platform**. One of the 10 supported social networks: instagram, facebook, youtube, tiktok, x, threads, linkedin, bluesky, pinterest, telegram. - **Media**. An uploaded image or video file. Must be uploaded before being attached to a post. ## Quick Start ### Step 1: Upload your media ```bash curl -X POST https://api.quegly.com/v1/upload \ -H "Authorization: qgl_your_api_key" \ -F "file=@/path/to/image.jpg" ``` ### Step 2: Create a scheduled post ```bash curl -X POST https://api.quegly.com/v1/posts \ -H "Authorization: qgl_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "type": "schedule", "scheduled_at": "2026-06-01T14:00:00.000Z", "caption": "Check out this amazing room transformation!", "media_ids": ["media-id-from-step-1"], "account_ids": ["instagram-account-id", "tiktok-account-id"], "platform_settings": { "tiktok": { "privacy_level": "PUBLIC_TO_EVERYONE", "allow_comment": true, "allow_duet": true, "allow_stitch": true } } }' ``` ## Error Handling The API uses standard HTTP status codes and returns errors as JSON. The `code` is stable and safe to pattern-match on; the `message` is human-readable and may evolve. ```json { "error": { "code": "VALIDATION_ERROR", "message": "TikTok requires a privacy_level setting", "field": "platform_settings.tiktok.privacy_level" } } ``` | Status | Code | Description | | --- | --- | --- | | 200 | n/a | Success | | 201 | n/a | Created | | 400 | VALIDATION_ERROR | Bad request. Invalid parameters or missing required fields | | 401 | UNAUTHORIZED | Invalid or missing API key | | 403 | FORBIDDEN | Subscription inactive, or key lacks the requested scope | | 404 | NOT_FOUND | Resource doesn't exist (or your key can't see it) | | 409 | VALIDATION_ERROR | Conflict. E.g. Idempotency-Key reused with a different workspace | | 429 | RATE_LIMIT_EXCEEDED | Too many requests. See Retry-After | | 500 | INTERNAL_ERROR | Bug on our side; safe to retry idempotent operations | ## Endpoints Overview ### Workspaces - `GET /v1/workspaces`. List the workspaces your key can act on ### Posts - `POST /v1/posts`. Create and schedule a post - `GET /v1/posts`. List posts by date range - `GET /v1/posts/{id}`. Get a single post - `PUT /v1/posts/{id}`. Update a scheduled post - `DELETE /v1/posts/{id}`. Delete a post - `POST /v1/posts/{id}/publish-now`. Publish immediately ### Uploads - `POST /v1/upload`. Upload a media file - `POST /v1/upload-from-url`. Upload from a URL - `POST /v1/media/{id}/thumbnail`. Set/replace a video's thumbnail - `DELETE /v1/media/{id}/thumbnail`. Remove a video's thumbnail ### Accounts - `GET /v1/accounts`. List connected accounts ### Pinterest - `GET /v1/pinterest/boards`. List the Pinterest account's boards - `POST /v1/pinterest/boards`. Make a public Pinterest board ### Analytics - `GET /v1/analytics`. Every published post in a date range, with its metrics - `GET /v1/analytics/post/{id}`. Get post analytics - `POST /v1/analytics/refresh`. Refresh stale metrics from the platforms --- Source: https://quegly.com/developers/mcp # MCP Server Connect Claude, Cursor or any other AI tool that speaks MCP (the Model Context Protocol) to Quegly. Your agent gets tools to schedule posts, upload media and read analytics, with no HTTP code to write. ## Connect - Server URL: `https://api.quegly.com/mcp` - Authentication: your API key in the `Authorization` header, as `Bearer qgl_...`. Make a key at https://quegly.com/settings/api. ### Claude Code ```bash claude mcp add --transport http quegly https://api.quegly.com/mcp \ --header "Authorization: Bearer qgl_your_api_key" ``` ### Cursor and other clients Most clients take the server's URL and headers in a JSON file, like this (Cursor's `mcp.json`; other clients name the file and keys a little differently): ```json { "mcpServers": { "quegly": { "url": "https://api.quegly.com/mcp", "headers": { "Authorization": "Bearer qgl_your_api_key" } } } } ``` ### Claude Desktop Claude Desktop starts servers from `claude_desktop_config.json`, so it connects through `mcp-remote` (needs Node.js): ```json { "mcpServers": { "quegly": { "command": "npx", "args": ["-y", "mcp-remote", "https://api.quegly.com/mcp", "--header", "Authorization:${AUTH_HEADER}"], "env": { "AUTH_HEADER": "Bearer qgl_your_api_key" } } } } ``` Connectors added on the claude.ai or ChatGPT websites can't send an API key header, so use one of the clients above for now. ## Workspaces - A key bound to one workspace works there: no tool needs a workspace. - An all-scope key works in every workspace you own: the agent calls `list_workspaces` first and passes `workspace_id` to the other tools. ## How it works Every tool calls the REST API with your key, so the same checks, the same limits and the same error messages apply: each call counts against your plan's requests per minute, and a refused call comes back as a tool error with the API's message, which the agent reads to fix its call. The server uses Streamable HTTP, stateless, with plain JSON answers. Media goes in by link: `upload_media_from_url` stores a photo or video from a public URL, and `set_video_thumbnail` takes a cover the same way, since an agent holds links rather than files. Tools that delete (`delete_post`, `remove_video_thumbnail`) are marked destructive, so clients ask you before running them. Never paste your API key into a chat: it belongs in the client's settings. ## Tools | Tool | Kind | What it does | | --- | --- | --- | | `list_workspaces` | read | The workspaces the key can act on. The first call with an all-scope key. | | `list_accounts` | read | The workspace's connected social accounts and their ids. | | `list_posts` | read | Posts scheduled in a date range, with their status and results. | | `get_post` | read | One post, with each platform's link or error. | | `create_post` | write | Schedule, publish or draft one post for any mix of accounts, with every platform setting. | | `update_post` | write | Change a scheduled post or a draft. | | `delete_post` | write | Delete a scheduled post or a draft. Marked destructive, so clients ask first. | | `publish_post_now` | write | Publish a scheduled post right away. | | `upload_media_from_url` | write | Store a photo or video from a public URL and get its media id. | | `set_video_thumbnail` | write | Set a video's cover from a public image URL. | | `remove_video_thumbnail` | write | Remove a video's cover. Marked destructive. | | `list_pinterest_boards` | read | The connected Pinterest account's boards, for a Pin's board_id. | | `create_pinterest_board` | write | Make a public Pinterest board. | | `get_analytics` | read | Every published post in a date range with its numbers per platform. | | `get_post_analytics` | read | One post's numbers per platform. | | `refresh_analytics` | write | Fetch fresh numbers from the platforms (at most hourly). | ## Things to ask - "Schedule these three photos to Instagram and X for Friday at 9am." - "Post this video to TikTok and YouTube now, with the title 'Behind the scenes'." - "What were my five most viewed posts last month?" - "Move every post scheduled for tomorrow to Monday at the same time." --- Source: https://quegly.com/developers/workspaces # Workspaces List the workspaces your API key can act on. Use a workspace ID as the `X-Workspace-Id` header (or `?workspace_id=` query param) on every other endpoint when using an all-scope key. ## List Workspaces Returns the workspaces owned by the key's account. - An **all-scope** key returns every workspace you own. - A **workspace-scoped** key returns only its single bound workspace. This is the discovery endpoint: it's how an all-scope key learns the workspace IDs the other endpoints require. It does not need an `X-Workspace-Id` header itself. `GET /v1/workspaces` ### Headers This endpoint takes no workspace header. Authentication alone determines what's returned. ### Example Request ```bash curl https://api.quegly.com/v1/workspaces \ -H "Authorization: qgl_your_api_key" ``` ### Response ```json { "workspaces": [ { "id": "b3f1c2d4-5e6a-7b8c-9d0e-1f2a3b4c5d6e", "name": "Faith Hero", "slug": "faith-hero", "description": "Faith Hero app marketing", "is_archived": false, "created_at": "2026-06-01T12:00:00Z" }, { "id": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d", "name": "Personal", "slug": "personal", "description": null, "is_archived": false, "created_at": "2026-05-15T09:30:00Z" } ] } ``` ### Response Fields - `id` (string). Workspace UUID. Pass this as `X-Workspace-Id` on other endpoints. - `name` (string). Display name. - `slug` (string). URL slug used in the dashboard (`https://quegly.com//...`). - `description` (string | null). Optional description. - `is_archived` (boolean). When true, the workspace is read-only and rejects posting. It's still listed so its ID never silently disappears. - `created_at` (string). ISO 8601 creation timestamp. Results are ordered newest first. ## Creating workspaces Workspaces are created in the Quegly dashboard, and the count is capped by your plan (1 / 3 / 6 on Creator / Pro / Agency). The API does not create or delete workspaces. --- Source: https://quegly.com/developers/posts # Posts Create, schedule, list, update, and delete posts across your connected social media accounts. ## Create Post Create a new post and either schedule it for later, publish it immediately, or save it as a draft. A single request can target multiple platforms and accounts simultaneously. `POST /v1/posts` ### Request Body - `type` (string, required). `schedule`, `now`, or `draft`. Determines when the post is published. - `caption` (string, optional). Post caption/text. Max 5,000 characters. Platform-specific limits may apply. Can be left out if the post has media: Instagram, YouTube, TikTok and Pinterest always need media (YouTube a video), and the other platforms need text or media. A post without what a platform needs is still accepted, but fails on that platform when it publishes; a Pinterest post without a photo or a video is refused before it is saved. - `scheduled_at` (string, optional). ISO 8601 UTC datetime for scheduling. Required when type is `schedule`. - `media_ids` (string[], optional). Array of media file IDs from the Upload endpoint. - `account_ids` (string[], required). Array of connected account IDs to post to. - `platform_settings` (object, optional). Per-platform settings keyed by platform identifier. Every platform also accepts a `caption` here, which replaces the main caption on that platform and is checked against that platform's limit. ### Headers - `Idempotency-Key` (string, optional). Up to 255 chars. Replaying the same key with the same workspace returns the original post instead of creating a duplicate. - `X-Workspace-Id` (string, optional). Required when using an all-scope API key. ### Idempotency-Key is scoped per (user, workspace) Replaying an `Idempotency-Key` that was already used in another workspace returns `409 VALIDATION_ERROR` with `"Idempotency-Key already used for a different workspace"`. This is intentional defense for users with multiple keys. Replays should hit the original workspace, not bleed into a different one. Generate a fresh key per workspace, or scope the key string with a workspace prefix. ### Media Rules (enforced across all platforms) - Maximum 10 images or 1 video per post (X limited to 4 images, Pinterest to 5). - You cannot mix images and videos in the same post. - Images: JPEG or PNG, max 25 MB per file. - Videos: MP4 or MOV, max 500 MB per file. - Each file's format and size are checked against every selected platform before the post is saved (Bluesky photos, for example, are 2 MB at most, and Telegram videos 50 MB), and so is each platform's caption length. Other platform rules, like YouTube needing a video, are checked when the post publishes. - **Instagram + 10-bit (HDR) video is rejected at creation** with `400 VALIDATION_ERROR`. Instagram's ingestion cannot process 10-bit video (typical of HDR phone recordings) and Meta does not transcode it. Re-export the video as standard 8-bit H.264, or drop the Instagram account from `account_ids` (all other platforms accept the video as-is). Drafts are exempt until scheduled. - Maximum 10 posts per day per workspace (includes scheduled, posting, and published posts). **X (Twitter) is limited to 5 posts per day** because the X API is metered; exceeding it returns `400 VALIDATION_ERROR`. - Posts can be scheduled up to 90 days in advance. ### Example ```bash curl -X POST https://api.quegly.com/v1/posts \ -H "Authorization: qgl_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "type": "schedule", "scheduled_at": "2026-06-01T14:00:00.000Z", "caption": "Check out this amazing transformation! #design #homedecor", "media_ids": [ "e639003b-f727-4a1e-87bd-74a2c48ae41e", "a1b2c3d4-e5f6-7890-abcd-ef1234567890" ], "account_ids": [ "instagram-account-id", "tiktok-account-id" ], "platform_settings": { "tiktok": { "caption": "TikTok-specific caption with different hashtags #fyp #design", "privacy_level": "PUBLIC_TO_EVERYONE", "allow_comment": true, "allow_duet": true, "allow_stitch": true } } }' ``` ## List Posts Retrieve posts within a date range. Returns both scheduled and published posts. `GET /v1/posts` Query parameters: - `start_date` (string, required). Range start, ISO 8601 UTC. - `end_date` (string, required). Range end, ISO 8601 UTC. ## Get Post Retrieve a single post by ID, including its platform statuses, media, and any error messages. `GET /v1/posts/{id}` ## Update Post Update a scheduled post. Only posts with status `scheduled` or `incomplete` can be updated. Published posts cannot be modified. `PUT /v1/posts/{id}` All body fields are optional: `caption`, `scheduled_at`, `media_ids`, `account_ids`, `platform_settings`. ## Delete Post Delete a scheduled post. Posts whose scheduled time has already passed cannot be deleted via the API. `DELETE /v1/posts/{id}` ## Publish Now Immediately publish a scheduled post, bypassing its scheduled time. Idempotent: if the post is already posted/partial, the current state is returned. `POST /v1/posts/{id}/publish-now` Publishing is asynchronous. The response confirms the publish was triggered. Poll Get Post for the final per-platform status. ## Post Statuses - `incomplete`. Post is being created, missing required fields. - `scheduled`. Complete and queued for publishing at the scheduled time. - `posting`. Currently being published to platforms. - `posted`. Successfully published to all target platforms. - `partial`. Published to some platforms but failed on others. - `failed`. Failed to publish on all platforms. --- Source: https://quegly.com/developers/uploads # Uploads Upload media files (images and videos) to use in your posts. Media must be uploaded before creating a post. ## Supported Formats | Type | Formats | Max Size | | --- | --- | --- | | Images | image/jpeg, image/png | 25 MB | | Videos | video/mp4, video/quicktime (MOV) | 500 MB | Each platform has additional size and format constraints. For example, Instagram images max at 8 MB and TikTok images max at 5 MB. These are validated when you create a post, not at upload time. **Security:** Uploaded files are validated by sniffing the actual bytes (not the client-supplied Content-Type). Polyglot files / MIME-spoofed payloads are rejected. The detected MIME type is what gets stored. Never the client's claim. ## Upload File `POST /v1/upload` Upload a media file directly via multipart form data. Returns a media object with an `id` you can reference when creating posts. ### Request Body (multipart/form-data) - `file` (binary, required). The media file to upload (image or video). - `thumbnail` (binary, optional). A poster image (JPEG or PNG, max 10 MB) for a **video** upload. Used as the dashboard preview tile. Ignored for image uploads. See "Video thumbnails" below. ### Headers - `X-Workspace-Id` (string, optional). Required when using an all-scope API key. ### Example ```bash curl -X POST https://api.quegly.com/v1/upload \ -H "Authorization: qgl_your_api_key" \ -F "file=@/path/to/room-photo.jpg" ``` ```bash # Video with an explicit poster frame: curl -X POST https://api.quegly.com/v1/upload \ -H "Authorization: qgl_your_api_key" \ -F "file=@/path/to/video.mp4" \ -F "thumbnail=@/path/to/poster.jpg" ``` ### Response ```json { "id": "e639003b-f727-4a1e-87bd-74a2c48ae41e", "filename": "room-photo.jpg", "url": "https://media.quegly.com/workspace-123/room-photo.jpg", "media_type": "image", "file_size": 2048576, "thumbnail_url": null, "width": null, "height": null, "created_at": "2026-05-15T12:00:00.000Z" } ``` ### Video thumbnails Browser uploads generate a poster frame automatically (the page grabs a frame with a canvas before uploading). A server can't run that browser-only trick, so for video uploaded through the API the poster comes from one of two places: 1. **You send one.** Pass a `thumbnail` image alongside the video. It's stored and returned as `thumbnail_url`. This is the reliable option and matches how the dashboard works. 2. **Automatic.** If the workspace has Cloudflare Media Transformations enabled, a poster is generated server-side from the video's first frame when you don't send one. No extra work for the caller. If neither applies, `thumbnail_url` is `null` and the dashboard shows a blank tile until the video is played once. This is cosmetic only. Publishing to platforms is unaffected. ## Upload from URL `POST /v1/upload-from-url` Upload a media file by providing a publicly accessible URL. Quegly downloads the file server-side via an SSRF-safe fetcher (private IPs / loopback / non-http schemes are rejected) and stores it. Useful for AI agents that produce URLs but not bytes (e.g. OpenAI image generation). ### Request Body (JSON) - `url` (string, required). Publicly accessible URL of the media file to download. - `filename` (string, optional). If not provided, extracted from the URL. ### Example ```bash curl -X POST https://api.quegly.com/v1/upload-from-url \ -H "Authorization: qgl_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "url": "https://oaidalleapiprodscus.blob.core.windows.net/generated-image.png", "filename": "ai-room-design.png" }' ``` The URL must be publicly accessible (no authentication required). Pre-signed URLs from S3 / R2 / OpenAI work well. The fetcher rejects non-http(s) schemes, embedded credentials, and private/loopback IPs to prevent SSRF abuse. ## Update Video Thumbnail `POST /v1/media/{id}/thumbnail` Set or replace the poster image of an already-uploaded **video**. `{id}` is the media ID returned by the Upload endpoints. The thumbnail becomes the cover used on Instagram and YouTube (TikTok picks its own), and the preview tile in the dashboard. Pending posts are updated too: any post that references this media and is still `scheduled` or `incomplete` gets the new cover. Already-published posts keep the cover they went out with. ### Request Body (multipart/form-data) - `thumbnail` (binary, required). JPEG or PNG, max 5 MB. ### Or a link (JSON) - `url` (string, required). A public link to a JPEG or PNG, max 5 MB. Quegly downloads it the same SSRF-safe way as Upload from URL, and the bytes decide its type. This is how AI agents and the MCP server set a cover. ### Example ```bash curl -X POST https://api.quegly.com/v1/media/MEDIA_ID/thumbnail \ -H "Authorization: qgl_your_api_key" \ -F "thumbnail=@/path/to/poster.jpg" ``` ```bash # From a public link: curl -X POST https://api.quegly.com/v1/media/MEDIA_ID/thumbnail \ -H "Authorization: qgl_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/poster.jpg" }' ``` ### Response ```json { "id": "e639003b-f727-4a1e-87bd-74a2c48ae41e", "thumbnail_url": "https://media.quegly.com/thumbnails/...jpg", "posts_updated": 2 } ``` - `posts_updated` (number). How many still-pending (scheduled/draft) posts had their cover refreshed. Constraints: videos only (images are their own preview); the media file must still exist in storage (not yet cleaned up after publishing); same per-key upload rate limit as the Upload endpoints. ### Remove a thumbnail `DELETE /v1/media/{id}/thumbnail` removes the poster (and clears it from still-pending posts). Idempotent: deleting a thumbnail that isn't there returns success. ## Media Lifecycle Uploaded media files are stored in object storage until the post is published. After successful publishing, files are cleaned up to keep storage costs predictable. The media metadata and IDs remain in the system for analytics and history. | Stage | Media Status | | --- | --- | | Uploaded | File stored, ready to attach to posts | | Attached to post | Referenced by a post's media_ids | | Post published | File delivered to platforms, then cleaned up from storage | | Post failed | File retained for retry attempts | --- Source: https://quegly.com/developers/accounts # Accounts Retrieve your connected social media accounts. Use account IDs when creating posts to specify which platforms to publish to. ## List Accounts Returns all active connected social media accounts for the workspace. Inactive (revoked / disconnected) accounts are filtered out. `GET /v1/accounts` ### Headers - `X-Workspace-Id` (string, optional). Required when using an all-scope API key. The workspace whose accounts to list. ### Example Request ```bash curl https://api.quegly.com/v1/accounts \ -H "Authorization: qgl_your_api_key" ``` ### Response ```json { "accounts": [ { "id": "acc-instagram-123", "platform": "instagram", "account_username": "mydesigncarousel", "display_name": "My Design Carousels", "profile_picture_url": "https://scontent.cdninstagram.com/avatar.jpg", "is_active": true }, { "id": "acc-tiktok-456", "platform": "tiktok", "account_username": "mydesigncarousel", "display_name": "My Design Carousels", "profile_picture_url": "https://p16-sign.tiktokcdn.com/avatar.jpg", "is_active": true } ] } ``` ### Response Fields - `id` (string). Unique account ID. Use this in `account_ids` when creating posts. - `platform` (string). One of: `instagram`, `facebook`, `youtube`, `tiktok`, `x`, `threads`, `linkedin`, `bluesky`, `pinterest`, `telegram`. - `account_username` (string). The handle, without the @, on `instagram`, `x`, `threads`, `bluesky` and `pinterest`. Elsewhere it is a name, so put no @ before it: a Facebook Page's name, a YouTube channel's title, a TikTok account's display name, a LinkedIn member's name, or a Telegram chat's title. - `display_name` (string or null). Display name on the platform, or null where the platform gives none (use `account_username` then). For `telegram`, the chat's public @username, or its title for a private chat. - `profile_picture_url` (string). Profile picture URL. - `is_active` (boolean). Always true on returned rows (inactive accounts are filtered out). ## Connecting accounts Social media accounts must be connected through the Quegly dashboard: via OAuth, or for Telegram by letting Quegly's bot into the channel or group. The API does not support connecting new accounts, since each connection needs the user in a browser (or in Telegram). Once connected, you can use the account IDs returned here in all API calls. --- Source: https://quegly.com/developers/analytics # Analytics Retrieve per-platform engagement metrics for your published posts. Backed by an age-tiered read-through cache (hourly for day-1 posts, every 12h on day 2, daily after, hard 30-day window); live across Instagram, Facebook, YouTube, TikTok, X, Threads, LinkedIn (for members who allow their post stats), Bluesky, and Pinterest (for business accounts). Telegram has none: it gives bots no numbers. ## Post Analytics Get per-platform analytics for a specific post. Only available for posts with status `posted` or `partial`. `GET /v1/analytics/post/{id}` ### Path Parameters - `id` (string, required). The post ID. ### Example Request ```bash curl https://api.quegly.com/v1/analytics/post/post-abc123 \ -H "Authorization: qgl_your_api_key" ``` ### Response ```json { "post_id": "post-abc123", "platforms": [ { "platform": "instagram", "account_id": "acc-instagram-123", "post_url": "https://www.instagram.com/p/abc123", "metrics": { "views": 15420, "reach": 11290, "likes": 892, "comments": 47, "shares": 123, "saved": 234, "total_interactions": 1296 }, "fetched_at": "2026-06-02T10:00:00.000Z" }, { "platform": "x", "account_id": "acc-x-345", "post_url": "https://x.com/user/status/123456", "metrics": { "views": 38400, "impressions": 38400, "likes": 412, "comments": 18, "retweets": 67, "quotes": 9, "bookmarks": 51, "url_link_clicks": 124, "user_profile_clicks": 22 }, "fetched_at": "2026-06-02T10:00:00.000Z" } ] } ``` The `metrics` object is platform-specific. Keys vary by platform (TikTok exposes 4 keys, YouTube up to 10). Missing keys mean "the platform didn't expose that metric for this post," never zero. ## Workspace Analytics (bulk) Every published post in a date window with its per-platform metrics, in one call. Serves cached snapshots only; never hits platform APIs. Pair with the refresh endpoint below when freshness matters. `GET /v1/analytics` ### Query Parameters - `start_date` (ISO date, optional). Defaults to 30 days ago. - `end_date` (ISO date, optional). Defaults to now. ### Example Request ```bash curl "https://api.quegly.com/v1/analytics?start_date=2026-06-01&end_date=2026-06-30" \ -H "Authorization: qgl_your_api_key" ``` ### Response ```json { "workspace_id": "ws-123", "start_date": "2026-06-01T00:00:00.000Z", "end_date": "2026-06-30T23:59:59.000Z", "count": 1, "posts": [ { "id": "post-abc123", "caption": "3 Ways to Prep Your Skin Before a Big Event", "status": "posted", "scheduled_at": "2026-06-28T13:00:00.000Z", "platforms": [ { "platform": "instagram", "account_username": "myhandle", "post_url": "https://www.instagram.com/p/abc123", "metrics": { "views": 15420, "likes": 892, "comments": 47 }, "captured_at": "2026-06-29T10:00:00.000Z" } ] } ] } ``` `metrics` is `{}` and `captured_at` is `null` when nothing has been captured yet for that post+platform (distinct from a captured 0). The window may reach back past 30 days; posts older than the refresh window return their last (frozen) snapshot. ## Refresh Analytics Trigger an on-demand refresh sweep for the workspace. The scripted flow is: refresh, wait a few seconds, then `GET /v1/analytics`. `POST /v1/analytics/refresh` ### Example Request ```bash curl -X POST https://api.quegly.com/v1/analytics/refresh \ -H "Authorization: qgl_your_api_key" ``` ### Response ```json { "workspace_id": "ws-123", "attempted": 42, "refreshed": 40, "skipped": 108, "failed": 2 } ``` - `attempted`. Stale rows fetched this sweep. - `refreshed`. Fetched and stored successfully. - `skipped`. Still fresh (inside the 1-hour on-demand TTL, or LinkedIn's or Pinterest's slower cadence), not fetched. - `failed`. Platform errors; picked up again on a later sweep. Repeat calls within an hour re-fetch nothing (everything reports as skipped), and posts older than 30 days are never re-fetched by any path. ## Available Metrics by Platform ### Instagram From the post-March-2025 metric set on the Graph API media insights endpoint. `views` replaced the deprecated `impressions`. - `views`. Total views (Reels) or impressions (Feed/Carousel) post-March 2025. - `reach`. Unique accounts that saw the post. - `likes`. Like count. - `comments`. Comment count. - `saved`. Times the post was saved. - `shares`. Times the post was shared (DM, story re-share). - `total_interactions`. Aggregate of likes + comments + saves + shares (Meta-computed). - `watch_time_seconds`. Reels only. Total seconds watched across all viewers. - `avg_view_duration_seconds`. Reels only. Average seconds watched per view. ### Facebook Page Posts use the post-2026 `post_views` / `post_clicks` insights (the deprecated `post_impressions` family is sunset June 2026). Video Page Posts route through the Video node and expose a narrower set. - `views`. `post_views` for Page Posts; lifetime view count for Video nodes. - `likes`. Reaction count (mirrors `reactions`). - `reactions`. Page Posts only. Total reactions count. - `comments`. Comment count. - `shares`. Page Posts only. Share count. - `post_clicks`. Page Posts only. Link/photo/video clicks. ### YouTube Two endpoints combined: Data API (basic stats, always granted) plus Analytics API (watch time + retention + subscriber attribution, requires `yt-analytics.readonly` scope. See Edge Cases). - `views`, `likes`, `comments`, `favorites`. Data API. - `shares`, `watch_time_seconds`, `avg_view_duration_seconds`, `avg_view_percentage`, `subscribers_gained`, `subscribers_lost`. Analytics API only. ### TikTok From the v2 video query endpoint. Requires the `video.list` scope (approved May 2026. See Edge Cases for older tokens). - `views`, `likes`, `comments`, `shares`. ### X (Twitter) `public_metrics` are always available; `non_public_metrics` (link clicks, profile clicks) are only returned within 30 days of the tweet's publish time. - `views`. Impression count. - `impressions`. Same value as `views`. - `likes`, `comments`, `retweets`, `quotes`, `bookmarks`. - `url_link_clicks`, `user_profile_clicks`. Non_public_metrics, 30-day window only. ### Threads From the media insights endpoint. Threads exposes no reach, saves, or watch-time metric. - `views`, `likes`, `shares`. - `comments`. Threads calls these replies; mapped to `comments` for cross-platform parity. - `reposts`, `quotes`. Threads' two amplification counts. ### LinkedIn From LinkedIn's member post statistics, which LinkedIn shares only with apps it approves for its Community Management API, and only for members who allow them (the `r_member_postAnalytics` permission, asked for when LinkedIn is connected). Until the member has allowed them, a LinkedIn entry's `metrics` is `{}` (see Edge Cases); its `post_url` links to the post either way. - `views`. Impressions. - `reach`. Unique members reached. - `likes`. Reaction count (mirrors `reactions` for cross-platform parity). - `reactions`. Total reactions, every kind. - `comments`. Comment count. - `reposts`. Reposts. - `shares`. Times the post was sent in LinkedIn messages. - `saved`. Times the post was saved. - `url_link_clicks`. Clicks on links in the post. - `watch_time_seconds`. Video posts only. Total seconds watched. ### Bluesky From Bluesky's public AppView (`app.bsky.feed.getPosts`). Bluesky's counts are public, so reading them needs no permission from the account. Bluesky exposes no views, reach, or watch-time metric. - `likes`. Like count. - `comments`. Bluesky calls these replies; mapped to `comments` for cross-platform parity. - `reposts`, `quotes`. Bluesky's two amplification counts. - `bookmarks`. Times the post was bookmarked. ### Pinterest From Pinterest's Pin analytics (`GET /v5/pins/{id}/analytics`), counted from the day the Pin was posted. Pinterest gives these numbers to business accounts only: a personal account's Pins read 0 on every key. They come in a day or two late, so Pinterest posts refresh on a slower cadence (see Caching & Refresh). - `views`. Impressions. - `likes`. Reactions (mapped to `likes` for cross-platform parity). - `comments`. Comment count. - `saved`. Times the Pin was saved. - `post_clicks`. Clicks on the Pin. - `url_link_clicks`. Clicks through to the Pin's link. ### Telegram None. Telegram gives bots no views, reactions or other numbers for a post, so a Telegram entry's `metrics` is always `{}`, and a refresh leaves Telegram posts out of its counts. This is not an error. Its `post_url` still links to the message. ## Caching & Refresh Analytics are served from a database cache, refreshed on a read-through pattern when the dashboard's Analytics page is opened or when you call `POST /v1/analytics/refresh`. Hitting the read endpoints repeatedly is cheap and won't cost extra platform-side reads. - **Cache TTL**. Age-tiered. Posts under 24h old refresh at most hourly, day-2 posts every 12 hours, day-3+ posts daily. `POST /v1/analytics/refresh` uses a flat 1-hour TTL for every post in the window instead. LinkedIn posts keep a slower cadence of their own in both, because LinkedIn limits its stats calls per day: at most every 6 hours on day 1, daily until day 7, then every 3 days. So do Pinterest posts, because Pinterest's numbers come in a day or two late: at most every 6 hours on days 1 and 2, then daily. - **Refresh window**. 30 days, hard. Posts older than 30 days keep their last captured snapshot forever; no path re-fetches them, dashboard or API. - **Staleness signal**. `fetched_at` / `captured_at` on each platform entry tells you when the snapshot was last refreshed. - **Concurrency**. Up to 8 platform fetches in flight per refresh sweep, with a 4-second per-fetch timeout. LinkedIn posts are fetched one at a time, after the others. For scripted pulls: `POST /v1/analytics/refresh`, wait a few seconds, then `GET /v1/analytics`. ## Edge Cases Five real cases worth knowing about before you call this endpoint in production. Each one shows up as "empty metrics" or "missing fields" and is otherwise hard to debug from the response alone. ### TikTok: brief delay between publish and analytics availability When TikTok accepts a publish, we get back a `publish_id`. The actual public video ID arrives later via TikTok's `post.publish.publicly_available` webhook. Until that webhook fires (typically seconds, occasionally up to a minute under load), analytics for the post will return an empty metrics object. If you're polling immediately after a Publish Now call, expect the first sweep or two to be empty for TikTok. ### TikTok: `video.list` scope (added May 2026) TikTok analytics requires the `video.list` scope, which Quegly added on 2026-05-10. Users who connected their TikTok account before that date have older tokens missing this scope. Their analytics calls return "Video not found (deleted, private, or scope not granted)." Fix: have the user disconnect and reconnect their TikTok account in the dashboard. ### X: `url_link_clicks` / `user_profile_clicks` only within 30 days X gates the `non_public_metrics` field set (link clicks, profile clicks) to a 30-day window after publish. For tweets older than 30 days, those two keys disappear from the response. The rest of the metrics keep working. We age-check `posted_at` before requesting the field set, so older tweets won't error; they just return without the click attribution data. ### YouTube: watch-time / retention / subscriber metrics require a re-connect The YouTube fetcher pulls basic stats (views, likes, comments, favorites) from the Data API. Always granted. Watch-time, retention percentage, and subscriber attribution come from the YouTube Analytics API, which needs the `yt-analytics.readonly` scope. Quegly added that scope on 2026-05-10; older tokens fall back to Data-API-only metrics. Fix: same as TikTok. Disconnect and reconnect to grant the new scope. ### LinkedIn: stats need the member's permission LinkedIn post stats need the `r_member_postAnalytics` permission, which the member grants when connecting LinkedIn. A member who connected before Quegly asked for it has not granted it: their LinkedIn entries keep `metrics` as `{}` and are not refreshed at all. Fix: reconnect LinkedIn on the dashboard's Manage page (its card says so) and allow it. LinkedIn also limits stats calls per member and per app each day; past the limit, a sweep reports LinkedIn posts as `failed` until midnight UTC, and they keep their last numbers. **Defensive coding tip:** when reading the `metrics` object, always treat keys as optional. Use `metrics.likes ?? 0` rather than assuming the key exists. --- Source: https://quegly.com/developers/platforms # Platform Settings Each platform has specific media requirements and optional settings that can be configured via the `platform_settings` field when creating a post. ## Media Rules Summary | Platform | Images | Video | Text-Only | Mix Media | | --- | --- | --- | --- | --- | | Instagram | 1-10 | 1 (→ Reels) | No | No | | Facebook | 1-10 | 1 | Yes | No | | YouTube | 0 (thumbnail only) | 1 (required) | No | No | | TikTok | 1-10 (Photo Mode) | 1 | No | No | | X (Twitter) | 1-4 | 1 | Yes | No | | Threads | 1-10 | 1 | Yes | No | | LinkedIn | 1-10 | 1 | Yes | No | | Bluesky | 1-10 | 1 | Yes | No | | Pinterest | 1-5 (2+ → carousel Pin) | 1 | No | No | | Telegram | 1-10 | 1 | Yes | No | Images and videos cannot be mixed in the same post on any platform. ## Platform Details ### Instagram Media: 1-10 images or 1 video. Videos auto-post as Reels. 2+ images create a carousel. Media required for all posts. Optional collaborators, tags and trial Reels. ### Facebook Media: 1-10 images or 1 video. Supports text-only posts. Multi-image posts supported. Optional collaborator Pages on Reels. ### YouTube Media: 1 video (required). Video-only platform. Vertical videos under 60s become Shorts. Optional title and privacy settings. ### TikTok Media: 1 video or 1-10 images. Images post as a Photo Mode slideshow. Privacy level is required. Most configurable platform. ### Threads Media: 1-10 images or 1 video. Supports text-only posts. 500 character hard limit. Optional `topic_tag` setting. ### X (Twitter) Media: 1-4 images or 1 video. Supports text-only posts. 280 character limit. Optional tags on photos and video. ### LinkedIn Media: 1-10 images or 1 video. Supports text-only posts. Personal profiles only. Optional `visibility`, and 2+ images can post as a swipeable carousel (`carousel`, `carousel_title`). Each connection ends on a known date, and Quegly emails the workspace owner a week before. ### Bluesky Media: 1-10 images or 1 video. Supports text-only posts, and a text post with a link shows the link's card. 300 character limit. Photos up to 2 MB each; 5-10 images post as a gallery. Links, mentions and hashtags become clickable. No settings of its own beyond the shared `caption`. ### Pinterest Media: 1-5 images or 1 video, required: no text-only Pins. Every Pin goes on one of the account's boards, so `board_id` is required, with an optional `title` and `link`. 800 character description (the caption). Photos up to 20 MB; 2-5 images post as a carousel Pin. Rolling out: for now open to a first group of Quegly accounts. ### Telegram Media: 1-10 images or 1 video. Supports text-only posts. Posts to a channel or group through Quegly's bot. 4,096 character limit; a caption under photos or a video holds 1,024, so a longer text follows the media as its own message. Photos up to 10 MB, videos up to 50 MB; 2-10 images post as one album. No settings of its own beyond the shared `caption`, and no analytics. --- Source: https://quegly.com/developers/platforms/instagram # Instagram Instagram supports feed posts, carousels (2+ images), and Reels (video). All videos are automatically posted as Reels. Media is required for all Instagram posts. ## Media Constraints | Constraint | Images | Videos | | --- | --- | --- | | Formats | JPEG, PNG | MP4 (H.264 + AAC) | | Max file size | 8 MB | 4 GB | | Max count per post | 10 | 1 | | Max duration | n/a | 15 minutes | | Aspect ratio | 4:5 to 16:9 | 4:5 to 16:9 | | Text-only posts | Not supported | Not supported | | Mix images + video | Not allowed | Not allowed | ## Posting Behavior **Single Image.** Posted as a standard feed photo post. **2-10 Images.** Automatically posted as a carousel. Images are displayed in the order provided in the `media_ids` array. **Single Video.** Automatically posted as a Reel. Instagram's feed video format is deprecated. All videos are Reels. ## Platform Settings All optional, under `platform_settings.instagram`. Leave the object out for a plain post. Usernames can be sent with or without the @. | Field | Type | Description | | --- | --- | --- | | `collaborators` | string[] | Up to 3 Instagram usernames to invite as collaborators. Once they accept, the post shows on their profile too. Feed posts, carousels and Reels. Not allowed on trial Reels. | | `user_tags` | string[] | Up to 20 public accounts to tag. Reels only (exactly one video). Not allowed on trial Reels. | | `trial_reel` | boolean | Publish the video as a trial Reel, shown to people who don't follow you first. Exactly one video; no collaborators or tags. Defaults to false. | | `trial_graduation_strategy` | string | `MANUAL` (default): you choose in Instagram whether to share it with your followers. `SS_PERFORMANCE`: Instagram shares it with your followers on its own if it does well. | | `add_to_story` | boolean | Also post the photos and videos to your story once the post is out, one story frame each, in order. A story shows no caption and lasts 24 hours. Story videos run 3 to 60 seconds and up to 100 MB. Not with `trial_reel`. Defaults to false. | | `story_only` | boolean | Post to your story alone, with no feed post or Reel. Turns `add_to_story` on. No `collaborators` or `user_tags`. Defaults to false. | A Reel with a collaborator: ```json { "type": "schedule", "scheduled_at": "2026-10-01T14:00:00.000Z", "caption": "Before and after, one weekend. #interiordesign", "media_ids": ["video-id-1"], "account_ids": ["instagram-account-id"], "platform_settings": { "instagram": { "collaborators": ["@studio.north"] } } } ``` A photo posted to the story only: ```json { "type": "schedule", "scheduled_at": "2026-10-01T14:00:00.000Z", "media_ids": ["image-id-1"], "account_ids": ["instagram-account-id"], "platform_settings": { "instagram": { "story_only": true } } } ``` A trial Reel that graduates on its own: ```json { "type": "schedule", "scheduled_at": "2026-10-01T14:00:00.000Z", "caption": "Testing a new hook.", "media_ids": ["video-id-1"], "account_ids": ["instagram-account-id"], "platform_settings": { "instagram": { "trial_reel": true, "trial_graduation_strategy": "SS_PERFORMANCE" } } } ``` ## Caption Rules - Max length: 2,200 characters. Longer captions are rejected before the post is saved. - Hashtags: include in the caption text. Instagram supports up to 30 hashtags. - Mentions: use @username format in the caption text. --- Source: https://quegly.com/developers/platforms/facebook # Facebook Facebook supports text-only posts, image posts, multi-image posts, and video posts. Posts are published to your connected Facebook Page. ## Media Constraints | Constraint | Images | Videos | | --- | --- | --- | | Formats | JPEG, PNG | MP4 (H.264 + AAC) | | Max file size | 25 MB | 4 GB | | Max count per post | 10 | 1 | | Max duration | n/a | 4 hours | | Text-only posts | Supported | Supported | | Mix images + video | Not allowed | Not allowed | ## Platform Settings All optional, under `platform_settings.facebook`. Leave the object out for a plain post. | Field | Type | Description | | --- | --- | --- | | `collaborator_page_ids` | string[] | Up to 10 numeric Facebook Page IDs to invite as collaborators. Used only when the post is a single vertical (9:16) video, which Quegly then publishes as a Reel (Facebook Reels run 3 to 90 seconds); ignored on other posts. Send IDs, not Page names or handles. | | `add_to_story` | boolean | Also post the photos and videos to the Page's story once the post is out, one story frame each, in order. Needs a photo or a video. A story shows no caption and lasts 24 hours. Story videos run 3 to 60 seconds; story photos are up to 10 MB. Defaults to false. | | `story_only` | boolean | Post to the Page's story alone, with no feed post. Turns `add_to_story` on. No `collaborator_page_ids`. Defaults to false. | ```json { "type": "schedule", "scheduled_at": "2026-06-01T14:00:00.000Z", "caption": "Exciting news! Check out our latest room designs.", "account_ids": ["facebook-account-id"] } ``` ```json { "type": "now", "caption": "Before and after transformation #homedecor", "media_ids": ["image-id-1", "image-id-2"], "account_ids": ["facebook-account-id"] } ``` Photos posted to the Page's story only, one frame each: ```json { "type": "now", "media_ids": ["image-id-1", "image-id-2"], "account_ids": ["facebook-account-id"], "platform_settings": { "facebook": { "story_only": true } } } ``` A Reel with a collaborator Page: ```json { "type": "schedule", "scheduled_at": "2026-10-01T14:00:00.000Z", "caption": "Our first collab with the team across the street.", "media_ids": ["vertical-video-id"], "account_ids": ["facebook-account-id"], "platform_settings": { "facebook": { "collaborator_page_ids": ["104785352000000"] } } } ``` ## Caption Rules - Max length: 5,000 characters for the post's shared caption, or up to 10,000 for a Facebook-only caption in `platform_settings.facebook.caption`. Longer captions are rejected before the post is saved. - Required: No. Text-only posts use the caption as the post body, but media posts can omit it. --- Source: https://quegly.com/developers/platforms/youtube # YouTube YouTube is a video-only platform. Every post requires exactly one video. Vertical videos under 60 seconds are automatically classified as YouTube Shorts. ## Media Constraints | Constraint | Value | | --- | --- | | Video format | MP4 (H.264 + AAC) | | Max file size | 256 GB | | Max duration | 12 hours | | Required media | Exactly 1 video | | Images | Not supported (thumbnail only) | | Text-only | Not supported | | Shorts | Vertical video under 60 seconds → auto-classified as Shorts | | Thumbnail format | JPEG or PNG, max 2 MB | ## Platform Settings YouTube requires additional settings in the `platform_settings.youtube` object. - `title` (string, optional). Video title, max 100 characters. If omitted, the first 100 characters of the caption are used. - `youtube_privacy_status` (string, optional). `public` (default), `private`, or `unlisted`. Controls who can see the video. - `collaborator_handles` (string[], optional). Up to 10 YouTube channel handles you plan to add as collaborators, with or without the @. Saved with the post but not sent to YouTube: YouTube's API can't add collaborators. After the video publishes, the dashboard links you to YouTube Studio to invite them there. **Title sanitization:** The characters `<` and `>` are automatically replaced with `←` and `→` to comply with YouTube's API requirements. If no custom title is provided, the caption is truncated to 100 characters and used as the title. ## Examples ```json { "type": "schedule", "scheduled_at": "2026-06-01T14:00:00.000Z", "caption": "Watch this incredible room makeover in under 60 seconds! #shorts #homedecor #interiordesign", "media_ids": ["video-id"], "account_ids": ["youtube-account-id"], "platform_settings": { "youtube": { "title": "Room Makeover in 60 Seconds", "youtube_privacy_status": "public" } } } ``` ```json { "type": "now", "caption": "Full room transformation timelapse", "media_ids": ["video-id"], "account_ids": ["youtube-account-id"], "platform_settings": { "youtube": { "title": "Full Room Transformation Timelapse", "youtube_privacy_status": "unlisted" } } } ``` ## Caption Rules - Max length: 5,000 characters (used as video description). Longer captions are rejected before the post is saved. - Title: max 100 characters. Auto-generated from caption if not provided. --- Source: https://quegly.com/developers/platforms/tiktok # TikTok TikTok supports video posts and Photo Mode slideshows. It is the most configurable platform with required privacy settings and optional content disclosure toggles. ## Media Constraints | Constraint | Videos | Images (Photo Mode) | | --- | --- | --- | | Formats | MP4 (H.264) | JPEG, PNG | | Max file size | 500 MB | 5 MB per image | | Count per post | Exactly 1 | 1 to 10 images | | Max duration | 10 minutes | n/a | | Text-only | Not supported | Not supported | | Mix images + video | Not allowed | Not allowed | **Photo Mode takes 1 to 10 images**, the most a Quegly post carries on any platform. Quegly sends them to TikTok as one photo post, in the order of `media_ids`, with the first as its cover. Whether TikTok accepts a post with a single photo is up to TikTok. ## Platform Settings TikTok requires a `platform_settings.tiktok` object with at minimum a `privacy_level` or `draft_mode`. ### Required - `privacy_level` (string). Who can see the post. One of: `PUBLIC_TO_EVERYONE`, `MUTUAL_FOLLOW_FRIENDS`, `FOLLOWER_OF_CREATOR`, `SELF_ONLY`. Not required when `draft_mode` is true (defaults to `SELF_ONLY`). ### Draft Mode - `draft_mode` (boolean, optional). Send to TikTok drafts inbox instead of posting directly. Allows adding trending sounds in the TikTok app before publishing. When true, `privacy_level` defaults to `SELF_ONLY` if not set. ### Interaction Settings - `allow_comment` (boolean, optional). Defaults to true. - `allow_duet` (boolean, optional). Video posts only. Defaults to true. - `allow_stitch` (boolean, optional). Video posts only. Defaults to true. ### Content Disclosure - `commercial_content` (boolean, optional). Enable commercial content disclosure. Defaults to false. - `brand_organic` (boolean, optional). Self-promotion flag ("Your Brand"). Cannot be combined with `brand_content`. - `brand_content` (boolean, optional). Paid partnership flag ("Branded Content"). Cannot be combined with `brand_organic` or `privacy_level: SELF_ONLY`. ### Music (Photo Mode only) - `auto_add_music` (boolean, optional). Tells TikTok to auto-add a recommended track to the slideshow. Photo Mode posts only. Ignored on videos. Cannot be used with `draft_mode` (the drafts inbox doesn't accept this flag; it's a DIRECT_POST-only field per TikTok's Content Posting API spec). Defaults to false. **When to enable auto_add_music.** TikTok's algorithm rewards Photo Mode slideshows with audio tracks over silent ones. Setting `auto_add_music: true` lets TikTok pick a fitting track at publish time and is the no-friction option for automated workflows. If you want manual control over the track instead, use `draft_mode: true` and pick one in the TikTok app before publishing. ## Business Rules **Privacy level is required (unless draft mode).** Every TikTok post must specify a `privacy_level`, unless `draft_mode` is true. When draft_mode is enabled, privacy_level defaults to SELF_ONLY. **draft_mode sends to TikTok inbox.** When draft_mode is true (or privacy_level is "SELF_ONLY"), the post is uploaded to your TikTok drafts inbox. This is useful for adding trending sounds and making final edits in the TikTok app before publishing. **Content disclosure constraints.** If commercial_content is true, at least one of brand_organic or brand_content must be true. brand_organic and brand_content cannot both be true. brand_content cannot be combined with privacy_level SELF_ONLY. **Duet and Stitch are video-only.** The allow_duet and allow_stitch settings only apply to video posts. They are ignored for Photo Mode slideshows. ## Examples ```json { "type": "schedule", "scheduled_at": "2026-06-01T14:00:00.000Z", "caption": "My landlord said I can't change anything so I showed her this #fyp #roomtransformation", "media_ids": ["video-id"], "account_ids": ["tiktok-account-id"], "platform_settings": { "tiktok": { "privacy_level": "PUBLIC_TO_EVERYONE", "allow_comment": true, "allow_duet": true, "allow_stitch": true } } } ``` ```json { "type": "now", "caption": "6 ways to redesign your kitchen without renovation #interiordesign #beforeandafter", "media_ids": ["slide-1-id", "slide-2-id", "slide-3-id", "slide-4-id", "slide-5-id", "slide-6-id"], "account_ids": ["tiktok-account-id"], "platform_settings": { "tiktok": { "draft_mode": true, "allow_comment": true } } } ``` **Tip for AI agents:** TikTok's algorithm heavily favors posts with trending sounds. For slideshows: either set `auto_add_music: true` (TikTok picks the track), or use `draft_mode: true` and pick a track manually in the TikTok app. ## Caption Rules - Max length: 2,200 characters. Longer captions are rejected before the post is saved. - Hashtags: include in caption text. TikTok currently limits to 5 hashtags. --- Source: https://quegly.com/developers/platforms/x # X (Twitter) X supports text-only tweets, tweets with images, and tweets with video. No additional platform settings are required. ## Media Constraints | Constraint | Images | Videos | | --- | --- | --- | | Formats | JPEG, PNG | MP4 (H.264 + AAC) | | Max file size | 5 MB | 512 MB | | Max count per post | 4 | 1 | | Max duration | n/a | 2 min 20 sec (140 seconds) | | Text-only | Supported | Supported | | Mix images + video | Not allowed | Not allowed | ## Platform Settings One optional setting, under `platform_settings.x`. Leave the object out for a plain post. | Field | Type | Description | | --- | --- | --- | | `tagged_user_ids` | string[] | Up to 10 numeric X user IDs to tag in the post's photos or video. Only used when the post has media; ignored on text-only posts. Send IDs, not @handles. | ## Examples ```json { "type": "now", "caption": "Just launched a new AI room redesign feature. Try it out and let me know what you think!", "account_ids": ["x-account-id"] } ``` ```json { "type": "schedule", "scheduled_at": "2026-06-01T14:00:00.000Z", "caption": "Before and after room transformation using AI. The results are incredible.", "media_ids": ["image-id-1", "image-id-2"], "account_ids": ["x-account-id"] } ``` ```json { "type": "schedule", "scheduled_at": "2026-06-01T18:00:00.000Z", "caption": "Watch this 60 second room transformation", "media_ids": ["video-id"], "account_ids": ["x-account-id"] } ``` ## Caption Rules - Max length: 280 characters for every X account, each link counted at its full length. Longer captions are rejected before the post is saved. To send a longer caption to the post's other platforms, give X its own in `platform_settings.x.caption`: the 280-character limit is checked against that text instead. --- Source: https://quegly.com/developers/platforms/threads # Threads Threads supports text-only posts, a single image or video, and carousels of 2-10 images. One optional setting attaches a topic tag. ## Media Constraints | Constraint | Images | Videos | | --- | --- | --- | | Formats | JPEG, PNG | MP4 (H.264 or HEVC + AAC) | | Max file size | 8 MB | 1 GB | | Max count per post | 10 | 1 | | Max duration | n/a | 5 minutes | | Max resolution | 1440px wide | 1920x1080 | | Text-only | Supported | Supported | | Mix images + video | Not allowed | Not allowed | Threads itself can mix images and videos in one carousel, but a Quegly post carries images or one video, never both, on every platform. ## Platform Settings Optional, under `platform_settings.threads`. | Field | Type | Values | | --- | --- | --- | | `topic_tag` | string | 1-50 characters, no periods, ampersands or spaces. A leading `#` is stripped. | ## Examples ```json { "type": "now", "caption": "Shipping the Threads integration today. Text posts, images, video and carousels from one calendar.", "account_ids": ["threads-account-id"] } ``` ```json { "type": "schedule", "scheduled_at": "2026-10-01T14:00:00.000Z", "caption": "Three room makeovers, one afternoon.", "media_ids": ["image-id-1", "image-id-2", "image-id-3"], "account_ids": ["threads-account-id"], "platform_settings": { "threads": { "topic_tag": "interiordesign" } } } ``` ## Caption Rules - Max length: 500 characters. This is a hard limit on Threads' side; longer captions are rejected before the post is saved. - Threads counts emoji as multi-byte, so a caption near 500 characters with many emoji can still be refused by Threads. Keep emoji-heavy posts a little shorter. - Up to 5 unique URLs per post. --- Source: https://quegly.com/developers/platforms/linkedin # LinkedIn LinkedIn supports text-only posts, 1 to 10 photos, or one video, published to the personal profile of the member who connected it. Three optional settings choose who can see the post and turn several photos into a carousel people swipe through. ## Media Constraints | Constraint | Images | Videos | | --- | --- | --- | | Formats | JPEG, PNG | MP4, MOV (H.264 + AAC recommended) | | Max file size | 8 MB | 500 MB | | Max count per post | 10 | 1 | | Duration | n/a | 3 seconds to 15 minutes | | Text-only | Supported | Supported | | Mix images + video | Not allowed | Not allowed | ## Platform Settings Optional, under `platform_settings.linkedin`. Leave the object out for a post anyone can see, with several photos shown as LinkedIn's grid. | Field | Type | Description | | --- | --- | --- | | `visibility` | string | Who can see the post: `PUBLIC` (anyone, LinkedIn's default) or `CONNECTIONS` (the member's connections only). | | `carousel` | boolean | Post 2 or more photos as a swipeable carousel instead of LinkedIn's grid. Quegly turns the photos into a PDF, one photo per page, and posts it as a LinkedIn document. Ignored for one photo, a video or a text-only post. | | `carousel_title` | string | The title LinkedIn shows above the carousel. Required when `carousel` is `true` and the post has 2 or more photos. Up to 58 characters; runs of spaces and line breaks become one space. | ## Examples ```json { "type": "now", "caption": "Three lessons from our first year of remote work, in one post. What would you add?", "account_ids": ["linkedin-account-id"] } ``` Photos as a swipeable carousel, for connections only: ```json { "type": "schedule", "scheduled_at": "2026-10-01T14:00:00.000Z", "caption": "Three room makeovers, one afternoon. Swipe for the before and after.", "media_ids": ["image-id-1", "image-id-2", "image-id-3"], "account_ids": ["linkedin-account-id"], "platform_settings": { "linkedin": { "visibility": "CONNECTIONS", "carousel": true, "carousel_title": "Three room makeovers, one afternoon" } } } ``` ## Caption Rules - Max length: 3,000 characters. Longer captions are rejected before the post is saved. ## Connection - Personal profiles only. Quegly posts to the profile of the member who connected the account; LinkedIn Company Pages cannot be connected. - A LinkedIn profile has no @handle: the account's `account_username` and `display_name` are the member's name. - Each connection ends on a known date: 60 days after the account is connected today, longer once LinkedIn renews the access automatically. Quegly emails the workspace owner a week before. Reconnect the account on the Manage page before then; once the access has ended, LinkedIn posts fail until the account is reconnected. Reconnecting the same profile keeps its account ID. ## Analytics LinkedIn shares a member's post stats only with apps it approves for its Community Management API, and only once the member allows them (the `r_member_postAnalytics` permission, asked for when LinkedIn is connected). Once allowed, a LinkedIn post's `metrics` carry `views`, `reach`, `likes`, `reactions`, `comments`, `reposts`, `shares` (sent in LinkedIn messages), `saved` and `url_link_clicks`, and a video's `watch_time_seconds`; until then they are `{}`. LinkedIn limits its stats calls per day, so LinkedIn posts refresh on a slower cadence than the other platforms'. Its `post_url` links to the post on LinkedIn either way. --- Source: https://quegly.com/developers/platforms/bluesky # Bluesky Bluesky supports text-only posts, 1 to 10 photos, or one video, with up to 300 characters of text. Links, mentions and hashtags in the caption become clickable, and a text post with a link shows the link's card. Bluesky has no settings of its own. ## Media Constraints | Constraint | Images | Videos | | --- | --- | --- | | Formats | JPEG, PNG | MP4, MOV (MP4 recommended) | | Max file size | 2 MB | 300 MB | | Max count per post | 10 | 1 | | Duration | n/a | Up to 10 minutes | | Text-only | Supported | Supported | | Mix images + video | Not allowed | Not allowed | ## Posting Behavior **Text Only.** The caption on its own. When it has a link, Bluesky shows a card for the first one (the page's title, description and image), which Quegly fetches as the post is published, as the Bluesky app does when a post is written. A page that cannot be read leaves the post without a card; the link still works. **1-4 Photos.** Posted as Bluesky's photo grid (`app.bsky.embed.images`), which every Bluesky app shows. **5-10 Photos.** Posted as a gallery (`app.bsky.embed.gallery`), Bluesky's embed for more than 4 photos. **Photo Metadata.** A photo's file stays downloadable from Bluesky, so Quegly strips each JPEG's metadata before uploading it: EXIF, XMP, IPTC and comments, which can hold where the photo was taken. Its orientation and color profile are kept. **One Video.** Sent to Bluesky's video service, which processes it first. Quegly publishes the post once the video is ready, so it never goes out without it. Bluesky takes videos only from accounts with a verified email, and up to 25 a day per account. ## Platform Settings None of its own. Under `platform_settings.bluesky`, the one field that has an effect is the `caption` every platform accepts: it replaces the post's shared caption on Bluesky alone, which is how a longer cross-post gets a version that fits in 300 characters. Any other field there is ignored, without an error. | Field | Type | Description | | --- | --- | --- | | `caption` | string | Bluesky's own text, in place of the post's caption. Up to 300 characters. | ## Examples ```json { "type": "now", "caption": "Three room makeovers, one afternoon. The before and after photos are up: https://example.com/makeovers #interiordesign", "account_ids": ["bluesky-account-id"] } ``` Five photos as a gallery, with a caption just for Bluesky: ```json { "type": "schedule", "scheduled_at": "2026-11-02T14:00:00.000Z", "caption": "Five room makeovers, one afternoon. Swipe through the before and after of each room, and tell us which one you would move into first. Every room was done on a weekend budget, with paint, new lighting and a few thrifted pieces. Paint colors and the full shopping list are at the link in our bio. #interiordesign #homedecor #beforeandafter", "media_ids": ["image-id-1", "image-id-2", "image-id-3", "image-id-4", "image-id-5"], "account_ids": ["instagram-account-id", "bluesky-account-id"], "platform_settings": { "bluesky": { "caption": "Five room makeovers, one afternoon. Which one would you move into first? #interiordesign" } } } ``` ## Caption Rules - Max length: 300 characters, counted the way Bluesky counts them (an emoji with its skin tone is one). Longer captions are rejected before the post is saved. Bluesky also limits a post's text to 3,000 bytes, which only a caption made mostly of emoji reaches. - Links (with or without https://), @mentions, #hashtags and $cashtags become clickable, found the way the Bluesky app finds them. Write a mention as the full handle (@alice.bsky.social); one that matches no account stays plain text. ## Connection - Accounts hosted by Bluesky, which is nearly every Bluesky account, connect on the Manage page through Bluesky's own sign-in page at bsky.social. An account hosted on any other server (PDS) cannot be connected. - The account's `account_username` is its handle without the @, such as `alice.bsky.social`, and `display_name` its display name. Reconnecting the same account keeps its account ID. ## Analytics Bluesky's counts are public: Quegly reads them from Bluesky's public AppView (`app.bsky.feed.getPosts`), with no permission needed from the account. A Bluesky post's `metrics` carry `likes`, `comments` (Bluesky's replies), `reposts`, `quotes` and `bookmarks`; Bluesky has no views, reach or watch time to give. Its `post_url` links to the post on bsky.app. --- Source: https://quegly.com/developers/platforms/pinterest # Pinterest Pinterest supports Pins with one photo, 2 to 5 photos as a carousel, or one video, each on one of the account's boards. Every Pin needs a board and a photo or a video: there are no text-only Pins. The caption is the Pin's description, up to 800 characters, and an optional title and link go with it. Pinterest is rolling out, so not every Quegly account can connect it yet. ## Media Constraints | Constraint | Images | Videos | | --- | --- | --- | | Formats | JPEG, PNG | MP4, MOV (MP4 with H.264 recommended) | | Max file size | 20 MB | 500 MB | | Max count per post | 5 | 1 | | Duration | n/a | 4 seconds to 15 minutes | | Aspect ratio | 2:3 recommended (1000 x 1500) | 9:16, 2:3 or 1:1 recommended | | Text-only | Not supported | Not supported | | Mix images + video | Not allowed | Not allowed | Pinterest itself takes videos up to 2 GB; 500 MB is Quegly's upload limit. Formats, file sizes and the number of photos are checked before the post is saved, and a video's length by Pinterest when the post publishes. ## Posting Behavior **One Photo.** Posted as a standard Pin. **2-5 Photos.** Posted as one carousel Pin, which people swipe through, in the order of `media_ids`. **One Video.** Uploaded to Pinterest, which processes it first. Quegly makes the Pin once the video is ready, so a Pin never goes out without it; a video Pinterest is still processing after 6 minutes fails the post on Pinterest. Its cover is the video's thumbnail (see Uploads), or the video's first frame when it has none or Pinterest cannot use it. **Title and Link.** The caption becomes the Pin's description. A `title` shows on the Pin above it, and a `link` is where the Pin takes people when they tap it. Quegly sends neither unless it is set. ## Platform Settings Under `platform_settings.pinterest`, and required on every post with a Pinterest account in `account_ids`: a Pin needs a `board_id`, the board it goes on. Without one, the post is refused with 400 VALIDATION_ERROR, drafts included. `title` and `link` are optional, and like every platform Pinterest also takes its own `caption`. Any other field there is ignored, without an error. | Field | Type | Description | | --- | --- | --- | | `board_id` | string | Required. The id of the board the Pin goes on, one of the connected account's boards: digits only, sent as a string, such as `"549755885175"`. | | `title` | string | Optional. The Pin's title, shown above its description. Up to 100 characters; runs of spaces and line breaks become one space. | | `link` | string | Optional. Where the Pin takes people when they tap it: a web address (http or https), up to 2,048 characters. One written without https://, such as `example.com/recipes`, gets it in front. | | `caption` | string | Optional. Pinterest's own description, in place of the post's caption. Up to 800 characters. | **Finding a board id.** `GET /v1/pinterest/boards` lists the connected account's boards, read live from Pinterest: `{ "boards": [{ "id": "549755885175", "name": "Summer recipes", "privacy": "PUBLIC" }] }`. `POST /v1/pinterest/boards` with `{ "name": "..." }` (up to 50 characters) makes a public board and returns it as `{ "board": { ... } }`. Both answer 404 NOT_FOUND when the workspace has no Pinterest account connected. A board's web address shows its name, not its id, so use these. Quegly checks that `board_id` is digits before the post is saved. A board that is not the account's, or was deleted since, fails when the post publishes, with Pinterest's reason in that platform's `error`. **Updating a post.** On `PUT /v1/posts/{id}`, `platform_settings` replaces the post's settings as a whole, so send `board_id` again with any change to them. ## Examples Four photos as a carousel Pin, with a title and a link: ```json { "type": "schedule", "scheduled_at": "2026-11-02T14:00:00.000Z", "caption": "Four small living rooms, each styled on a weekend budget with paint, new lighting and a few thrifted pieces. Swipe for the before and after, and save the one you would copy first. #smallspaces #livingroomdecor #homedecor", "media_ids": ["image-id-1", "image-id-2", "image-id-3", "image-id-4"], "account_ids": ["pinterest-account-id"], "platform_settings": { "pinterest": { "board_id": "549755885175", "title": "Four small living rooms, styled on a weekend budget", "link": "https://example.com/small-living-rooms" } } } ``` A video Pin, posted now beside an Instagram Reel, with a description just for Pinterest: ```json { "type": "now", "caption": "One weekend, one rental kitchen, no renovation. We swapped the cabinet handles, painted the backsplash tiles, added peel-and-stick shelving and changed every bulb to warm white. Every product is linked in our bio. Which change would you make first? #kitchenmakeover #rentalfriendly #homedecor", "media_ids": ["video-id"], "account_ids": ["instagram-account-id", "pinterest-account-id"], "platform_settings": { "pinterest": { "board_id": "549755885175", "caption": "A rental kitchen refreshed in one weekend, with no renovation: new cabinet handles, painted backsplash tiles, peel-and-stick shelving and warm white bulbs.", "link": "https://example.com/rental-kitchen" } } } ``` ## Caption Rules - Max length: 800 characters, the most a Pin's description holds. Longer captions are rejected before the post is saved. - The caption is sent as written, as the Pin's description. It can be left out: a Pin needs a photo or a video, not text. - The title never comes from the caption: Quegly sends one only when `title` is set. ## Connection - Pinterest is rolling out: for now it is open to a first group of Quegly accounts. Until it reaches an account, the Manage page shows Pinterest as coming soon. As for every platform, the API posts only to a Pinterest account connected there. - Personal and business Pinterest accounts connect on the Manage page, through Pinterest's own sign-in page. Quegly asks to see and make the account's boards and Pins, and to see its profile. It does not ask for secret boards, so it cannot post to them. - The account's `account_username` is its Pinterest username, and its `display_name` is `null`. Reconnecting the same account keeps its account ID. - The connection renews itself: Pinterest's access lasts 30 days, and Quegly renews it before it ends. If Pinterest refuses a renewal, for example after the access was removed in Pinterest's settings, the account switches off: it leaves `GET /v1/accounts`, and Pinterest posts fail until it is reconnected on the Manage page. ## Analytics Pinterest gives a Pin's numbers to business accounts only: a personal account's Pins read 0 on every key. Quegly reads them from Pinterest's Pin analytics (`GET /v5/pins/{id}/analytics`). A Pinterest post's `metrics` carry `views` (impressions), `likes` (reactions), `comments`, `saved` (saves), `post_clicks` (clicks on the Pin) and `url_link_clicks` (clicks through to its link). Pinterest's numbers come in a day or two late, so Pinterest posts refresh at most every 6 hours on their first two days, then daily. Its `post_url` links to the Pin: `https://www.pinterest.com/pin/123456789012345678/`. --- Source: https://quegly.com/developers/platforms/telegram # Telegram Telegram supports text-only posts, 1 to 10 photos, or one video. Quegly's bot posts them to a channel or group. Text holds up to 4,096 characters, and a caption under photos or a video up to 1,024; a longer text follows the media as its own message. Telegram has no settings of its own, and no analytics. ## Media Constraints | Constraint | Images | Videos | | --- | --- | --- | | Formats | JPEG, PNG | MP4, MOV (MP4 with H.264 recommended) | | Max file size | 10 MB | 50 MB | | Max count per post | 10 | 1 | | Duration | n/a | No limit (only the 50 MB size) | | Text-only | Supported | Supported | | Mix images + video | Not allowed | Not allowed | The file sizes are what Telegram takes from a bot. Telegram itself can mix photos and videos in one album, but a Quegly post carries photos or one video, never both, on every platform. ## Posting Behavior **Text Only.** Sent as one message, up to 4,096 characters. **One Photo or Video.** Sent with the text as its caption. Telegram compresses photos, as it does for any photo sent in its apps. **2-10 Photos.** Sent as one Telegram album, with the text as its caption. **Longer Text With Media.** A caption under photos or a video holds 1,024 characters. When the text is longer, the photos or video go out without a caption, and the whole text follows right after as its own message. If that message cannot be sent, the post still counts as posted, with a warning, since the media is already out. ## Platform Settings None of its own. Under `platform_settings.telegram`, the one field that has an effect is the `caption` every platform accepts: it replaces the post's shared caption on Telegram alone, for example to keep it within the 1,024 characters a caption under media holds. Any other field there is ignored, without an error. | Field | Type | Description | | --- | --- | --- | | `caption` | string | Telegram's own text, in place of the post's caption. Up to 4,096 characters. | ## Examples ```json { "type": "now", "caption": "New this week: three room makeovers on a weekend budget. Paint colors, lighting and the full shopping list are on the blog: https://example.com/makeovers", "account_ids": ["telegram-account-id"] } ``` Four photos as one album, with a caption just for Telegram: ```json { "type": "schedule", "scheduled_at": "2026-11-02T14:00:00.000Z", "caption": "Four room makeovers, one weekend each. Swipe through the before and after, and tell us which room you would move into first. Paint colors and the full shopping list are at the link in our bio. #interiordesign #homedecor #beforeandafter", "media_ids": ["image-id-1", "image-id-2", "image-id-3", "image-id-4"], "account_ids": ["instagram-account-id", "telegram-account-id"], "platform_settings": { "telegram": { "caption": "Four room makeovers, one weekend each. Which room would you move into first? Paint colors and the shopping list: https://example.com/makeovers" } } } ``` ## Caption Rules - Max length: 4,096 characters. Longer captions are rejected before the post is saved. Under photos or a video, Telegram's caption holds 1,024, and a longer text is sent after the media as its own message. - The text is sent as written: Markdown and HTML are not turned into formatting. Telegram makes links, @mentions and #hashtags clickable on its own. ## Connection - A Telegram channel or group, which Quegly's bot posts to. No Telegram password or login is shared with Quegly. A workspace posts to one Telegram chat at a time; connecting another one replaces it. - Connect it on the Manage page with Connect on Telegram. For a channel, start Quegly's bot in Telegram and tap Pick a channel: Telegram makes the bot an admin there, allowed to post and nothing else. For a group, pick the group and Telegram adds the bot to it. The bot posts nothing in the chat to connect it. - The account's `account_username` is the chat's title, and `display_name` its public @username, or the title again for a private chat. Reconnecting the same chat keeps its account ID. - If the bot is removed from the chat or loses the right to post, the account switches off: it leaves `GET /v1/accounts`, and Telegram posts fail. Add the bot back, then reconnect Telegram on the Manage page. ## Analytics None. Telegram gives bots no views, reactions or other numbers for a post, so a Telegram entry's `metrics` is always `{}`, and an analytics refresh leaves Telegram posts out. Its `post_url` links to the message: `https://t.me/mychannel/42` in a public channel or group, or `https://t.me/c/1234567890/42` in a private one, which opens for its members only. Messages in a basic group have no links, so its `post_url` is `null`.