Late API Management Skill
Operational workflows for managing scheduled social media posts via the Late.co API (https://getlate.dev/api/v1). This skill helps you list, reschedule, bulk delete, and sync posts when the posting schedule changes or issues arise.
Prerequisites
LATE_API_KEY environment variable set (Bearer token, format: sk_...)
- The project's
schedule.json in the repo root defines posting time slots per platform
Authentication
All requests use Bearer auth:
curl -H "Authorization: Bearer $env:LATE_API_KEY" https://getlate.dev/api/v1/posts
API Base URL
https://getlate.dev/api/v1
API Reference
Posts
| Method |
Endpoint |
Description |
GET |
/v1/posts |
List posts. Supports query params: status (draft/scheduled/published/failed), platform, profileId, dateFrom, dateTo, createdBy, includeHidden |
POST |
/v1/posts |
Create a post. Fields: content, platforms (array of {platform, accountId}), scheduledFor (ISO datetime), timezone, isDraft, publishNow, mediaItems, queuedFromProfile, queueId, tags, hashtags, tiktokSettings, platformSpecificData |
GET |
/v1/posts/{postId} |
Get a single post by ID |
PUT |
/v1/posts/{postId} |
Update a post. Only draft/scheduled/failed/partial posts can be edited. Fields: content, scheduledFor, tiktokSettings |
DELETE |
/v1/posts/{postId} |
Delete a post. Published posts cannot be deleted. Refunds upload quota |
POST |
/v1/posts/bulk-upload |
Bulk upload from CSV. Optional dryRun query param |
Accounts
| Method |
Endpoint |
Description |
GET |
/v1/accounts |
List connected social media accounts |
Profiles
| Method |
Endpoint |
Description |
GET |
/v1/profiles |
List profiles |
POST |
/v1/profiles |
Create a profile |
Queue (Late's built-in scheduling)
| Method |
Endpoint |
Description |
GET |
/v1/queue/slots?profileId=X |
Get queue schedules for a profile. Add all=true for all queues |
POST |
/v1/queue/slots |
Create a new queue. Body: profileId, name, timezone, slots, active |
PUT |
/v1/queue/slots |
Update queue. Body: profileId, queueId, timezone, slots, reshuffleExisting (auto-reschedule existing posts) |
DELETE |
/v1/queue/slots?profileId=X&queueId=Y |
Delete a queue |
GET |
/v1/queue/preview?profileId=X&count=N |
Preview upcoming N queue slots |
GET |
/v1/queue/next-slot?profileId=X |
Preview next available slot (informational only — do NOT use with scheduledFor) |
Media
| Method |
Endpoint |
Description |
POST |
/v1/media/presign |
Get presigned upload URL. Body: {filename, contentType} → {uploadUrl, publicUrl, key, expiresIn} |
PUT |
{uploadUrl} |
Upload file bytes to the presigned URL with Content-Type header |
Analytics
| Method |
Endpoint |
Description |
GET |
/v1/analytics |
Get post performance metrics and engagement data |
Publishing Logs
| Method |
Endpoint |
Description |
GET |
/v1/logs |
View publishing attempt logs (API endpoint, status, request/response, retries). Retained 7 days |
Webhooks
| Method |
Endpoint |
Description |
GET |
/v1/webhooks |
List configured webhooks |
POST |
/v1/webhooks |
Create a webhook. Events: post.scheduled, post.published, post.failed, post.partial, account.connected, account.disconnected, message.received |
Rate Limits
| Plan |
Requests/min |
| Free |
60 |
| Build |
120 |
| Accelerate |
600 |
| Unlimited |
1,200 |
Additional limits:
- Velocity: 15 posts/hour/account
- Daily: Platform-specific (X: 20, Pinterest: 25, Instagram/Facebook: 100, Threads: 250, others: 50)
- Cooldown: Escalating backoff on repeated errors (10min → 20min → 40min → 24h)
Project-Specific Context
schedule.json Format
The project's schedule.json (repo root) defines local posting time slots:
{
"timezone": "America/Chicago",
"platforms": {
"linkedin": {
"slots": [
{ "days": ["tue", "wed"], "time": "08:00", "label": "Morning thought leadership" }
],
"avoidDays": ["sat", "sun"]
}
}
}
Existing LateApiClient (src/L2-clients/late/lateApi.ts)
The project has a TypeScript client with these methods:
listProfiles() → LateProfile[]
listAccounts() → LateAccount[]
getScheduledPosts(platform?) → LatePost[]
getDraftPosts(platform?) → LatePost[]
createPost(params) → LatePost
updatePost(postId, updates) → LatePost
deletePost(postId) → void
uploadMedia(filePath) → {url, type}
validateConnection() → {valid, profileName?, error?}
listQueues(profileId, all?) — List all queues for a profile
createQueue(params) — Create a new queue
updateQueue(params) — Update queue (supports reshuffleExisting)
deleteQueue(profileId, queueId) — Delete a queue
previewQueue(profileId, queueId?, count?) — Preview upcoming slot times
getNextQueueSlot(profileId, queueId?) — Get next available slot
Approved posts use queue-first scheduling: VidPipe resolves queueId via {platform}-{clipType} naming, sends queuedFromProfile + queueId, and relies on Late FIFO order. Manual scheduledFor is fallback-only.
Local Queue (src/services/postStore.ts)
Published posts are tracked locally in publish-queue/ (pending) and published/ (done). Each item has metadata.json with latePostId, scheduledFor, platform, accountId, status.
Scheduler (src/services/scheduler.ts)
findNextSlot(platform) finds the next available time slot by:
- Loading
schedule.json config
- Fetching booked slots from Late API + local published items
- Generating candidates in 14-day chunks, sorted chronologically
- Returning the first unbooked candidate
Operational Workflows
1. List All Scheduled Posts
curl -s -H "Authorization: Bearer $env:LATE_API_KEY" \
"https://getlate.dev/api/v1/posts?status=scheduled" | python -m json.tool
Filter by platform:
curl -s -H "Authorization: Bearer $env:LATE_API_KEY" \
"https://getlate.dev/api/v1/posts?status=scheduled&platform=linkedin"
Filter by date range:
curl -s -H "Authorization: Bearer $env:LATE_API_KEY" \
"https://getlate.dev/api/v1/posts?status=scheduled&dateFrom=2026-02-01&dateTo=2026-03-01"
2. Reschedule a Single Post
curl -s -X PUT -H "Authorization: Bearer $env:LATE_API_KEY" \
-H "Content-Type: application/json" \
"https://getlate.dev/api/v1/posts/{postId}" \
-d '{"scheduledFor": "2026-02-20T08:00:00-06:00"}'
3. Bulk Reschedule All Posts (Schedule Change)
When schedule.json changes, use this workflow:
- Read the new schedule from
schedule.json
- Fetch all scheduled posts from Late API
- For each post, compute the new optimal time slot based on the updated schedule
- Update each post with the new
scheduledFor datetime via PUT /v1/posts/{postId}
- Report what changed (old time → new time for each post)
Important: Always confirm with the user before executing bulk reschedule. Show them the proposed changes first.
Recommended: Use vidpipe sync-queues --reshuffle for automated bulk reschedule via Late queue system. Use vidpipe realign --queue or vidpipe reschedule --queue for queue-based alternatives.
4. Bulk Delete Scheduled Posts
# First list to confirm what will be deleted
curl -s -H "Authorization: Bearer $env:LATE_API_KEY" \
"https://getlate.dev/api/v1/posts?status=scheduled&platform=twitter"
# Then delete each post by ID (published posts CANNOT be deleted)
curl -s -X DELETE -H "Authorization: Bearer $env:LATE_API_KEY" \
"https://getlate.dev/api/v1/posts/{postId}"
Important: Always list and confirm with the user before deleting. Show post content previews.
5. Sync Local Queue with Late API
Compare local published/ metadata with Late API state:
- Read all local published items from
recordings/*/publish-queue/ and published/
- For each item with a
latePostId, fetch the Late post via GET /v1/posts/{postId}
- Report mismatches:
- Orphaned in Late: Posts in Late API with no matching local item
- Orphaned locally: Local items with
latePostId that no longer exists in Late
- Schedule mismatch:
scheduledFor differs between local metadata and Late API
6. Validate Late API Connection
curl -s -H "Authorization: Bearer $env:LATE_API_KEY" \
"https://getlate.dev/api/v1/profiles"
If this returns profiles, the connection is valid. If 401, the API key is invalid or expired.
7. View Publishing Logs
curl -s -H "Authorization: Bearer $env:LATE_API_KEY" \
"https://getlate.dev/api/v1/logs"
Shows publishing attempts with platform API endpoint, HTTP status, request/response bodies, and retry info. Logs are retained for 7 days.
8. Inspect Connected Accounts
curl -s -H "Authorization: Bearer $env:LATE_API_KEY" \
"https://getlate.dev/api/v1/accounts" | python -m json.tool
Returns all connected social accounts with _id, platform, username, displayName, isActive, profileId.
9. Update Late Queue Slots (with auto-reshuffle)
If you want Late's built-in queue (not the local schedule.json) to reschedule existing posts:
curl -s -X PUT -H "Authorization: Bearer $env:LATE_API_KEY" \
-H "Content-Type: application/json" \
"https://getlate.dev/api/v1/queue/slots" \
-d '{
"profileId": "PROFILE_ID",
"timezone": "America/Chicago",
"slots": [
{"day": "tue", "time": "08:00"},
{"day": "wed", "time": "12:00"}
],
"reshuffleExisting": true
}'
The reshuffleExisting: true flag tells Late to automatically move existing queued posts to match the new slots.
Safety Rules
- Never delete published posts — the API will reject it anyway (400 error)
- Always list before bulk operations — show the user what will be affected
- Confirm destructive actions — ask the user to approve before deleting or rescheduling multiple posts
- Use
dryRun for bulk uploads — test with ?dryRun=true query param first
- Respect rate limits — add delays between bulk API calls (60 req/min on free plan)
- Handle 429 responses — check
Retry-After header and wait before retrying
Troubleshooting
| Problem |
Cause |
Fix |
| 401 Unauthorized |
Invalid or expired API key |
Check LATE_API_KEY env var, regenerate key at getlate.dev settings |
| 429 Rate Limited |
Too many requests |
Wait for Retry-After period, add delays between bulk calls |
| 400 on DELETE |
Trying to delete a published post |
Published posts cannot be deleted — only draft/scheduled/failed |
| 400 on PUT |
Trying to update a published post |
Published/publishing/cancelled posts cannot be edited |
| Empty posts list |
Wrong status filter or no posts exist |
Try without status filter, check platform filter matches account |
| Media upload fails |
Presigned URL expired (1 hour) |
Request a new presigned URL and retry upload |
| Duplicate 409 |
Same content posted to same account within 24h |
Change content or wait 24 hours |
| Account cooldown |
Repeated publishing errors |
Wait for cooldown to expire (escalating: 10min → 24h) |