API Reference

OpenTweet API Docs

Create, schedule, publish posts and access analytics for X/Twitter programmatically. Simple REST API with Bearer token auth.

Quick Start

Base URL

https://opentweet.io

Authentication

All endpoints require an API key. Get yours from . You can pass the key in either header:

bash
# Option 1: Authorization header
curl -H "Authorization: Bearer ot_your_api_key" \
  https://opentweet.io/api/v1/me

# Option 2: X-API-Key header
curl -H "X-API-Key: ot_your_api_key" \
  https://opentweet.io/api/v1/me

Rate Limits

Pro Plan

60
requests / minute
1,000
requests / day
50
posts / bulk request

Advanced Plan

300
requests / minute
10,000
requests / day
50
posts / bulk request

Rate limit headers (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset) are included in 429 rate-limit error responses.

Endpoints

GET/api/v1/accounts

List Connected X Accounts

Returns all X accounts connected to your OpenTweet account. Use the account "id" as x_account_id when creating or listing posts for a specific account.

Requires API Key

Response

json
{
  "accounts": [
    {
      "id": "65f1a2b3c4d5e6f7a8b9c0d1",
      "x_handle": "@primary_account",
      "x_name": "Primary Account",
      "is_primary": true,
      "nickname": null,
      "x_subscription_type": "Premium+",
      "connected_at": "2026-01-15T10:00:00Z",
      "profile_image_url": "https://pbs.twimg.com/..."
    },
    {
      "id": "65f1a2b3c4d5e6f7a8b9c0d2",
      "x_handle": "@secondary_account",
      "x_name": "Secondary Account",
      "is_primary": false,
      "nickname": "Brand",
      "x_subscription_type": "Pro",
      "connected_at": "2026-02-20T14:00:00Z",
      "profile_image_url": "https://pbs.twimg.com/..."
    }
  ]
}

Pro plan supports 1 account, Advanced up to 3, Agency up to 10.

Use the "id" field as x_account_id in other endpoints to target a specific account.

GET/api/v1/me

Verify Connection & Check Limits

Verify your API key works and check your subscription status, daily post limits, and post counts. Call this first before scheduling or publishing.

Requires API Key

Response

json
{
  "authenticated": true,
  "subscription": {
    "has_access": true,
    "status": "active",
    "is_trialing": false,
    "trial_ends_at": null,
    "current_period_ends_at": "2026-04-01T00:00:00Z"
  },
  "limits": {
    "can_post": true,
    "posts_published_today": 3,
    "remaining_posts_today": 17,
    "daily_limit": 20
  },
  "stats": {
    "total_posts": 142,
    "scheduled_posts": 8,
    "posted_posts": 120,
    "draft_posts": 14
  }
}

If subscription.has_access is false, you cannot schedule or publish posts.

limits is null when the user has no active subscription.

GET/api/v1/posts

List Posts

Retrieve a paginated list of your posts. Filter by status to see only scheduled, posted, draft, or failed posts.

Requires API Key

Query Parameters

NameTypeDescription
pageintegerPage number (default: 1)
limitintegerPosts per page (default: 20, max: 100)
statusstringFilter: "scheduled", "posted", "draft", or "failed"
x_account_idstringFilter by X account ID (for multi-account users)

Response

json
{
  "posts": [
    {
      "id": "65f1a2b3c4d5e6f7a8b9c0d1",
      "x_account_id": "65f1a2b3c4d5e6f7a8b9c0d1",
      "category": "General",
      "text": "Just shipped a new feature!",
      "scheduled_date": "2026-03-01T10:00:00Z",
      "posted": false,
      "posted_date": null,
      "failed": false,
      "failed_reason": null,
      "is_thread": false,
      "thread_tweets": null,
      "media_urls": null,
      "thread_media": null,
      "community_id": null,
      "share_with_followers": false,
      "x_post_id": null,
      "review_status": "approved",
      "created_at": "2026-02-16T08:30:00Z",
      "updated_at": "2026-02-16T08:30:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 142,
    "pages": 8
  }
}
POST/api/v1/posts

Create Post(s)

Create a single post, a thread, or bulk create up to 50 posts at once. Include scheduled_date to schedule, or publish_now to post to X immediately.

Requires API Key

Request Body

Including scheduled_date requires an active subscription and counts toward daily limits. Omit it to save as a draft. Use publish_now: true to post to X immediately (single post only, cannot combine with scheduled_date or bulk posts). All dates must be in the future (ISO 8601). Use x_account_id to target a specific X account (multi-account users).

json
// Single post (draft)
{
  "text": "Just shipped a new feature!",
  "category": "Product"
}

// Single post with schedule
{
  "text": "Just shipped a new feature!",
  "category": "Product",
  "scheduled_date": "2026-03-01T10:00:00Z"
}

// Publish immediately to X
{
  "text": "Hello from the API!",
  "publish_now": true
}

// Post with media
{
  "text": "Check out this screenshot!",
  "media_urls": ["https://your-uploaded-media-url.jpg"],
  "scheduled_date": "2026-03-01T10:00:00Z"
}

// Post to X Community
{
  "text": "Shared with the community!",
  "community_id": "1234567890",
  "share_with_followers": true
}

// Post to a specific X account (multi-account)
{
  "text": "Hello from my second account!",
  "x_account_id": "65f1a2b3c4d5e6f7a8b9c0d2",
  "publish_now": true
}

// Thread with per-tweet media
{
  "text": "Here's what I learned building a SaaS in 2026:",
  "is_thread": true,
  "thread_tweets": [
    "1/ Start with a problem you have yourself",
    "2/ Ship fast, iterate faster",
    "3/ Your first 10 users matter more than the next 1000"
  ],
  "thread_media": [["https://img1.jpg"], [], ["https://img3.jpg"]],
  "scheduled_date": "2026-03-01T10:00:00Z"
}

// Bulk create (up to 50)
{
  "posts": [
    { "text": "Monday motivation", "scheduled_date": "2026-03-02T09:00:00Z" },
    { "text": "Tuesday tip", "scheduled_date": "2026-03-03T14:00:00Z" }
  ],
  "x_account_id": "optional — applies to all posts if not set per-post"
}

Response

json
{
  "success": true,
  "count": 1,
  "posts": [
    {
      "id": "65f1a2b3c4d5e6f7a8b9c0d1",
      "text": "Just shipped a new feature!",
      "category": "Product",
      "scheduled_date": "2026-03-01T10:00:00Z",
      "is_thread": false,
      "thread_tweets": null,
      "media_urls": null,
      "thread_media": null,
      "community_id": null,
      "share_with_followers": false,
      "created_at": "2026-02-16T08:30:00Z"
    }
  ]
}

Returns 201 on success.

publish_now requires an active subscription and counts toward daily limits.

When publish_now succeeds, response includes x_post_id, posted: true, and posted_date.

When publish_now fails to post to X, returns 502 with the post ID (post is saved but not published).

GET/api/v1/posts/:id

Get Single Post

Retrieve details of a specific post by its ID.

Requires API Key

Response

json
{
  "id": "65f1a2b3c4d5e6f7a8b9c0d1",
  "category": "General",
  "text": "Just shipped a new feature!",
  "scheduled_date": "2026-03-01T10:00:00Z",
  "posted": false,
  "posted_date": null,
  "failed": false,
  "failed_reason": null,
  "is_thread": false,
  "thread_tweets": null,
  "media_urls": null,
  "thread_media": null,
  "community_id": null,
  "share_with_followers": false,
  "x_post_id": null,
  "review_status": "approved",
  "created_at": "2026-02-16T08:30:00Z",
  "updated_at": "2026-02-16T08:30:00Z"
}

Same response shape as individual post in List Posts.

PUT/api/v1/posts/:id

Update Post

Update a draft or scheduled post. You cannot update posts that have already been published.

Requires API Key

Request Body

All fields are optional. Only include the fields you want to update. Setting scheduled_date requires an active subscription. Set scheduled_date to null to unschedule (convert back to draft).

json
{
  "text": "Updated tweet text",
  "category": "Marketing",
  "is_thread": false,
  "thread_tweets": [],
  "media_urls": [],
  "thread_media": [],
  "community_id": "1234567890",
  "share_with_followers": true,
  "scheduled_date": "2026-03-01T10:00:00Z"
}

Response

json
{
  "id": "65f1a2b3c4d5e6f7a8b9c0d1",
  "text": "Updated tweet text",
  "category": "Marketing",
  "scheduled_date": "2026-03-01T10:00:00Z",
  "is_thread": false,
  "thread_tweets": null,
  "media_urls": [],
  "thread_media": null,
  "community_id": "1234567890",
  "share_with_followers": true,
  "updated_at": "2026-02-16T09:00:00Z"
}

Returns 400 if the post has already been published.

Setting scheduled_date counts toward daily limits.

DELETE/api/v1/posts/:id

Delete Post

Permanently delete a post.

Requires API Key

Response

json
{
  "success": true,
  "message": "Post deleted"
}

Deleting a published post only removes it from OpenTweet. It does not delete the tweet from X/Twitter.

POST/api/v1/posts/:id/schedule

Schedule Post

Set or update the scheduled publish time for a post. Requires an active subscription.

Requires API Key

Request Body

Date must be in the future. Use ISO 8601 format.

json
{
  "scheduled_date": "2026-03-01T10:00:00Z"
}

Response

json
{
  "success": true,
  "id": "65f1a2b3c4d5e6f7a8b9c0d1",
  "text": "Just shipped a new feature!",
  "scheduled_date": "2026-03-01T10:00:00Z",
  "message": "Post scheduled successfully"
}

Requires active subscription.

Counts toward daily post limits.

Returns 400 if the post has already been published.

POST/api/v1/posts/:id/publish

Publish Immediately

Publish a post to X/Twitter right now. No request body needed. Requires an active subscription.

Requires API Key

Response

json
{
  "success": true,
  "id": "65f1a2b3c4d5e6f7a8b9c0d1",
  "text": "Just shipped a new feature!",
  "x_post_id": "1234567890123456789",
  "posted_date": "2026-02-16T10:00:00Z",
  "message": "Post published successfully"
}

Requires active subscription.

Counts toward daily post limits.

Returns 400 if the post has already been published.

Returns 502 if the X/Twitter API fails.

POST/api/v1/posts/batch-schedule

Batch Schedule Posts

Schedule multiple existing posts at once (up to 50). Requires an active subscription.

Requires API Key

Request Body

community_id, share_with_followers, and x_account_id are optional and apply to all posts in the batch. All scheduled_date values must be in the future (ISO 8601).

json
{
  "schedules": [
    { "post_id": "65f1a2b3c4d5e6f7a8b9c0d1", "scheduled_date": "2026-03-02T09:00:00Z" },
    { "post_id": "65f1a2b3c4d5e6f7a8b9c0d2", "scheduled_date": "2026-03-03T14:00:00Z" }
  ],
  "community_id": "1234567890",
  "share_with_followers": true,
  "x_account_id": "optional — target X account"
}

Response

json
{
  "success": true,
  "message": "2 post(s) scheduled",
  "scheduled": [
    {
      "id": "65f1a2b3c4d5e6f7a8b9c0d1",
      "text": "Monday motivation",
      "category": "General",
      "scheduled_date": "2026-03-02T09:00:00Z",
      "is_thread": false
    }
  ],
  "errors": [
    { "post_id": "65f1a2b3c4d5e6f7a8b9c0d2", "error": "Post not found" }
  ]
}

Requires active subscription.

Counts toward daily post limits.

Maximum 50 posts per batch.

errors array is only present if some posts failed to schedule.

POST/api/v1/upload

Upload Media

Upload an image or video to use in posts. Returns a URL to include in media_urls or thread_media when creating or updating posts.

Requires API Key

Request Body

Use Content-Type: multipart/form-data. Supported formats: JPG, PNG, GIF, WebP, MP4, MOV. Max size: 5MB for images, 20MB for videos.

json
// multipart/form-data
// field: file (the image or video file)

curl -X POST https://opentweet.io/api/v1/upload \
  -H "Authorization: Bearer ot_your_key" \
  -F "file=@screenshot.png"

Response

json
{
  "url": "https://your-cdn-url.com/uploaded-file.png"
}

Upload media first, then use the returned URL in media_urls or thread_media when creating/updating posts.

GET/api/v1/analytics/overview

Account Overview

Returns comprehensive account-level analytics including posting stats, streaks, trends, and category breakdown.

Requires API Key

Response

json
{
  "stats": {
    "total_posts_created": 142,
    "total_posts_published": 120,
    "total_posts_scheduled": 8,
    "publishing_rate": 85,
    "total_active_days": 45,
    "avg_posts_per_week": 7.2,
    "most_active_day": "monday",
    "most_active_hour": 9,
    "total_characters_written": 28400,
    "total_threads_created": 12,
    "total_media_posts": 34
  },
  "streaks": {
    "current": 5,
    "longest": 14,
    "last_posted_date": "2026-02-26T10:00:00Z",
    "streak_start_date": "2026-02-22T09:00:00Z"
  },
  "trends": {
    "this_week": 7,
    "last_week": 5,
    "this_month": 24,
    "last_month": 18,
    "best_month": { "month": "2026-01", "count": 32 }
  },
  "categories": [
    { "name": "Product", "count": 45, "percentage": 32 }
  ],
  "recent_activity": {
    "last_7_days": [3, 2, 4, 1, 3, 2, 5],
    "last_30_days": [2, 3, 1, ...]
  }
}
GET/api/v1/analytics/tweets

Tweet Engagement Metrics

Returns engagement metrics for published tweets including likes, retweets, impressions, and engagement rates. Requires Advanced plan.

Requires API Key

Query Parameters

NameTypeDescription
periodinteger or "all"Number of days (7-365) or "all" for all time (default: 30)
x_account_idstringFilter by X account ID (for multi-account users)

Response

json
{
  "has_x_account": true,
  "has_data": true,
  "sync_status": {
    "last_synced_at": "2026-02-26T08:00:00Z",
    "total_tweets_synced": 120
  },
  "summary": {
    "total_tracked_tweets": 85,
    "avg_engagement_rate": 3.2,
    "avg_impressions": 1250,
    "avg_likes": 15.4,
    "avg_retweets": 3.1,
    "avg_replies": 2.8,
    "total_impressions": 106250,
    "total_engagements": 3400
  },
  "top_performers": [
    {
      "x_post_id": "1234567890",
      "text_preview": "Just shipped...",
      "content_type": "text",
      "posted_at": "2026-02-20T10:00:00Z",
      "likes": 45, "retweets": 12, "replies": 8,
      "quotes": 3, "impressions": 5200,
      "bookmarks": 6, "engagement_rate": 5.1
    }
  ],
  "worst_performers": [],
  "content_type_stats": [],
  "engagement_timeline": [],
  "best_engagement_hours": [],
  "best_engagement_days": []
}

Requires Advanced plan. Returns 403 on Pro plan.

Returns has_x_account: false if no X account is connected.

Returns has_data: false if no engagement data has been synced yet.

GET/api/v1/analytics/best-times

Best Posting Times

Analyzes your recent publishing patterns to find optimal posting times based on your last 15 published posts.

Requires API Key

Response

json
{
  "has_enough_data": true,
  "total_posts_analyzed": 15,
  "hour_distribution": [
    { "hour": 9, "label": "9AM", "count": 5 }
  ],
  "day_distribution": [
    { "day": "Monday", "short_day": "Mon", "count": 4 }
  ],
  "best_hours": [
    { "hour": 9, "label": "9AM", "count": 5 }
  ],
  "best_days": [
    { "day": "Monday", "count": 4 }
  ],
  "summary": "Most active days: Monday, Wednesday. Best posting hours: 9AM, 2PM"
}

Requires at least 3 published posts. Returns has_enough_data: false otherwise.

Status Codes

CodeMeaning
200Success
201Created (new post)
400Bad request (validation error)
401Unauthorized (invalid API key)
403Forbidden (subscription required)
404Post not found
429Rate limit or daily post limit exceeded
500Server error
502X/Twitter API error

Error Response Shape

json
{
  "error": "Human-readable error message",
  "details": ["Array of specific validation errors (optional)"],
  "remaining": 0  // Only on 429 responses: remaining posts/requests allowed
}

Common Workflows

Verify your connection

bash
curl -H "Authorization: Bearer ot_your_key" \
  https://opentweet.io/api/v1/me

Check authenticated is true and subscription.has_access is true.

Create and schedule a tweet (one step)

bash
curl -X POST https://opentweet.io/api/v1/posts \
  -H "Authorization: Bearer ot_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Just shipped a new feature!",
    "scheduled_date": "2026-03-01T10:00:00Z"
  }'

Post immediately (one step)

bash
curl -X POST https://opentweet.io/api/v1/posts \
  -H "Authorization: Bearer ot_your_key" \
  -H "Content-Type: application/json" \
  -d '{"text": "Hello from the API!", "publish_now": true}'

Or use the two-step approach:

bash
# Step 1: Create a draft
curl -X POST https://opentweet.io/api/v1/posts \
  -H "Authorization: Bearer ot_your_key" \
  -H "Content-Type: application/json" \
  -d '{"text": "Hello from the API!"}'

# Step 2: Publish it (replace POST_ID with the id from step 1)
curl -X POST https://opentweet.io/api/v1/posts/POST_ID/publish \
  -H "Authorization: Bearer ot_your_key"

Schedule a week of content

bash
curl -X POST https://opentweet.io/api/v1/posts \
  -H "Authorization: Bearer ot_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "posts": [
      {"text": "Monday motivation", "scheduled_date": "2026-03-02T09:00:00Z"},
      {"text": "Tuesday tip", "scheduled_date": "2026-03-03T14:00:00Z"},
      {"text": "Wednesday wins", "scheduled_date": "2026-03-04T11:00:00Z"},
      {"text": "Thursday thoughts", "scheduled_date": "2026-03-05T16:00:00Z"},
      {"text": "Friday feels", "scheduled_date": "2026-03-06T10:00:00Z"}
    ]
  }'

Upload media and post with image

bash
# Step 1: Upload the image
curl -X POST https://opentweet.io/api/v1/upload \
  -H "Authorization: Bearer ot_your_key" \
  -F "file=@screenshot.png"
# Returns: { "url": "https://..." }

# Step 2: Create post with the media URL
curl -X POST https://opentweet.io/api/v1/posts \
  -H "Authorization: Bearer ot_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Check out this screenshot!",
    "media_urls": ["https://returned-url-from-upload"],
    "scheduled_date": "2026-03-01T10:00:00Z"
  }'

Post to a specific X account (multi-account)

bash
# Step 1: List your connected accounts
curl -H "Authorization: Bearer ot_your_key" \
  https://opentweet.io/api/v1/accounts
# Returns accounts with their IDs

# Step 2: Create a post targeting a specific account
curl -X POST https://opentweet.io/api/v1/posts \
  -H "Authorization: Bearer ot_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Hello from my second account!",
    "x_account_id": "ACCOUNT_ID_FROM_STEP_1",
    "publish_now": true
  }'

If you omit x_account_id, posts go through your primary account.

Check your analytics

bash
# Account overview (stats, streaks, trends)
curl -H "Authorization: Bearer ot_your_key" \
  https://opentweet.io/api/v1/analytics/overview

# Best posting times
curl -H "Authorization: Bearer ot_your_key" \
  https://opentweet.io/api/v1/analytics/best-times

# Tweet engagement (Advanced plan only)
curl -H "Authorization: Bearer ot_your_key" \
  "https://opentweet.io/api/v1/analytics/tweets?period=30"
🤖

Using OpenClaw?

Install the official skill and your agent handles everything above automatically.

bash
clawhub install opentweet-x-poster
export OPENTWEET_API_KEY="ot_your_key"
openclaw "schedule 5 tweets about our launch for this week"

Your agent can also read the plain-text API docs directly at /api/v1/docs.

Ready to build?

Get your API key and start posting in under 3 minutes.

API included in every plan. No extra charge.