LateLate API
Core

Posts


List posts visible to the authenticated user

Getting Post URLs: For published posts, each platform entry includes platformPostUrl with the public URL. Use status=published filter to fetch only published posts with their URLs.

Notes and constraints by platform when interpreting the response:

  • YouTube: posts always include at least one video in mediaItems.
  • Instagram/TikTok: posts always include media; drafts may omit media until finalized in client.
  • TikTok: mediaItems will not mix photos and videos in the same post.
GET
/v1/posts
AuthorizationBearer <token>

API key authentication - use your Late API key as a Bearer token

In: header

Query Parameters

page?integer

Page number (1-based)

Default1
Range1 <= value
limit?integer

Page size

Default10
Range1 <= value <= 100
status?string
Value in"draft" | "scheduled" | "published" | "failed"
platform?string
profileId?string
createdBy?string
dateFrom?string
Formatdate
dateTo?string
Formatdate
includeHidden?boolean
Defaultfalse

Response Body

application/json

application/json

curl -X GET "https://getlate.dev/api/v1/posts"

{
  "posts": [
    {
      "_id": "65f1c0a9e2b5af0012ab34cd",
      "title": "Launch post",
      "content": "We just launched!",
      "status": "scheduled",
      "scheduledFor": "2024-11-01T10:00:00Z",
      "timezone": "UTC",
      "platforms": [
        {
          "platform": "twitter",
          "accountId": {
            "_id": "64e1f0...",
            "platform": "twitter",
            "username": "@acme",
            "displayName": "Acme Corp",
            "isActive": true
          },
          "status": "pending"
        }
      ],
      "tags": [
        "launch"
      ],
      "createdAt": "2024-10-01T12:00:00Z",
      "updatedAt": "2024-10-01T12:00:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 10,
    "total": 1,
    "pages": 1
  }
}

{
  "error": "Unauthorized"
}

Create a draft, scheduled, or immediate post

Getting Post URLs:

  • For immediate posts (publishNow: true): The response includes platformPostUrl in each platform entry under post.platforms[].
  • For scheduled posts: Fetch the post via GET /v1/posts/{postId} after the scheduled time; platformPostUrl will be populated once published.

Content/Caption requirements:

  • content (caption/description) is optional when:
    • Media is attached (mediaItems or per-platform customMedia)
    • All platforms have customContent set
    • Posting only to YouTube (title is used instead)
  • Text-only posts (no media) require content
  • Stories do not use captions (content is ignored)
  • Reels, feed posts, and other media posts can have optional captions

Platform constraints:

  • YouTube requires a video in mediaItems; optional custom thumbnail via MediaItem.thumbnail.
  • Instagram and TikTok require media; do not mix videos and images for TikTok.
  • Instagram carousels support up to 10 items; Stories publish as 'story'.
  • Threads carousels support up to 10 images (no videos in carousels); single posts support one image or video.
  • Facebook Stories require media (single image or video); set contentType to 'story' in platformSpecificData.
  • LinkedIn multi-image supports up to 20 images; single PDF documents supported (max 100MB, ~300 pages, cannot mix with other media).
  • Pinterest supports single image via image_url or a single video per Pin; boardId is required.
  • Bluesky supports up to 4 images per post. Images may be automatically recompressed to ≤ ~1MB to satisfy Bluesky's blob limit. When no media is attached, a link preview may be generated for URLs in the text.
  • Snapchat requires media (single image or video); set contentType to 'story', 'saved_story', or 'spotlight' in platformSpecificData. Stories are ephemeral (24h), Saved Stories are permanent, Spotlight is for video content.

Multi-page/multi-location posting: Some platforms allow posting to multiple pages, organizations, or locations from a single account connection. Use the same accountId multiple times with different targets in platformSpecificData:

  • Facebook: pageId - post to multiple Facebook Pages (list via GET /v1/accounts/{id}/facebook-page)
  • LinkedIn: organizationUrn - post to multiple organizations (list via GET /v1/accounts/{id}/linkedin-organizations)
  • Google Business: locationId - post to multiple locations (list via GET /v1/accounts/{id}/gmb-locations)
  • Reddit: subreddit - post to multiple subreddits from the same account
POST
/v1/posts
AuthorizationBearer <token>

API key authentication - use your Late API key as a Bearer token

In: header

title?string
content?string

Post caption/text content. Optional when media is attached (images, videos, etc.). Required for text-only posts. Can also be omitted if all platforms have customContent set.

mediaItems?
platforms?
scheduledFor?string
Formatdate-time
publishNow?boolean
Defaultfalse
isDraft?boolean
Defaultfalse
timezone?string
Default"UTC"
tags?array<string>

Tags/keywords for the post. YouTube-specific constraints:

  • No count limit; duplicates are automatically removed
  • Each tag must be ≤ 100 characters
  • Combined total across all tags ≤ 500 characters (YouTube's limit)
hashtags?array<string>
mentions?array<string>
crosspostingEnabled?boolean
Defaulttrue
metadata?
tiktokSettings?

Root-level TikTok settings applied to all TikTok platforms in the request. This is a convenience shorthand. Settings here are merged into each TikTok platform's platformSpecificData, with platform-specific settings taking precedence.

queuedFromProfile?string

Profile ID to schedule via queue.

When provided (without scheduledFor), the post will be automatically assigned to the next available slot from the profile's queue. The system uses distributed locking to prevent race conditions when multiple posts are scheduled concurrently. Do not call /v1/queue/next-slot and then use that time in scheduledFor. That bypasses the queue system and can cause duplicate slot assignments.

queueId?string

Specific queue ID to use when scheduling via queue. Only used when queuedFromProfile is also provided. If omitted, uses the profile's default queue.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://getlate.dev/api/v1/posts" \  -H "Content-Type: application/json" \  -d '{}'

{
  "post": {
    "_id": "65f1c0a9e2b5af0012ab34cd",
    "title": "Launch post",
    "content": "We just launched!",
    "status": "scheduled",
    "scheduledFor": "2024-11-01T10:00:00Z",
    "timezone": "UTC",
    "platforms": [
      {
        "platform": "twitter",
        "accountId": {
          "_id": "64e1f0...",
          "platform": "twitter",
          "username": "@acme",
          "displayName": "Acme Corp",
          "isActive": true
        },
        "status": "pending"
      }
    ]
  },
  "message": "Post scheduled successfully"
}

{
  "error": "string"
}
{
  "error": "Unauthorized"
}
{
  "error": "string"
}
{
  "error": "This exact content was already posted to this account within the last 24 hours.",
  "details": {
    "accountId": "string",
    "platform": "string",
    "existingPostId": "string"
  }
}
{
  "error": "string",
  "details": {}
}

Get a single post

Fetch a single post by ID. For published posts, this returns platformPostUrl for each platform - useful for retrieving post URLs after scheduled posts publish.

GET
/v1/posts/{postId}
AuthorizationBearer <token>

API key authentication - use your Late API key as a Bearer token

In: header

Path Parameters

postIdstring

Response Body

application/json

application/json

application/json

curl -X GET "https://getlate.dev/api/v1/posts/string"

{
  "post": {
    "_id": "65f1c0a9e2b5af0012ab34cd",
    "title": "Launch post",
    "content": "We just launched!",
    "status": "scheduled",
    "scheduledFor": "2024-11-01T10:00:00Z",
    "platforms": [
      {
        "platform": "twitter",
        "accountId": {
          "_id": "64e1f0...",
          "platform": "twitter",
          "username": "@acme",
          "displayName": "Acme Corp",
          "isActive": true
        },
        "status": "pending"
      }
    ]
  }
}

{
  "error": "Unauthorized"
}
Empty
{
  "error": "Not found"
}

Delete a post

Delete a post. Published posts cannot be deleted. When deleting a scheduled or draft post that consumed upload quota, the quota will be automatically refunded.

DELETE
/v1/posts/{postId}
AuthorizationBearer <token>

API key authentication - use your Late API key as a Bearer token

In: header

Path Parameters

postIdstring

Response Body

application/json

application/json

application/json

curl -X DELETE "https://getlate.dev/api/v1/posts/string"
{
  "message": "Post deleted successfully"
}
Empty
{
  "error": "Unauthorized"
}
Empty
{
  "error": "Not found"
}

Update a post

Update an existing post. Only draft, scheduled, failed, and partial posts can be edited. Published, publishing, and cancelled posts cannot be modified.

PUT
/v1/posts/{postId}
AuthorizationBearer <token>

API key authentication - use your Late API key as a Bearer token

In: header

Path Parameters

postIdstring
[key: string]?any

Response Body

application/json

application/json

application/json

curl -X PUT "https://getlate.dev/api/v1/posts/string" \  -H "Content-Type: application/json" \  -d '{    "content": "Updated content for our launch post!",    "scheduledFor": "2024-11-02T14:00:00Z"  }'
{
  "message": "Post updated successfully",
  "post": {
    "_id": "65f1c0a9e2b5af0012ab34cd",
    "content": "Updated content for our launch post!",
    "status": "scheduled",
    "scheduledFor": "2024-11-02T14:00:00Z"
  }
}
Empty
Empty
{
  "error": "Unauthorized"
}
Empty
{
  "error": "Not found"
}

Validate and schedule multiple posts from CSV

POST
/v1/posts/bulk-upload
AuthorizationBearer <token>

API key authentication - use your Late API key as a Bearer token

In: header

Query Parameters

dryRun?boolean
Defaultfalse
file?file
Formatbinary

Response Body

application/json

application/json

application/json

curl -X POST "https://getlate.dev/api/v1/posts/bulk-upload"
{
  "success": true,
  "totalRows": 10,
  "created": 8,
  "failed": 2,
  "errors": [
    {
      "row": 3,
      "error": "Invalid date format"
    },
    {
      "row": 7,
      "error": "Account not found"
    }
  ],
  "posts": [
    {
      "_id": "65f1c0a9e2b5af0012ab34cd",
      "content": "First bulk post",
      "status": "scheduled",
      "scheduledFor": "2024-11-01T10:00:00Z"
    }
  ]
}
Empty
Empty
{
  "error": "Unauthorized"
}
{
  "error": "string",
  "details": {}
}

Retry publishing a failed or partial post

POST
/v1/posts/{postId}/retry
AuthorizationBearer <token>

API key authentication - use your Late API key as a Bearer token

In: header

Path Parameters

postIdstring

Response Body

application/json

application/json

application/json

application/json

curl -X POST "https://getlate.dev/api/v1/posts/string/retry"
{
  "message": "Post published successfully",
  "post": {
    "_id": "65f1c0a9e2b5af0012ab34cd",
    "content": "Check out our new product!",
    "status": "published",
    "publishedAt": "2024-11-01T10:00:05Z",
    "platforms": [
      {
        "platform": "twitter",
        "accountId": {
          "_id": "64e1f0...",
          "platform": "twitter",
          "username": "@acme",
          "displayName": "Acme Corp",
          "isActive": true
        },
        "status": "published",
        "platformPostId": "1234567890",
        "platformPostUrl": "https://twitter.com/acme/status/1234567890"
      }
    ]
  }
}
Empty
Empty
{
  "error": "Unauthorized"
}
Empty
{
  "error": "Not found"
}
Empty
{
  "error": "string",
  "details": {}
}