Code Examples
This page provides complete workflow examples for common Soku API tasks. Each example is shown in both bash/curl and JavaScript (Node.js) to help you get started quickly.Setup
Environment Variable
All examples assume your API key is stored in an environment variable:Copy
Ask AI
export SOKU_API_KEY="sk_live_your_api_key_here"
JavaScript Base Configuration
The JavaScript examples use the built-infetch API available in Node.js 18+. Here is a reusable helper function used throughout this page:
Copy
Ask AI
const SOKU_API_KEY = process.env.SOKU_API_KEY;
const BASE_URL = 'https://api.mysoku.io';
async function sokuRequest(path, options = {}) {
const url = `${BASE_URL}${path}`;
const headers = {
'Content-Type': 'application/json',
'soku-api-key': SOKU_API_KEY,
...options.headers,
};
const response = await fetch(url, {
...options,
headers,
});
if (!response.ok) {
const body = await response.json();
const error = new Error(body.error?.message || 'API request failed');
error.code = body.error?.code;
error.status = response.status;
error.requestId = body.error?.requestId;
throw error;
}
// Handle 204 No Content
if (response.status === 204) {
return null;
}
return response.json();
}
Publish a Text Post
Post a text update to multiple platforms at once.bash
Copy
Ask AI
curl -X POST https://api.mysoku.io/v1/posts \
-H "Content-Type: application/json" \
-H "soku-api-key: $SOKU_API_KEY" \
-d '{
"post": {
"content": {
"text": "Just shipped our biggest update yet. More details coming soon.",
"mediaType": "text",
"platform": ["threads", "x", "linkedin"]
}
}
}'
JavaScript
Copy
Ask AI
async function publishTextPost() {
const result = await sokuRequest('/v1/posts', {
method: 'POST',
body: JSON.stringify({
post: {
content: {
text: 'Just shipped our biggest update yet. More details coming soon.',
mediaType: 'text',
platform: ['threads', 'x', 'linkedin'],
},
},
}),
});
console.log('Post submitted:', result.postSubmissionId);
return result;
}
Upload Media and Publish a Video Post
Upload a video from a remote URL, then publish it to multiple platforms.bash
Copy
Ask AI
# Step 1: Upload the video
MEDIA_RESPONSE=$(curl -s -X POST https://api.mysoku.io/v1/media \
-H "Content-Type: application/json" \
-H "soku-api-key: $SOKU_API_KEY" \
-d '{
"url": "https://example.com/videos/product-demo.mp4"
}')
MEDIA_URL=$(echo "$MEDIA_RESPONSE" | jq -r '.url')
echo "Uploaded media: $MEDIA_URL"
# Step 2: Publish the video post
curl -X POST https://api.mysoku.io/v1/posts \
-H "Content-Type: application/json" \
-H "soku-api-key: $SOKU_API_KEY" \
-d "{
\"post\": {
\"content\": {
\"text\": \"Watch our full product walkthrough\",
\"mediaType\": \"video\",
\"videoUrl\": \"$MEDIA_URL\",
\"platform\": [
{\"platform\": \"instagram\", \"accountId\": \"ig_main\"},
{\"platform\": \"tiktok\", \"accountId\": \"tt_brand\"},
\"youtube\"
]
}
}
}"
JavaScript
Copy
Ask AI
async function uploadAndPublishVideo() {
// Step 1: Upload the video
const media = await sokuRequest('/v1/media', {
method: 'POST',
body: JSON.stringify({
url: 'https://example.com/videos/product-demo.mp4',
}),
});
console.log('Uploaded media:', media.url);
// Step 2: Publish the video post
const result = await sokuRequest('/v1/posts', {
method: 'POST',
body: JSON.stringify({
post: {
content: {
text: 'Watch our full product walkthrough',
mediaType: 'video',
videoUrl: media.url,
platform: [
{ platform: 'instagram', accountId: 'ig_main' },
{ platform: 'tiktok', accountId: 'tt_brand' },
'youtube',
],
},
},
}),
});
console.log('Post submitted:', result.postSubmissionId);
return result;
}
Publish an Image Post with Multiple Images
bash
Copy
Ask AI
curl -X POST https://api.mysoku.io/v1/posts \
-H "Content-Type: application/json" \
-H "soku-api-key: $SOKU_API_KEY" \
-d '{
"post": {
"content": {
"text": "Behind the scenes from today'\''s shoot",
"mediaType": "image",
"imageUrls": [
"https://storage.mysoku.io/media/img1.jpg",
"https://storage.mysoku.io/media/img2.jpg",
"https://storage.mysoku.io/media/img3.jpg"
],
"platform": [
{ "platform": "instagram", "accountId": "ig_main" },
{ "platform": "facebook", "accountId": "fb_page" },
"linkedin"
]
}
}
}'
JavaScript
Copy
Ask AI
async function publishImagePost() {
const result = await sokuRequest('/v1/posts', {
method: 'POST',
body: JSON.stringify({
post: {
content: {
text: "Behind the scenes from today's shoot",
mediaType: 'image',
imageUrls: [
'https://storage.mysoku.io/media/img1.jpg',
'https://storage.mysoku.io/media/img2.jpg',
'https://storage.mysoku.io/media/img3.jpg',
],
platform: [
{ platform: 'instagram', accountId: 'ig_main' },
{ platform: 'facebook', accountId: 'fb_page' },
'linkedin',
],
},
},
}),
});
console.log('Post submitted:', result.postSubmissionId);
return result;
}
Generate an OG Image and Publish
Render a template image and publish it as an image post.bash
Copy
Ask AI
# Step 1: Render the OG image
TEMPLATE_RESPONSE=$(curl -s -X POST https://api.mysoku.io/v1/templates \
-H "Content-Type: application/json" \
-H "soku-api-key: $SOKU_API_KEY" \
-d '{
"templateSlug": "tweetImage",
"config": {
"title": "5 Lessons From Building in Public",
"author": "Soku Team",
"theme": "dark"
}
}')
IMAGE_URL=$(echo "$TEMPLATE_RESPONSE" | jq -r '.url')
echo "Rendered image: $IMAGE_URL"
# Step 2: Publish the image post
curl -X POST https://api.mysoku.io/v1/posts \
-H "Content-Type: application/json" \
-H "soku-api-key: $SOKU_API_KEY" \
-d "{
\"post\": {
\"content\": {
\"text\": \"5 Lessons From Building in Public — a thread.\",
\"mediaType\": \"image\",
\"imageUrls\": [\"$IMAGE_URL\"],
\"platform\": [\"x\", \"linkedin\"]
}
}
}"
JavaScript
Copy
Ask AI
async function renderTemplateAndPublish() {
// Step 1: Render the OG image
const template = await sokuRequest('/v1/templates', {
method: 'POST',
body: JSON.stringify({
templateSlug: 'tweetImage',
config: {
title: '5 Lessons From Building in Public',
author: 'Soku Team',
theme: 'dark',
},
}),
});
console.log('Rendered image:', template.url);
// Step 2: Publish the image post
const result = await sokuRequest('/v1/posts', {
method: 'POST',
body: JSON.stringify({
post: {
content: {
text: '5 Lessons From Building in Public — a thread.',
mediaType: 'image',
imageUrls: [template.url],
platform: ['x', 'linkedin'],
},
},
}),
});
console.log('Post submitted:', result.postSubmissionId);
return result;
}
Transcribe a Video and Repurpose as Text
Transcribe a video file and publish the transcript as a text post on other platforms.bash
Copy
Ask AI
# Step 1: Upload the video
MEDIA_RESPONSE=$(curl -s -X POST https://api.mysoku.io/v1/media \
-H "Content-Type: application/json" \
-H "soku-api-key: $SOKU_API_KEY" \
-d '{
"url": "https://example.com/podcasts/episode-42.mp4"
}')
MEDIA_URL=$(echo "$MEDIA_RESPONSE" | jq -r '.url')
# Step 2: Transcribe the video (with idempotency key)
TRANSCRIPTION=$(curl -s -X POST https://api.mysoku.io/v1/ai/transcribe \
-H "Content-Type: application/json" \
-H "soku-api-key: $SOKU_API_KEY" \
-H "Idempotency-Key: ep42-transcribe-$(date +%Y%m%d)" \
-d '{
"mediaId": "media_uploaded_id_here",
"language": "en",
"model": "whisper-1",
"durationSeconds": 600
}')
TRANSCRIPT_TEXT=$(echo "$TRANSCRIPTION" | jq -r '.transcript')
CREDITS_USED=$(echo "$TRANSCRIPTION" | jq -r '.creditsDebited')
echo "Transcription complete. Credits used: $CREDITS_USED"
# Step 3: Publish the transcript as a text post
curl -X POST https://api.mysoku.io/v1/posts \
-H "Content-Type: application/json" \
-H "soku-api-key: $SOKU_API_KEY" \
-d "{
\"post\": {
\"content\": {
\"text\": \"Key takeaways from Episode 42:\n\n$TRANSCRIPT_TEXT\",
\"mediaType\": \"text\",
\"platform\": [\"threads\", \"linkedin\"]
}
}
}"
JavaScript
Copy
Ask AI
async function transcribeAndRepurpose() {
// Step 1: Upload the video
const media = await sokuRequest('/v1/media', {
method: 'POST',
body: JSON.stringify({
url: 'https://example.com/podcasts/episode-42.mp4',
}),
});
// Step 2: Transcribe the video
const transcription = await sokuRequest('/v1/ai/transcribe', {
method: 'POST',
headers: {
'Idempotency-Key': `ep42-transcribe-${new Date().toISOString().slice(0, 10)}`,
},
body: JSON.stringify({
mediaId: 'media_uploaded_id_here',
language: 'en',
model: 'whisper-1',
durationSeconds: 600,
}),
});
console.log(`Transcription complete. Credits used: ${transcription.creditsDebited}`);
// Step 3: Publish the transcript as a text post
const result = await sokuRequest('/v1/posts', {
method: 'POST',
body: JSON.stringify({
post: {
content: {
text: `Key takeaways from Episode 42:\n\n${transcription.transcript}`,
mediaType: 'text',
platform: ['threads', 'linkedin'],
},
},
}),
});
console.log('Post submitted:', result.postSubmissionId);
return result;
}
Schedule a Post for Later
Schedule a post to be published at a specific future time.bash
Copy
Ask AI
curl -X POST https://api.mysoku.io/v1/posts \
-H "Content-Type: application/json" \
-H "soku-api-key: $SOKU_API_KEY" \
-d '{
"post": {
"content": {
"text": "Happy Monday! Here is your weekly dose of motivation.",
"mediaType": "text",
"platform": ["threads", "x", "linkedin"]
}
},
"scheduledTime": "2026-03-02T14:00:00.000Z"
}'
JavaScript
Copy
Ask AI
async function schedulePost() {
const result = await sokuRequest('/v1/posts', {
method: 'POST',
body: JSON.stringify({
post: {
content: {
text: 'Happy Monday! Here is your weekly dose of motivation.',
mediaType: 'text',
platform: ['threads', 'x', 'linkedin'],
},
},
scheduledTime: '2026-03-02T14:00:00.000Z',
}),
});
console.log('Post scheduled:', result.postSubmissionId);
return result;
}
Manage Repurpose Links
Create, list, and delete repurpose links for automated content repurposing.bash
Copy
Ask AI
# List existing repurpose links
curl -s -X GET "https://api.mysoku.io/v1/repurposeLinks?platform=instagram" \
-H "soku-api-key: $SOKU_API_KEY" | jq
# Create a new repurpose link
curl -X POST https://api.mysoku.io/v1/repurposeLinks \
-H "Content-Type: application/json" \
-H "soku-api-key: $SOKU_API_KEY" \
-d '{
"platform": "instagram",
"accountId": "ig_main",
"type": "post",
"templateId": "tmpl_f7e8d9c0",
"settings": {
"includeCaption": true,
"addHashtags": false
}
}'
# Delete a repurpose link
curl -X DELETE https://api.mysoku.io/v1/repurposeLinks/rl_a1b2c3d4 \
-H "soku-api-key: $SOKU_API_KEY"
JavaScript
Copy
Ask AI
async function manageRepurposeLinks() {
// List existing links filtered by platform
const links = await sokuRequest('/v1/repurposeLinks?platform=instagram');
console.log(`Found ${links.length} repurpose links`);
// Create a new repurpose link
const newLink = await sokuRequest('/v1/repurposeLinks', {
method: 'POST',
body: JSON.stringify({
platform: 'instagram',
accountId: 'ig_main',
type: 'post',
templateId: 'tmpl_f7e8d9c0',
settings: {
includeCaption: true,
addHashtags: false,
},
}),
});
console.log('Created repurpose link:', newLink.id);
// Delete a repurpose link
await sokuRequest(`/v1/repurposeLinks/${newLink.id}`, {
method: 'DELETE',
});
console.log('Deleted repurpose link:', newLink.id);
}
Error Handling with Retry Logic
A production-ready request function with automatic retry for transient errors.JavaScript
Copy
Ask AI
const SOKU_API_KEY = process.env.SOKU_API_KEY;
const BASE_URL = 'https://api.mysoku.io';
async function sokuRequestWithRetry(path, options = {}, maxRetries = 3) {
const url = `${BASE_URL}${path}`;
const headers = {
'Content-Type': 'application/json',
'soku-api-key': SOKU_API_KEY,
...options.headers,
};
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, {
...options,
headers,
});
// Log rate limit status
const remaining = response.headers.get('X-RateLimit-Remaining');
const limit = response.headers.get('X-RateLimit-Limit');
if (remaining && limit) {
console.log(`Rate limit: ${remaining}/${limit} remaining`);
}
// Handle rate limiting
if (response.status === 429) {
if (attempt === maxRetries) {
const body = await response.json();
throw new Error(
`Rate limit exceeded after ${maxRetries} retries (requestId: ${body.error?.requestId})`
);
}
const retryAfter = parseInt(response.headers.get('Retry-After'), 10) || 1;
const jitter = Math.random() * 1000;
const delay = retryAfter * 1000 + jitter;
console.log(`Rate limited. Waiting ${Math.round(delay / 1000)}s before retry ${attempt + 1}...`);
await new Promise((resolve) => setTimeout(resolve, delay));
continue;
}
// Handle server errors with retry
if (response.status >= 500) {
if (attempt === maxRetries) {
const body = await response.json();
throw new Error(
`Server error after ${maxRetries} retries: ${body.error?.code} (requestId: ${body.error?.requestId})`
);
}
const delay = Math.pow(2, attempt) * 1000 + Math.random() * 1000;
console.log(`Server error. Waiting ${Math.round(delay / 1000)}s before retry ${attempt + 1}...`);
await new Promise((resolve) => setTimeout(resolve, delay));
continue;
}
// Handle client errors (no retry)
if (!response.ok) {
const body = await response.json();
const error = new Error(body.error?.message || 'API request failed');
error.code = body.error?.code;
error.status = response.status;
error.requestId = body.error?.requestId;
error.details = body.error?.details;
throw error;
}
// Handle 204 No Content
if (response.status === 204) {
return null;
}
return response.json();
}
}
// Usage
async function main() {
try {
const result = await sokuRequestWithRetry('/v1/posts', {
method: 'POST',
body: JSON.stringify({
post: {
content: {
text: 'Published with production-grade error handling.',
mediaType: 'text',
platform: ['threads'],
},
},
}),
});
console.log('Post submitted:', result.postSubmissionId);
} catch (error) {
if (error.code === 'missing_integrations') {
console.error('Connect the required platforms in Settings > Integrations.');
} else if (error.code === 'insufficient_credits') {
console.error('Purchase more credits at Account > Credits.');
} else {
console.error(`Unexpected error: ${error.message} (requestId: ${error.requestId})`);
}
}
}
Batch Publishing Workflow
Publish multiple posts sequentially with rate limit awareness.JavaScript
Copy
Ask AI
async function batchPublish(posts) {
const results = [];
for (const post of posts) {
try {
const result = await sokuRequestWithRetry('/v1/posts', {
method: 'POST',
body: JSON.stringify(post),
});
results.push({ status: 'success', postSubmissionId: result.postSubmissionId });
console.log(`Published: ${result.postSubmissionId}`);
} catch (error) {
results.push({ status: 'error', code: error.code, message: error.message });
console.error(`Failed: ${error.code} - ${error.message}`);
}
// Small delay between requests to avoid hitting rate limits
await new Promise((resolve) => setTimeout(resolve, 200));
}
const succeeded = results.filter((r) => r.status === 'success').length;
const failed = results.filter((r) => r.status === 'error').length;
console.log(`Batch complete: ${succeeded} succeeded, ${failed} failed`);
return results;
}
// Usage
const posts = [
{
post: {
content: {
text: 'Monday motivation: consistency beats perfection.',
mediaType: 'text',
platform: ['threads', 'x'],
},
},
scheduledTime: '2026-03-02T09:00:00.000Z',
},
{
post: {
content: {
text: 'Tuesday tip: automate what you repeat.',
mediaType: 'text',
platform: ['threads', 'x'],
},
},
scheduledTime: '2026-03-03T09:00:00.000Z',
},
{
post: {
content: {
text: 'Wednesday wisdom: ship it, then improve.',
mediaType: 'text',
platform: ['threads', 'x'],
},
},
scheduledTime: '2026-03-04T09:00:00.000Z',
},
];
batchPublish(posts);
API Key Rotation
Programmatically rotate an API key. This example uses Firebase ID token authentication for the key management endpoints.bash
Copy
Ask AI
FIREBASE_TOKEN="eyJhbGciOiJSUzI1NiIs..."
# Step 1: Create a new key
NEW_KEY_RESPONSE=$(curl -s -X POST https://api.mysoku.io/v1/api-keys \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $FIREBASE_TOKEN" \
-d '{"name": "Production Server (rotated)"}')
NEW_KEY=$(echo "$NEW_KEY_RESPONSE" | jq -r '.apiKey')
echo "New key created"
# Step 2: Verify the new key works
curl -s -X GET https://api.mysoku.io/v1/repurposeLinks \
-H "soku-api-key: $NEW_KEY" | jq '.| length'
# Step 3: Update your application environment variable
echo "Update SOKU_API_KEY to: $NEW_KEY"
# Step 4: Revoke the old key
OLD_KEY_ID="key_old_id_here"
curl -X DELETE "https://api.mysoku.io/v1/api-keys/$OLD_KEY_ID" \
-H "Authorization: Bearer $FIREBASE_TOKEN"
echo "Old key revoked: $OLD_KEY_ID"
JavaScript
Copy
Ask AI
async function rotateApiKey(firebaseToken, oldKeyId) {
const headers = {
'Content-Type': 'application/json',
Authorization: `Bearer ${firebaseToken}`,
};
// Step 1: Create a new key
const createResponse = await fetch('https://api.mysoku.io/v1/api-keys', {
method: 'POST',
headers,
body: JSON.stringify({ name: 'Production Server (rotated)' }),
});
const { apiKey: newApiKey } = await createResponse.json();
console.log('New key created');
// Step 2: Verify the new key works
const testResponse = await fetch('https://api.mysoku.io/v1/repurposeLinks', {
headers: { 'soku-api-key': newApiKey },
});
if (!testResponse.ok) {
throw new Error('New key verification failed');
}
console.log('New key verified successfully');
// Step 3: Revoke the old key
await fetch(`https://api.mysoku.io/v1/api-keys/${oldKeyId}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${firebaseToken}` },
});
console.log('Old key revoked:', oldKeyId);
// Return the new key for your application to use
return newApiKey;
}
Next Steps
| Topic | Description |
|---|---|
| Introduction | API overview and base URL |
| Authentication | API key setup and security |
| Posts API | Full endpoint reference for post creation |
| Error Handling | Complete error code reference |
| Rate Limits | Rate limit tiers and best practices |