Sendhour API
Post to TikTok, YouTube, Instagram, Threads, Bluesky, X, LinkedIn from AI agents, scripts and your own tools. Posts can be scheduled directly and go out at the time you set, exactly like posts made in the composer, or created as drafts that a person confirms first.
Authentication
Create an API key in Settings → API access. Send it as a bearer token. Keys are shown once; replace or revoke them at any time. Anyone with your key can post to your accounts, so keep it out of shared files and public repositories.
Authorization: Bearer sh_xxxxxxxxxxxxxxxxClaude, ChatGPT & Grok Bot
Add Sendhour as a connector and sign in. No API key needed: the app asks you to log in to Sendhour and allow it, then you can say things like "schedule this to Threads and Bluesky for 9 tomorrow" or "what's going out this week?". Disconnect it any time in Settings → Connected apps.
Claude (web, desktop, mobile)
1. Customize → Connectors → + Add → Add custom connector
2. Name: Sendhour
URL: https://sendhour.com/mcp (ends in /mcp)
3. Authentication: Sign in now
OAuth client: Use Claude's published identity
Request headers: leave empty
4. Add → Connect → sign in to Sendhour → Allow
No API key needed.
ChatGPT
1. Plugins → Add (top right) → Add custom MCP server
2. Icon: https://sendhour.com/static/brand/mark-256.png (optional)
Name: Sendhour
Connection: Server URL → https://sendhour.com/mcp (ends in /mcp)
3. Authentication: OAuth
Advanced OAuth settings: leave as they are
4. Tick "I understand and want to continue"
5. Create as a plugin → Connect → sign in to Sendhour → Allow
No API key needed.
Grok Bot
1. In a chat, ask Grok Bot to add the custom MCP server https://sendhour.com/mcp
2. Sign in to Sendhour when it asks → Allow
No API key needed.
If Claude says Couldn't check the server with a 404, check that the URL ends in /mcp. Claude custom connectors work on every Claude plan, including free. ChatGPT custom MCP servers are on the web at chatgpt.com/plugins; if Add custom MCP server is missing, turn on developer mode in Settings → Apps → Advanced settings, or ask your workspace admin. Chat apps can't hand over files from your computer, so give them a public link to the photo or video, or ask for a draft and add the media in Sendhour.
AI agents (MCP)
Sendhour runs an MCP server at https://sendhour.com/mcp (streamable HTTP). Coding agents connect with your API key as a bearer token; apps that support it sign in with OAuth instead. Tools: list_accounts, upload_media_from_url, add_text_to_photo, schedule_post, create_draft, list_posts, get_post, cancel_post, search_viral, list_niches, get_credits, get_analytics, list_videos.
Claude Code
claude mcp add --transport http sendhour https://sendhour.com/mcp \
--header "Authorization: Bearer sh_your_api_key"Codex
# ~/.codex/config.toml (then: export SENDHOUR_API_KEY=sh_your_api_key)
[mcp_servers.sendhour]
url = "https://sendhour.com/mcp"
bearer_token_env_var = "SENDHOUR_API_KEY"Cursor
// ~/.cursor/mcp.json
{ "mcpServers": { "sendhour": {
"url": "https://sendhour.com/mcp",
"headers": { "Authorization": "Bearer sh_your_api_key" }
} } }Gemini CLI
gemini mcp add --transport http --header "Authorization: Bearer sh_your_api_key" sendhour https://sendhour.com/mcpGrok Build (xAI)
# ~/.grok/config.toml
[mcp_servers.sendhour]
url = "https://sendhour.com/mcp"
headers = { "Authorization" = "Bearer sh_your_api_key" }Muse Code (Meta)
// ~/.config/muse/settings.json
{ "schema_version": 1, "mcp_servers": { "sendhour": {
"transport": "streamable_http",
"url": "https://sendhour.com/mcp",
"headers": { "Authorization": "Bearer sh_your_api_key" }
} } }Hermes Agent
# ~/.hermes/config.yaml
mcp_servers:
sendhour:
url: "https://sendhour.com/mcp"
headers:
Authorization: "Bearer sh_your_api_key"Any MCP client
URL: https://sendhour.com/mcp (streamable HTTP)
Auth: OAuth sign-in, or the header Authorization: Bearer sh_your_api_keyCommand line
The sendhour command schedules posts from a terminal or a script, and gives terminal agents a simple tool to call. It's one file with no dependencies and needs Node.js 18 or newer.
npm install -g sendhour # or run any command with npx sendhour …
curl -fsSL https://sendhour.com/install.sh | sh # or, without npm
sendhour login # paste your API key
sendhour accounts
sendhour post "New video is up" --to bluesky --to threads --at "tomorrow 9am"
sendhour post "Behind the scenes" --to youtube --media clip.mp4 \
--title "Behind the scenes" --visibility public --at "fri 18:00"
sendhour post "For TikTok" --to tiktok --media clip.mp4 --privacy public
sendhour posts --status scheduled
sendhour cancel post_…
sendhour viral --niche acne-skincare --days 7 # what's going viral in a niche
sendhour analytics --days 30 # how your accounts and posts did--to takes an account id, an @username or a network name. --media takes files or links and uploads them for you. --at understands 2026-10-20 09:30, tomorrow 9am, fri 18:00, +2h and ISO 8601; times without an offset use your computer's time zone. viral takes the viral library filters as flags (--q, --niche, --category, --platform, --kind, --days, --sort, --limit, --page, --all-posts, --saved); analytics takes --days (7, 30 or 90), --to and --limit. Add --json to any command for machine-readable output. SENDHOUR_API_KEY overrides the saved key. Run sendhour help for every option.
Agent skill
Agents that run shell commands (Claude Code, Codex, OpenClaw and similar) can also use the command line tool or the REST API directly. The skill file at https://sendhour.com/skill.md teaches them the calls and the rules. Install it where your agent loads skills:
# Claude Code; for Codex use ~/.codex/skills, for OpenClaw its skills folder
mkdir -p ~/.claude/skills/sendhour
curl -o ~/.claude/skills/sendhour/SKILL.md https://sendhour.com/skill.md
export SENDHOUR_API_KEY=sh_your_api_keyList accounts
curl https://sendhour.com/api/v1/accounts \
-H "Authorization: Bearer $SENDHOUR_KEY"{ "accounts": [
{ "id": "acc_…", "network": "tiktok", "display_name": "dailybrew", "username": "@dailybrew", "status": "active", "can_schedule": true },
{ "id": "acc_…", "network": "youtube", "display_name": "Daily Brew", "username": "@dailybrew", "status": "active", "can_schedule": true }
] }
Upload media
Send the raw file as the request body with its content type. Videos: MP4 (MOV and WEBM work for TikTok only). Photos: JPEG, PNG, WEBP (PNGs are converted to JPEG; photos larger than 1080 × 1920, or 1920 × 1080 for landscape, are scaled down to fit). Maximum 95 MB per file.
curl -X PUT "https://sendhour.com/api/v1/media?filename=clip.mp4" \
-H "Authorization: Bearer $SENDHOUR_KEY" \
-H "Content-Type: video/mp4" \
--data-binary @clip.mp4{ "id": "med_…", "kind": "video", "duration": 21.4, "width": 1080, "height": 1920 }
Agents that can't send files can pass a public link instead; Sendhour downloads it:
curl -X POST https://sendhour.com/api/v1/media/from-url \
-H "Authorization: Bearer $SENDHOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://example.com/clip.mp4" }'Add text to a photo
Draws a heading and text on one of your photos, the way the studio draws slideshow slides, and returns a new photo (the original stays). The heading is bigger and bold. In text, a line break starts a new line and a blank line starts a new paragraph. Long text is drawn smaller to fit. Every style field is optional: preset (classic, box, caps, minimal), position (top, upper, center, lower, bottom), align, size (small to huge), stroke (an outline around the letters, true or false), text_color, box_color (boxes of that colour behind the text, or "none") and y (an exact height: the middle of the text, in percent from the top, 15 to 75). Free.
curl -X POST https://sendhour.com/api/v1/media/med_…/text \
-H "Authorization: Bearer $SENDHOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "heading": "1. they text you all the time.", "text": "at first, it feels attentive.\n\nbut it can also be pressure.", "style": { "preset": "minimal", "position": "upper", "stroke": true } }'Schedule a post
Creates one post per account, checked against every network's limits first, so either all are scheduled or none. Omit scheduled_at to publish now. TikTok posts publish directly with the TikTok settings in the request (see Network fields).
curl -X POST https://sendhour.com/api/v1/posts \
-H "Authorization: Bearer $SENDHOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"account_ids": ["acc_…", "acc_…"],
"caption": "Five slow mornings worth copying",
"media_ids": ["med_1"],
"scheduled_at": "2026-10-20T07:30:00-04:00"
}'{ "posts": [ { "id": "post_…", "network": "instagram", "status": "scheduled", … }, … ] }
Takes the same fields as a draft, plus account_ids (a list) in place of account_id. Posts appear on your calendar like any other and can be cancelled until they start sending.
Create a draft
A draft waits in Sendhour until a person opens it, checks it and confirms it. Use drafts when you want a look before anything is scheduled. One draft targets one account.
curl -X POST https://sendhour.com/api/v1/drafts \
-H "Authorization: Bearer $SENDHOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"account_id": "acc_…",
"caption": "Five slow mornings worth copying #morningroutine",
"media_ids": ["med_1", "med_2", "med_3"],
"scheduled_at": "2026-10-20T07:30:00-04:00",
"external_ref": "my-post-42"
}'| Field | Notes |
|---|---|
account_id | From /api/v1/accounts |
caption | The post text. Each network's length limit is checked when the draft is confirmed. |
media_ids | Uploaded media, in order |
scheduled_at | Suggested time; the reviewer can change it |
external_ref | Your own identifier, returned with the post |
The response includes a review_url that opens the draft in the composer. Share it with whoever confirms posts.
Network fields
| Network | Extra fields |
|---|---|
| TikTok | One video, or 1–35 photos. title for photo posts (90 characters). content_posting_method: DIRECT_POST (default) publishes; UPLOAD only sends the media to the TikTok app inbox, where you finish it within 24 hours (TikTok then ignores the other settings). privacy_level: PUBLIC_TO_EVERYONE (default), MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR or SELF_ONLY. comment: allow comments (default true). duet, stitch (video): true to allow, off by default. autoAddMusic (photo): yes lets TikTok add music. video_made_with_ai (video): AI-generated label. brand_organic_toggle (your brand) and brand_content_toggle (branded content, not with SELF_ONLY). Drafts take kind (video or photo, required) and leave the settings to the reviewer. |
| YouTube | title (2–100 characters), visibility: public, unlisted or private (both required to schedule), made_for_kids. Needs one MP4 video. |
post_type: post (default) or story. Needs at least one photo or video; up to 10 photos. | |
link_url: share an https link as a card (not with media), with an optional link_title (up to 200 characters; LinkedIn shows this title, not the page's). Up to 3,000 characters; 1–20 JPEG, PNG or GIF photos, or one MP4 video (3 seconds to 30 minutes). | |
| Threads | None. Up to 500 characters and 10 photos or one video. |
| Bluesky | None. Up to 300 characters and 4 photos or one video. |
| X | None. Up to 280 characters and 4 photos or one video. |
Check, list, cancel
curl https://sendhour.com/api/v1/posts/post_… -H "Authorization: Bearer $SENDHOUR_KEY"
curl "https://sendhour.com/api/v1/posts?status=scheduled,draft&limit=50" -H "Authorization: Bearer $SENDHOUR_KEY"
curl -X DELETE https://sendhour.com/api/v1/posts/post_… -H "Authorization: Bearer $SENDHOUR_KEY"Listing accepts status (comma-separated), account_id and limit (up to 200). DELETE cancels a draft or scheduled post; once a post is sending it can't be cancelled (409).
Statuses: draft, scheduled, publishing, processing, published, inbox (TikTok), failed, needs_review, cancelled. needs_review means the outcome is unknown; check the account before trying again. Published posts include a url when the network returns one.
Viral library
Public TikTok posts and YouTube Shorts that got far more views than their account usually gets (the same list as Library in the app). ratio is views divided by the account's usual views. Read-only; the MCP tool is search_viral. Called with no q, niche or category, search_viral searches the niches that match the category you picked when setting up Sendhour and returns them as niches_used (all_niches: true searches everything); list_niches lists every niche. The REST endpoint always searches every niche unless you filter. Included with the free trial and every plan: once a trial or subscription has ended it answers 402 with an error message. A public demo is at /features/viral-library.
curl "https://sendhour.com/api/v1/viral?platform=tiktok&kind=slideshow&days=30&sort=ratio&limit=20" -H "Authorization: Bearer $SENDHOUR_KEY"{ "posts": [ { "id": 812, "platform": "tiktok", "url": "https://www.tiktok.com/@…/photo/…", "kind": "slideshow", "slide_count": 8,
"handle": "…", "caption": "…", "sound": "…", "posted_at": "2026-10-03T14:02:11.000Z", "views": 1840000,
"usual_views": 42000, "ratio": 43.8, "is_outlier": true, "saved": false, … } ],
"page": 1, "pages": 3, "total": 57 }
Each post has niche, the niche its account is followed in, and category. Accounts followed in app-marketing (accounts marketing an app) also have a topic: the niche their posts are about, e.g. dating, or null while it isn't known.
Filters: q (caption, account or sound), platform (tiktok, youtube), category (slug of a group of niches, e.g. beauty), niche (slug; also matches posts whose topic is that niche), kind (video, slideshow), days (7, 30, 90, or 0 for any time; default 30), outliers=false to include every post (the default list also includes a few posts the Sendhour team added by hand, marked added_by_hand: true), saved=true for posts saved in your workspace, sort (ratio, views, newest), limit (up to 100) and page. Open url to watch a post on its network; Sendhour doesn't copy the videos.
Analytics
The numbers on the app's Analytics page: views, engagement (likes, comments, shares, saves, reposts), followers and posts published, overall and per account, for the last 7, 30 or 90 days (days, default 30), with the previous period alongside (null when there is no earlier data). top_posts are the posts published with Sendhour in the period, by engagement, with their lifetime stats. Read-only: it returns the numbers Sendhour already stored (refreshed about hourly) and never asks the networks. null means the network doesn't report that number or there is nothing yet. Filter with account_id (comma-separated); limit sets the number of top posts (default 10, up to 20). Needs an active plan (402 otherwise). The MCP tool is get_analytics.
curl "https://sendhour.com/api/v1/analytics?days=30" -H "Authorization: Bearer $SENDHOUR_KEY"TikTok videos
Every public video on your connected TikTok accounts, however it was posted: with Sendhour, in the TikTok app, or as an inbox draft you finished on your phone. Newest first, with lifetime stats (views, likes, comments, shares) and by_day: the stats as last seen on each day (UTC) since, so you can see which posts are still growing. days (7, 30 or 90, default 30) limits videos by when they were posted; filter with account_id (comma-separated); limit up to 200 (default 50). Read-only and refreshed about hourly; cover_url links expire after a few hours. Accounts need the analytics permissions (needs_reconnect otherwise) and an active plan (402 otherwise). The MCP tool is list_videos.
curl "https://sendhour.com/api/v1/videos?days=7" -H "Authorization: Bearer $SENDHOUR_KEY"{ "days": 30, "start": "2026-09-16", "end": "2026-10-15", "last_synced_at": "2026-10-15T08:00:00.000Z",
"totals": { "views": 48200, "views_previous": 31000, "engagement": 3900, "engagement_previous": 2600,
"followers": 5120, "followers_change": 340, "posts_published": 12, "posts_published_previous": 9 },
"accounts": [ { "id": "acc_…", "network": "tiktok", "display_name": "dailybrew", "username": "@dailybrew", "status": "active",
"needs_reconnect": false, "views": 41000, "engagement": 3500, "followers": 4800, "posts_published": 8, … } ],
"top_posts": [ { "id": "post_…", "account_id": "acc_…", "network": "tiktok", "kind": "video", "title": null,
"caption": "Five slow mornings worth copying", "published_at": "2026-10-03T07:30:00.000Z", "url": "https://…",
"stats": { "views": 18400, "likes": 1500, "comments": 42, "shares": 61, "engagement": 1603 }, "stats_updated_at": "…" } ] }
AI credits
The workspace's AI credit balance (credits pay for images and video in the app's studio). Plan credits expire at the end of each monthly period; extra credits from packs have their own expiry. The MCP tool is get_credits.
curl "https://sendhour.com/api/v1/credits" -H "Authorization: Bearer $SENDHOUR_KEY"{ "balance": 640, "monthly_allowance": 500,
"plan_credits": { "remaining": 340, "expires_at": "2026-11-10T09:00:00.000Z", "trial": false },
"extra_credits": [ { "remaining": 300, "expires_at": "2027-10-02T12:00:00.000Z" } ] }
Limits
- Scheduling posts, creating drafts and uploading media need an active plan.
- TikTok allows roughly 15 API posts per account per day, and 5 pending inbox uploads per 24 hours.
- Uploads are limited to 95 MB per file; photos for networks other than TikTok up to 10 MB.
- Uploaded files are deleted a few days after a post finishes.