Skip to content

JavaScript Examples

These examples demonstrate how to use the Tailwind API with JavaScript and TypeScript.

Replace the example account, board, post, media, and destination values with your own. Scheduling examples use a date in 2099 so they remain valid; choose the future date and time you actually want.

// No additional dependencies needed - fetch is built into Node.js 18+
const API_URL = 'https://api-v1.tailwind.ai';
const API_KEY = process.env.TAILWIND_API_KEY;
const headers = {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
};
async function listAccounts() {
const response = await fetch(`${API_URL}/v1/accounts`, { headers });
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const { data } = await response.json();
return data.accounts;
}
// Usage
const accounts = await listAccounts();
console.log('Accounts:', accounts);
async function listBoards(accountId) {
const response = await fetch(
`${API_URL}/v1/accounts/${accountId}/boards`,
{ headers }
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const { data } = await response.json();
return data.boards;
}
// Usage
const boards = await listBoards('123456');
console.log('Boards:', boards);
async function createPost(accountId, postData) {
const response = await fetch(
`${API_URL}/v1/accounts/${accountId}/posts`,
{
method: 'POST',
headers,
body: JSON.stringify(postData)
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(error.error?.message || 'Failed to create post');
}
const { data } = await response.json();
return data.post;
}
// Usage - Create a scheduled post
const post = await createPost('123456', {
mediaUrl: 'https://example.com/my-image.jpg',
title: 'Amazing Recipe',
description: 'Try this delicious recipe! #recipes #cooking',
url: 'https://myblog.com/recipe',
boardId: '1106196864631757445',
altText: 'A colorful plate of food',
sendAt: '2099-01-20T14:00:00Z'
});
console.log('Created post:', post.id);
console.log('Status:', post.status);
// Set mediaType to 'video' for video pins
const videoPost = await createPost('123456', {
mediaUrl: 'https://example.com/my-video.mp4',
mediaType: 'video',
title: 'Watch This Tutorial',
description: 'Step-by-step guide #tutorial #video',
url: 'https://myblog.com/tutorial',
boardId: '1106196864631757445',
sendAt: '2099-01-20T14:00:00Z'
});
console.log('Video post created:', videoPost.id);
// Omit sendAt to create a draft
const draft = await createPost('123456', {
mediaUrl: 'https://example.com/my-image.jpg',
title: 'Draft for Review',
description: 'I will schedule this later',
boardId: '1106196864631757445'
});
console.log('Draft created:', draft.id);
// draft.status will be 'draft'

If the draft doesn’t already have a boardId, provide one when scheduling:

async function schedulePost(accountId, postId, sendAt, boardId) {
const body = { sendAt };
if (boardId) body.boardId = boardId;
const response = await fetch(
`${API_URL}/v1/accounts/${accountId}/posts/${postId}/schedule`,
{
method: 'POST',
headers,
body: JSON.stringify(body)
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(error.error?.message || 'Failed to schedule post');
}
const { data } = await response.json();
return data.post;
}
// Usage
const scheduled = await schedulePost(
'123456',
'post_abc123',
'2099-01-25T09:00:00Z',
'1106196864631757445' // Required if draft has no board assigned
);
console.log('Scheduled for:', new Date(scheduled.sendAt * 1000));
async function listPosts(accountId, options = {}) {
const params = new URLSearchParams();
if (options.status) params.set('status', options.status);
if (options.limit) params.set('limit', options.limit.toString());
if (options.cursor) params.set('cursor', options.cursor);
if (options.startDate) params.set('startDate', options.startDate);
if (options.endDate) params.set('endDate', options.endDate);
const url = `${API_URL}/v1/accounts/${accountId}/posts?${params}`;
const response = await fetch(url, { headers });
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const { data } = await response.json();
return {
posts: data.posts,
cursor: data.cursor
};
}
// List queued posts (default)
const queued = await listPosts('123456');
console.log('Queued posts:', queued.posts.length);
// List drafts
const drafts = await listPosts('123456', { status: 'draft' });
console.log('Drafts:', drafts.posts.length);
// List sent posts in a date range
const sent = await listPosts('123456', {
status: 'sent',
startDate: '2024-01-01T00:00:00Z',
endDate: '2024-01-31T23:59:59Z',
limit: 100
});
console.log('Sent in January:', sent.posts.length);
async function deletePost(accountId, postId) {
const response = await fetch(
`${API_URL}/v1/accounts/${accountId}/posts/${postId}`,
{
method: 'DELETE',
headers
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(error.error?.message || 'Failed to delete post');
}
return true;
}
// Usage
await deletePost('123456', 'post_abc123');
console.log('Post deleted');
/**
* Schedule multiple posts with a delay between each
*/
async function bulkSchedule(accountId, posts) {
const results = [];
for (const postData of posts) {
try {
const post = await createPost(accountId, postData);
results.push({ success: true, post });
console.log(`Scheduled: ${post.id}`);
} catch (error) {
results.push({ success: false, error: error.message, data: postData });
console.error(`Failed:`, error.message);
}
}
return results;
}
// Usage
const postsToSchedule = [
{
mediaUrl: 'https://example.com/image1.jpg',
title: 'Post 1',
description: 'First post',
url: 'https://example.com/post-1',
boardId: '1106196864631757445',
sendAt: '2099-01-20T09:00:00Z'
},
{
mediaUrl: 'https://example.com/image2.jpg',
title: 'Post 2',
description: 'Second post',
url: 'https://example.com/post-2',
boardId: '1106196864631757445',
sendAt: '2099-01-20T14:00:00Z'
},
{
mediaUrl: 'https://example.com/image3.jpg',
title: 'Post 3',
description: 'Third post',
url: 'https://example.com/post-3',
boardId: '1106196864631757445',
sendAt: '2099-01-20T19:00:00Z'
}
];
const results = await bulkSchedule('123456', postsToSchedule);
console.log(`Scheduled ${results.filter(r => r.success).length} posts`);
interface Account {
id: string;
userId?: string;
displayName: string | null;
username: string | null;
avatarUrl: string | null;
tokenAuthorized: boolean;
isDomainVerified: boolean;
createdAt: number | null;
}
interface Board {
id: string;
name: string;
isCollaborator: boolean;
isSecret: boolean;
}
interface Post {
id: string;
status: 'draft' | 'queued' | 'sent' | 'uploading';
mediaUrl: string;
mediaType: 'image' | 'video';
title: string | null;
description: string | null;
url: string | null;
boardId: string | null;
altText: string | null;
sendAt: number | null;
sentAt: number | null;
createdAt: number;
pinId: string | null;
isSimplifiedPin: boolean;
}
interface CreatePostInput {
mediaUrl: string;
mediaType?: 'image' | 'video';
title?: string;
description?: string;
url?: string;
/** Required when sendAt is provided; use a numeric Pinterest board ID */
boardId?: string;
altText?: string;
isSimplifiedPin?: boolean;
/** Requires title, description, url, and boardId when set */
sendAt?: string;
}
async function safeApiCall(fn) {
try {
return await fn();
} catch (error) {
if (error.message.includes('401')) {
console.error('Authentication failed. Check your API key.');
} else if (error.message.includes('429')) {
console.error('Daily rate limit exceeded. Retry after midnight UTC.');
} else if (error.message.includes('404')) {
console.error('Resource not found. Check your IDs.');
} else {
console.error('API error:', error.message);
}
throw error;
}
}
// Usage
const accounts = await safeApiCall(() => listAccounts());