Edits the title, texts, media, tags and items, or moves the post to drafts or to the queue. Send only what changed; everything else stays. A YouTube item that is already uploaded and still scheduled can also change its time and metadata (checked live on YouTube).
HeadersAuthorization: Bearer praeko_… and X-Praeko-Contract: en. Always send both.
Parameters
Name
In
Description
id
path
Post id.
Idempotency-Key
header
Idempotency key (1 to 255 visible characters, for example a UUID). Repeating the SAME request with the same key within 24 h returns the stored response (with Idempotent-Replayed: true) without running it again; a different request with the same key returns 422 idempotency_key_reuse; a request still in progress returns 409 idempotency_in_progress. 5xx errors are not stored. The key is scoped per workspace and per route. See Retry a request safely.
Request body
JSON. Required fields must be sent; the others can be left out. Send only what changes. A post adopted from Buffer only accepts text, time and draft changes (anything else returns 409 post_adopted). YouTube, already uploaded: a YouTube item in the scheduled state (Praeko has already uploaded the video as private, with publishAt in the future) or private can change scheduledFor (it becomes videos.update with status.publishAt; the rest of the status is preserved), title, description (texts.youtube), videoTags, categoryId and the pinnedComment that has not been posted yet. Before acting, the server checks LIVE on YouTube (videos.list, 1 quota unit; each write costs 50). If the video was already published, the scheduled time has passed, the video was deleted or it was not uploaded by Praeko, nothing is written and the response is 409 with the reason and the Studio link (https://studio.youtube.com/video/<id>/edit). The new time must be at least 5 minutes from now (otherwise 422 time_in_past). Asking for the same time again does not call YouTube. A PATCH with several items applies each one independently and returns itemResults (one per already uploaded YouTube item: applied, changed, fields, notApplied, reason, message, publishAt, studio); if NONE of the requested items could change, the response is the 409 of the first one. Pending Instagram and TikTok items follow the usual rules.
Field
Type
Description
title (optional)
string
New title. 1 to 200 characters.
texts (optional)
object
Per-network texts that change. The fields you send replace the current ones; the others stay. A text cannot be empty or contain a placeholder such as [LINK_DO_LONGO].
texts.youtube (optional)
object
YouTube texts.
texts.youtube.title (optional)
string
Title.
texts.youtube.text (optional)
string
Description.
texts.youtube.pinnedComment (optional)
string
Pinned comment.
texts.instagram (optional)
object
Instagram texts.
texts.instagram.text (optional)
string
Caption.
texts.instagram.pinnedComment (optional)
string
Pinned comment.
texts.tiktok (optional)
object
TikTok texts.
texts.tiktok.text (optional)
string
Caption.
media (optional)
object
Media replacement.
media.cover (optional)
string
Id of the new cover (or null to remove it).
media.video (optional)
string
Id of the new video (only for an adopted post or one still in draft; otherwise 409 media_locked).
tags (optional)
string[]
Tags (replace the current ones).
items (optional)
object[]
Items to edit, selected by id or by network. Only items that are still pending can be edited.
items[].id (optional)
string
Item id.
items[].network (optional)
string
Network of the item (alternative to id). Values: youtube, instagram, tiktok.
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Patch, "https://praeko.app/v1/posts/01KA3W2Y6H1RC5ZQ8M0T4VXE7N");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("PRAEKO_KEY"));
request.Headers.Add("X-Praeko-Contract", "en");
request.Content = new StringContent("""
{
"title": "The test that only failed on Mondays (final cut)",
"tags": [
"behind-the-scenes",
"testing"
],
"items": [
{
"network": "youtube",
"scheduledFor": "2026-11-10T20:30:00.000Z"
}
]
}
""", Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Main {
public static void main(String[] args) throws Exception {
String body = """
{
"title": "The test that only failed on Mondays (final cut)",
"tags": [
"behind-the-scenes",
"testing"
],
"items": [
{
"network": "youtube",
"scheduledFor": "2026-11-10T20:30:00.000Z"
}
]
}
""";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://praeko.app/v1/posts/01KA3W2Y6H1RC5ZQ8M0T4VXE7N"))
.header("Authorization", "Bearer " + System.getenv("PRAEKO_KEY"))
.header("X-Praeko-Contract", "en")
.header("Content-Type", "application/json")
.method("PATCH", HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode() + " " + response.body());
}
}
http PATCH "https://praeko.app/v1/posts/01KA3W2Y6H1RC5ZQ8M0T4VXE7N" \
"Authorization: Bearer $PRAEKO_KEY" \
"X-Praeko-Contract: en" \
"Content-Type: application/json" \
--raw='{
"title": "The test that only failed on Mondays (final cut)",
"tags": [
"behind-the-scenes",
"testing"
],
"items": [
{
"network": "youtube",
"scheduledFor": "2026-11-10T20:30:00.000Z"
}
]
}'
Official SDK for NodeInstall it with npm install @praeko/sdk (see SDK and CLI).
No official SDKExample with the language's standard library (or HTTP client). Only the JavaScript SDK exists.
No official SDKExample with the language's standard library (or HTTP client). Only the JavaScript SDK exists.
No official SDKExample with the language's standard library (or HTTP client). Only the JavaScript SDK exists.
No official SDKExample with the language's standard library (or HTTP client). Only the JavaScript SDK exists.
No official SDKExample with the language's standard library (or HTTP client). Only the JavaScript SDK exists.
No official SDKExample with the language's standard library (or HTTP client). Only the JavaScript SDK exists.
Response
Success status
When
200
Success.
Response 200
{
"id": "01KA3W2Y6H1RC5ZQ8M0T4VXE7N",
"title": "The test that only failed on Mondays (final cut)",
"state": "queued",
"type": "short",
"kind": "video",
"origin": "api",
"media": {
"video": "01KA3VQ7M2T9XE4HB8N5RZ6C0D",
"cover": null
},
"texts": {
"youtube": {
"title": "The test that only failed on Mondays",
"text": "A test that passed from Tuesday to Sunday. What it was hiding.\n\n#programming #testing"
}
},
"tags": [
"behind-the-scenes",
"testing"
],
"thumb": null,
"items": [
{
"id": "01KA3W2Y6J9D3BNF7G2K5PQS8A",
"network": "youtube",
"channel": "01KA3V0R5D8M2WQ7X4HT9NZ1BF",
"state": "scheduled",
"scheduledFor": "2026-11-10T20:30:00.000Z",
"custom": true,
"mode": "scheduled",
"runAt": "2026-11-10T19:30:00.000Z",
"thumb": null
}
],
"createdAt": "2026-11-07T14:02:11.482Z",
"updatedAt": "2026-11-07T15:40:03.120Z",
"actions": []
}
Errors
Status
code
When
400
empty_body
The request body is empty.
400
invalid_json
The request body is not a JSON object.
413
body_too_large
The request body is larger than 1 MB.
404
post_not_found
The post does not exist.
404
item_not_found
The item does not exist in this post.
409
not_editable
The item has already gone out or is in progress (YouTube, already uploaded: it can only change while the video is still private and scheduled; once the scheduled time has passed, it can no longer change).
409
post_adopted
Post adopted from Buffer: only text, time and draft can change.
409
media_locked
The video can only be replaced in an adopted post or in a draft; the image of an image post cannot be replaced by PATCH.
409
video_not_private
YouTube, already uploaded: the video is already public on YouTube, so its time and metadata can only change in Studio (details.studio has the link).
409
video_not_found
YouTube, already uploaded: the video was deleted on YouTube.
422
time_in_past
The new time must be at least 5 minutes from now.
502
youtube_error
YouTube did not respond or rejected the check/write; nothing was changed (or itemResults says what was).
422
invalid_field
A field is invalid.
422
idempotency_key_reuse
The Idempotency-Key was already used with a different request (different method, path, query or body).
409
idempotency_in_progress
A request with the same Idempotency-Key is still in progress (retry in a moment).
409
idempotency_response_too_large
The original response was larger than 256 KB and was not stored; fetch the resource with GET instead.
Errors on any route
Status
code
When
401
unauthorized
The Authorization: Bearer … header is missing.
401
invalid_api_key
The key does not exist.
401
revoked_api_key
The key has been revoked.
401
expired_api_key
The key has expired.
403
forbidden
The key's scope is not enough for this route (see the scope badge).
429
rate_limited
The per-minute request limit was exceeded.
The body of every error is { "error": { "code", "message", "details"? } }. See all error codes.
Try it
Try it
The live test uses your key and your account. It stays off when you are not signed in.