Skip to main content
Reference

ClimbX API docs

A small REST API for AI agents to publish and schedule X posts and read analytics and voice data on behalf of a ClimbX account. JSON in, JSON out. v1 is intentionally minimal.

Authentication

Every request needs a key in the Authorization header. Create one in ClimbX under Settings → API. The full key is shown once at creation - store it somewhere safe. We keep only a hash and cannot show it again.

Authorization: Bearer climbx_sk_your_key

The owning account must be on an active plan or trial. A lapsed subscription returns 402 even if the key is still live. Revoke a key any time from the same screen; it stops working on the next call.

Base URL and limits

https://climbx.so/api/v1
  • Daily post cap: 5 posts per account per day across publish and schedule. Resets at 00:00 UTC. Over the cap returns 429.
  • No links: posts containing a URL are rejected with 400. Link posts cut reach and cost 13x to publish.
  • Content type: send Content-Type: application/json on every POST and PATCH.
  • Media: attach up to 4 images by passing image_urls (public https URLs) on publish or schedule. Video over the API is a work in progress.

Publish a post now

POST/api/v1/posts

Ships a post to X immediately through the connected account. Attach up to 4 images with image_urls.

FieldTypeDescription
textstringrequiredThe post body. 1 to 10,000 chars. No URLs.
image_urlsstring[]optionalUp to 4 public https image URLs. Each is fetched and attached. Video is not supported yet.
curl -X POST https://climbx.so/api/v1/posts \
  -H "Authorization: Bearer climbx_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"text": "shipped something today."}'
Response 200
{
  "ok": true,
  "id": "1799999999999999999",
  "url": "https://x.com/i/web/status/1799999999999999999",
  "record_id": "9f1c...",
  "posts_used_today": 1,
  "daily_cap": 5
}

List recent posts with metrics

GET/api/v1/posts

Returns the account’s recent published posts and their latest metrics snapshot.

FieldTypeDescription
limitnumberoptionalHow many to return. 1 to 100, default 30.
curl https://climbx.so/api/v1/posts?limit=10 \
  -H "Authorization: Bearer climbx_sk_your_key"
Response 200
{
  "posts": [
    {
      "id": "1799...",
      "text": "...",
      "format": "hot_take",
      "posted_at": "2026-05-25T08:30:00Z",
      "is_reply": false,
      "metrics": {
        "impressions": 14200,
        "views": 14200,
        "likes": 180,
        "replies": 24,
        "retweets": 12,
        "quote_tweets": 3
      }
    }
  ]
}

Schedule a post

POST/api/v1/schedule

Queues a post for a future time. ClimbX ships it at the scheduled minute and retries on transient failures. A past time publishes on the next tick.

FieldTypeDescription
textstringrequiredThe post body. 1 to 10,000 chars. No URLs.
scheduled_forstringrequiredISO 8601 datetime, e.g. 2026-06-01T14:00:00Z.
image_urlsstring[]optionalUp to 4 public https image URLs, attached at publish time. No video yet.
curl -X POST https://climbx.so/api/v1/schedule \
  -H "Authorization: Bearer climbx_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"text": "morning thought.", "scheduled_for": "2026-06-01T08:00:00Z"}'
Response 200
{
  "ok": true,
  "scheduled": {
    "id": "3a2b...",
    "text": "morning thought.",
    "scheduled_for": "2026-06-01T08:00:00Z",
    "status": "pending"
  },
  "posts_used_today": 2,
  "daily_cap": 5
}

List scheduled posts

GET/api/v1/schedule

Returns upcoming posts that are still pending or mid-publish.

curl https://climbx.so/api/v1/schedule \
  -H "Authorization: Bearer climbx_sk_your_key"
Response 200
{
  "scheduled": [
    {
      "id": "3a2b...",
      "text": "morning thought.",
      "scheduled_for": "2026-06-01T08:00:00Z",
      "status": "pending"
    }
  ]
}

Reschedule a pending post

PATCH/api/v1/schedule/{id}

Moves a still-pending scheduled post to a new time. Works only while the post is pending; once ClimbX has started publishing it, it can’t be changed (returns 409).

FieldTypeDescription
scheduled_forstringrequiredNew ISO 8601 datetime.
curl -X PATCH https://climbx.so/api/v1/schedule/3a2b... \
  -H "Authorization: Bearer climbx_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"scheduled_for": "2026-06-02T09:30:00Z"}'
Response 200
{
  "ok": true,
  "scheduled": {
    "id": "3a2b...",
    "text": "morning thought.",
    "scheduled_for": "2026-06-02T09:30:00Z",
    "status": "pending"
  }
}

Cancel a scheduled post

DELETE/api/v1/schedule/{id}

Cancels a still-pending scheduled post so it won’t publish. Cancelling does not give back a daily-cap slot - the post counted when you created it.

curl -X DELETE https://climbx.so/api/v1/schedule/3a2b... \
  -H "Authorization: Bearer climbx_sk_your_key"
Response 200
{
  "ok": true,
  "cancelled": { "id": "3a2b...", "status": "cancelled" }
}

Performance summary

GET/api/v1/analytics

Headline KPIs plus a per-format breakdown over a window. Replies are excluded.

FieldTypeDescription
daysnumberoptionalLookback window. 1 to 90, default 30.
curl https://climbx.so/api/v1/analytics?days=30 \
  -H "Authorization: Bearer climbx_sk_your_key"
Response 200
{
  "range_days": 30,
  "summary": {
    "posts_published": 42,
    "total_impressions": 512000,
    "avg_impressions": 12190,
    "avg_replies": 18,
    "avg_likes": 140,
    "engagement_rate_pct": 2.8
  },
  "by_format": [
    { "format": "hot_take", "posts": 12, "median_replies": 22, "median_impressions": 15000 }
  ]
}

Voice profile

GET/api/v1/voice

The account’s voice persona, evidence-backed learnings, cadence targets, and posting schedule. Use it to draft in the owner’s voice and time posts well.

curl https://climbx.so/api/v1/voice \
  -H "Authorization: Bearer climbx_sk_your_key"
Response 200
{
  "voice": {
    "persona": "lowercase, short lines, founder-to-founder...",
    "mode": "adaptive",
    "learnings": [
      { "text": "open with a contradiction", "polarity": "positive", "evidence": "..." }
    ],
    "cadence": { "phase": "grow", "posts_per_day": 2, "replies_per_day": 5 },
    "schedule": {
      "timezone": "Europe/Copenhagen",
      "active_start_hour": 8,
      "active_end_hour": 22,
      "weekly_slots": { "mon": ["09:00", "17:00"] }
    }
  }
}

Errors

Errors return a JSON body with an error code and a human message.

StatuserrorWhen
400invalid_body / url_posts_not_allowed / invalid_idBad payload, the text contains a link, or a malformed id.
401missing_bearer / invalid_keyNo key, or the key is unknown or revoked.
402subscription_requiredThe owning account is not on a plan or trial.
404not_foundNo scheduled post with that id on this account.
400image_rejectedAn image_url could not be used: bad URL, blocked host, too large, or not an image.
409x_not_connected / x_token_expired / not_pendingReconnect X, or the post is no longer pending (already publishing or published).
429daily_post_cap_reachedHit the 5/day cap. Resets 00:00 UTC.
500image_store_failedCould not store a scheduled image; the post was rolled back.
502publish_failed / media_upload_failedX rejected the post or an image.