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.
Feature Matrix
A complete comparison of features between API v1 and API v2:
Feature | API v1 | API v2 |
Platforms Support |
|
|
X (Twitter) | Required | Yes |
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 |
|
|
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 |
|
| Requires social_set_id |
|
| Use status filter |
|
| Use status filter |
|
| Set |
|
| Split into two endpoints |
| — | Not available in v2 |
| — | Not available in v2 |
— |
| New: Get single draft |
— |
| New: Edit draft |
— |
| New: Delete draft |
— |
| New: Get presigned upload URL |
— |
| New: Check media processing status |
— |
| New: List 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
platformsobject withenabledflag andpostsarraysThreads are created by adding multiple items to the
postsarrayScheduling uses
publish_atfield (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-keytoAuthorization: BearerUse new API v2 keys
Get social set IDs via
GET /v2/social-setsUpdate draft creation payload to use
platformsstructure withenabledandpostsReplace
threadify: truewith explicitpostsarraysUpdate scheduling to use
publish_atfield (supports ISO 8601 datetime,"now", or"next-free-slot")Replace
recently-scheduled/recently-publishedendpoints with filtered list queriesImplement 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"}]
}
}
}