Skip to main content

Typefully API v1 → v2 migration guide

Updated yesterday

This guide helps you migrate from Typefully API v1 to API v2. API v2 offers significant improvements including cross-platform support, media uploads, and full draft management.

  • API v2 docs and playground are available here

  • You can also read the full guide for API v2 here

Feature Matrix

A complete comparison of features between API v1 and API v2:

Feature

API v1

API v2

Platforms Support

X (Twitter)

Required

Yes

LinkedIn

No

Yes

Mastodon

No

Yes

Threads

No

Yes

Bluesky

No

Yes

Cross-platform posting

No

Different content per platform is possible

Drafts

Create draft

Yes

Yes

List drafts

Recently scheduled/published only

Full list with filters

Get single draft

No

Yes

Edit draft

No

Yes

Delete draft

No

Yes

Filter by status

Separate endpoints

Query parameter on the list drafts API

Filter by tags

No

Yes

Sorting options

No

Yes (created_at, updated_at, scheduled_date, published_at)

Draft tags

No

Yes

Threading

Create threads

Newline-separated content

Explicit posts array

Auto-threadify

Yes (automatic splitting)

Powerful control using posts array

Per-post control

No

Yes (full control over each post)

Scheduling

Schedule for specific time

Yes

Yes

next-free-slot

Yes

Yes (publish_at: "next-free-slot")

Publish immediately

No

Yes (publish_at: "now")

Media

Upload images

No

Yes

Upload videos

No

Yes

Upload GIFs

No

Yes

Upload PDFs

No

Yes (LinkedIn only)

Platform-specific media limits

N/A

Yes (validated per platform)

Notifications

Get notifications

Yes (inbox & activity)

Not available

Mark notifications read

Yes

Not available

Account/User

Verify credentials

Yes

Yes (GET /v2/me)

List accounts/social sets

No

Yes (with platform details)

Tags

List tags

No

Yes

Create tags

No

Yes

Assign tags to drafts

No

Yes

Sharing

Public share URL

Via separate endpoint

Via draft update

Private URL

No

Yes

API Design

RESTful resources

Partial

Full REST

OpenAPI documentation

No

Yes

Pagination

Fixed limit (30)

Configurable limit/offset

Structured error responses

No

Yes (with error codes & field details)

Authentication

Header format

x-api-key: Bearer <key>

Authorization: Bearer <key>

Authentication Changes

API v1:

x-api-key: Bearer YOUR_API_KEY

API v2:

Authorization: Bearer YOUR_API_KEY

Note that API v1 keys cannot be used with API v2

Endpoint Mapping

v1 Endpoint

v2 Equivalent

Notes

POST /api/v1/drafts/

POST /v2/social-sets/{id}/drafts

Requires social_set_id

GET /api/v1/drafts/recently-scheduled/

GET /v2/social-sets/{id}/drafts?status=scheduled

Use status filter

GET /api/v1/drafts/recently-published/

GET /v2/social-sets/{id}/drafts?status=published

Use status filter

POST /api/v1/drafts/{id}/share/

PATCH /v2/social-sets/{id}/drafts/{draft_id}

Set share: true in request body

GET /api/v1/verify-credentials/

GET /v2/me + GET /v2/social-sets

Split into two endpoints

GET /api/v1/notifications/

Not available in v2

POST /api/v1/notifications/mark-all-read/

Not available in v2

GET /v2/social-sets/{id}/drafts/{draft_id}

New: Get single draft

PATCH /v2/social-sets/{id}/drafts/{draft_id}

New: Edit draft

DELETE /v2/social-sets/{id}/drafts/{draft_id}

New: Delete draft

POST /v2/social-sets/{id}/media/upload

New: Get presigned upload URL

GET /v2/social-sets/{id}/media/{media_id}

New: Check media processing status

GET /v2/social-sets/{id}/tags

New: List tags

POST /v2/social-sets/{id}/tags

New: Create tag

New Concepts in v2

Social Sets

API v2 introduces Social Sets - groups of connected platform accounts. All draft operations are scoped under a social set and require a social_set_id.

To get your social set IDs, call:

GET /v2/social-sets

Response:

{
"results": [
{
"id": 12345,
"username": "elonmusk",
"name": "Elon Musk",
"profile_image_url": "https://typefully-user-avatars.s3.amazonaws.com/_generic/account/537/twitter.jpeg",
"team": {
"id": "abc123def4567890",
"name": "Marketing Team"
}
}
],
"count": 1,
"limit": 1,
"offset": 1,
"next": "string",
"previous": "string"
}

To get detailed info including all connected platforms:

GET /v2/social-sets/12345

Response:

{
"id": 12345,
"username": "elonmusk",
"name": "Elon Musk",
"profile_image_url": "https://typefully-user-avatars.s3.amazonaws.com/_generic/account/537/twitter.jpeg",
"team": {
"id": "abc123def4567890",
"name": "Marketing Team"
},
"platforms": {
"x": {
"platform": "x",
"username": "string",
"name": "string",
"profile_image_url": "string",
"profile_url": "string"
},
"linkedin": {
"platform": "linkedin",
"username": "string",
"name": "string",
"profile_image_url": "string",
"profile_url": "string"
},
"mastodon": {
"platform": "mastodon",
"username": "string",
"name": "string",
"profile_image_url": "string",
"profile_url": "string",
"followers_count": 1,
"server": "string"
},
"threads": {
"platform": "threads",
"username": "string",
"name": "string",
"profile_image_url": "string",
"profile_url": "string"
},
"bluesky": {
"platform": "bluesky",
"username": "string",
"name": "string",
"profile_image_url": "string",
"profile_url": "string"
}
}
}

Platform-Specific Content

In v2, you can specify different content for each platform. Each platform has an enabled flag and a posts array:

{
"platforms": {
"x": {
"enabled": true,
"posts": [
{
"text": "Hello world! This is my first post.",
"media_ids": [
"550e8400-e29b-41d4-a716-446655440000"
]
}
],
"settings": {
"reply_to_url": "https://x.com/therajatkapoor/status/1399394576951959554"
}
},
"linkedin": {
"enabled": true,
"posts": [
{
"text": "Hello world! This is my first post.",
"media_ids": [
"550e8400-e29b-41d4-a716-446655440000"
]
}
],
"settings": {}
},
"mastodon": {
"enabled": true,
"posts": [
{
"text": "Hello world! This is my first post.",
"media_ids": [
"550e8400-e29b-41d4-a716-446655440000"
]
}
],
"settings": {}
},
"threads": {
"enabled": true,
"posts": [
{
"text": "Hello world! This is my first post.",
"media_ids": [
"550e8400-e29b-41d4-a716-446655440000"
]
}
],
"settings": {}
},
"bluesky": {
"enabled": true,
"posts": [
{
"text": "Hello world! This is my first post.",
"media_ids": [
"550e8400-e29b-41d4-a716-446655440000"
]
}
],
"settings": {}
}
}
}

You can also publish to only some platforms by setting enabled: false or omitting them from the request.

Scheduling with publish_at

v2 uses a publish_at field to control when the draft is published:

  • Omit publish_at - Save as draft

  • "publish_at": "now" - Publish immediately

  • "publish_at": "next-free-slot" - Schedule to your next available posting slot

  • "publish_at": "2025-01-20T14:00:00Z" - Schedule for a specific time (ISO 8601)

Request Format Changes

Creating a Draft

API v1 Request:

{
"content": "Hello world!\n\n\n\nThis is tweet 2",
"schedule-date": "2025-01-25T10:00:00Z",
"threadify": true
}

API v2 Request:

{
"platforms": {
"x": {
"enabled": true,
"posts": [
{ "text": "Hello world!" },
{ "text": "This is tweet 2" }
]
}
},
"publish_at": "2025-01-25T10:00:00Z"
}

API v2 Response Sample:

{
"id": 12345,
"social_set_id": 67890,
"status": "draft",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-16T09:15:00Z",
"scheduled_date": "2025-01-20T14:00:00Z",
"published_at": "2025-01-20T14:00:05Z",
"draft_title": "Weekly Newsletter",
"tags": [
"marketing",
"product"
],
"preview": "Excited to announce our new feature! 🚀",
"share_url": "https://typefully.com/share/abc123",
"private_url": "https://typefully.com/?d=12345&a=67890",
"platforms": {
"x": {
"enabled": true,
"posts": [
{
"text": "Hello world! This is my first post.",
"media_ids": [
"550e8400-e29b-41d4-a716-446655440000"
]
}
],
"settings": {
"reply_to_url": "https://x.com/therajatkapoor/status/1399394576951959554"
}
},
"linkedin": {
"enabled": true,
"posts": [
{
"text": "Hello world! This is my first post.",
"media_ids": [
"550e8400-e29b-41d4-a716-446655440000"
]
}
],
"settings": {}
},
"mastodon": {
"enabled": true,
"posts": [
{
"text": "Hello world! This is my first post.",
"media_ids": [
"550e8400-e29b-41d4-a716-446655440000"
]
}
],
"settings": {}
},
"threads": {
"enabled": true,
"posts": [
{
"text": "Hello world! This is my first post.",
"media_ids": [
"550e8400-e29b-41d4-a716-446655440000"
]
}
],
"settings": {}
},
"bluesky": {
"enabled": true,
"posts": [
{
"text": "Hello world! This is my first post.",
"media_ids": [
"550e8400-e29b-41d4-a716-446655440000"
]
}
],
"settings": {}
}
},
"x_published_url": "https://x.com/username/status/1234567890",
"linkedin_published_url": "https://www.linkedin.com/feed/update/urn:li:share:1234567890",
"mastodon_published_url": "https://mastodon.social/@username/1234567890",
"threads_published_url": "https://www.threads.net/@username/post/ABC123",
"bluesky_published_url": "https://bsky.app/profile/username.bsky.social/post/abc123",
"x_post_published_at": "2025-01-20T14:00:05Z",
"linkedin_post_published_at": "2025-01-20T14:00:08Z",
"mastodon_post_published_at": "2025-01-20T14:00:10Z",
"threads_post_published_at": "2025-01-20T14:00:12Z",
"bluesky_post_published_at": "2025-01-20T14:00:15Z"
}

Key differences:

  • Content is structured in a platforms object with enabled flag and posts arrays

  • Threads are created by adding multiple items to the posts array

  • Scheduling uses publish_at field (supports ISO datetime, "now", or "next-free-slot")

  • No auto-threadify - you control the thread structure explicitly

  • Response includes full platform status and published URLs

Error Responses

API v1:

{
"error": "Invalid schedule date"
}

API v2:

{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{
"field": "publish_at",
"message": "Schedule date must be in the future",
"type": "value_error",
"loc": ["body", "publish_at"]
}
]
}
}

Code Examples

Creating a Simple Post

v1:

curl -X POST https://api.typefully.com/api/v1/drafts/ \
-H "x-api-key: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"content": "Hello world!"}'

v2:

curl -X POST https://api.typefully.com/v2/social-sets/12345/drafts \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"platforms": {"x": {"enabled": true, "posts": [{"text": "Hello world!"}]}}}'

Creating a Thread

v1:

curl -X POST https://api.typefully.com/api/v1/drafts/ \
-H "x-api-key: Bearer YOUR_KEY" \
-d '{"content": "Tweet 1\n\n\n\nTweet 2", "threadify": true}'

v2:

curl -X POST https://api.typefully.com/v2/social-sets/12345/drafts \
-H "Authorization: Bearer YOUR_KEY" \
-d '{"platforms": {"x": {"enabled": true, "posts": [{"text": "Tweet 1"}, {"text": "Tweet 2"}]}}}'

Scheduling a Post

v1:

curl -X POST https://api.typefully.com/api/v1/drafts/ \
-H "x-api-key: Bearer YOUR_KEY" \
-d '{"content": "Scheduled!", "schedule-date": "2025-01-25T10:00:00Z"}'

v2:

curl -X POST https://api.typefully.com/v2/social-sets/12345/drafts \
-H "Authorization: Bearer YOUR_KEY" \
-d '{"platforms": {"x": {"enabled": true, "posts": [{"text": "Scheduled!"}]}}, "publish_at": "2025-01-25T10:00:00Z"}'

Publishing Immediately (v2 only)

Use "publish_at": "now" to publish immediately:

curl -X POST https://api.typefully.com/v2/social-sets/12345/drafts \
-H "Authorization: Bearer YOUR_KEY" \
-d '{"platforms": {"x": {"enabled": true, "posts": [{"text": "Publishing now!"}]}}, "publish_at": "now"}'

Cross-Platform Posting (v2 only)

Post different content to each platform:

{
"platforms": {
"x": {"enabled": true, "posts": [{"text": "Short for X"}]},
"linkedin": {"enabled": true, "posts": [{"text": "Professional LinkedIn content..."}]}
}
}

Uploading Media (v2 only)

Step 1: Get presigned URL

curl -X POST https://api.typefully.com/v2/social-sets/12345/media/upload \
-H "Authorization: Bearer YOUR_KEY" \
-d '{"file_name": "photo.jpg"}'

Returns media_id and upload_url.

Step 2: Upload to S3

curl -X PUT "<upload_url>" -H --data-binary @photo.jpg

Step 3: Check status

curl https://api.typefully.com/v2/social-sets/12345/media/<media_id> \
-H "Authorization: Bearer YOUR_KEY"

Returns status (processing/ready/failed) and media_urls.

Step 4: Attach to draft

{
"platforms": {
"x": {
"enabled": true,
"posts": [{"text": "With image!", "media_ids": ["<media_id>"]}]
}
}
}

Working with Tags (v2 only)

List tags:

curl https://api.typefully.com/v2/social-sets/12345/tags \
-H "Authorization: Bearer YOUR_KEY"

Response:

{
"results": [
{"slug": "marketing", "name": "Marketing", "created_at": "2025-01-10T09:00:00Z"},
{"slug": "product-launch", "name": "Product Launch", "created_at": "2025-01-12T14:30:00Z"}
],
"count": 2,
"limit": 10,
"offset": 0
}

Create a tag:

curl -X POST https://api.typefully.com/v2/social-sets/12345/tags \
-H "Authorization: Bearer YOUR_KEY" \
-d '{"name": "Weekly Newsletter"}'

Create draft with tags:

curl -X POST https://api.typefully.com/v2/social-sets/12345/drafts \
-H "Authorization: Bearer YOUR_KEY" \
-d '{"platforms": {"x": {"enabled": true, "posts": [{"text": "New post!"}]}}, "tags": ["marketing"]}'

Migration Checklist

  • Update authentication header from x-api-key to Authorization: Bearer

  • Use new API v2 keys

  • Get social set IDs via GET /v2/social-sets

  • Update draft creation payload to use platforms structure with enabled and posts

  • Replace threadify: true with explicit posts arrays

  • Update scheduling to use publish_at field (supports ISO 8601 datetime, "now", or "next-free-slot")

  • Replace recently-scheduled/recently-published endpoints with filtered list queries

  • Implement media upload flow if attaching images/videos

  • Update error handling for new structured error response format

  • Decide on notification handling (keep using v1 or remove functionality)

FAQ

How do I find my social_set_id?

Call GET /v2/social-sets with your API key. The response will include all social sets you have access to with their IDs.

Can I still use v1 endpoints?

Yes, v1 endpoints continue to work until 15th June 2026. Creating new API V1 keys is now disabled. We recommend migrating to v2 for new features and improvements.

What if I need notifications?

Notification endpoints are only available in v1. If you need notifications, continue using the v1 /api/v1/notifications/ endpoint. However, it will be deprecated on 15th June 2026. You can check out our webhooks to receive notifications for important events

How do I handle the threadify behavior?

In v2, you have full control over thread structure. Instead of relying on auto-splitting, explicitly define each post in the posts array. This gives you precise control but requires you to handle splitting yourself.

How do I publish to multiple platforms with different content?

Include each platform in the platforms object with enabled: true and its own content:

{
"platforms": {
"x": {
"enabled": true,
"posts": [{"text": "Short for X"}]
},
"linkedin": {
"enabled": true,
"posts": [{"text": "Longer professional content for LinkedIn"}]
}
}
}
Did this answer your question?