npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

upload-post

v2.5.0

Published

Official client library for Upload-Post API - Cross-platform social media upload for TikTok, Instagram, YouTube, LinkedIn, Facebook, Pinterest, Threads, Reddit, Bluesky, and X (Twitter)

Readme

Upload-Post SDK for Node.js

Official Node.js client for the Upload-Post API - Cross-platform social media upload.

Upload videos, photos, text posts, and documents to TikTok, Instagram, YouTube, LinkedIn, Facebook, Pinterest, Threads, Reddit, Bluesky, X (Twitter), Discord, and Telegram with a single API.

Installation

npm install upload-post

Quick Start

import { UploadPost } from 'upload-post';

const client = new UploadPost('YOUR_API_KEY');

// Upload a video to multiple platforms
const response = await client.upload('./video.mp4', {
  title: 'Check out this awesome video! 🎬',
  user: 'my-profile',
  platforms: ['tiktok', 'instagram', 'youtube']
});

console.log(response);

Features

  • Video Upload - TikTok, Instagram, YouTube, LinkedIn, Facebook, Pinterest, Threads, Bluesky, X, Discord, Telegram
  • Photo Upload - TikTok, Instagram, LinkedIn, Facebook, Pinterest, Threads, Reddit, Bluesky, X, Discord, Telegram
  • Text Posts - X, LinkedIn, Facebook, Threads, Reddit, Bluesky, Discord, Telegram
  • Document Upload - LinkedIn (PDF, PPT, PPTX, DOC, DOCX)
  • Scheduling - Schedule posts for later
  • Posting Queue - Add posts to your configured queue
  • First Comments - Auto-post first comment after publishing
  • Analytics - Get engagement metrics
  • Full TypeScript Support

API Reference

Upload Video

const response = await client.upload('./video.mp4', {
  title: 'My awesome video',
  user: 'my-profile',
  platforms: ['tiktok', 'instagram', 'youtube'],
  
  // Optional: Schedule for later
  scheduledDate: '2024-12-25T10:00:00Z',
  timezone: 'Europe/Madrid',
  
  // Optional: Add first comment
  firstComment: 'Thanks for watching! 🙏',
  
  // Optional: Platform-specific settings
  tiktokPrivacyLevel: 'PUBLIC_TO_EVERYONE',
  instagramMediaType: 'REELS',
  youtubePrivacyStatus: 'public',
  youtubeTags: ['tutorial', 'coding'],
});

Upload Photos

// Upload single or multiple photos
const response = await client.uploadPhotos(
  ['./photo1.jpg', './photo2.jpg', 'https://example.com/photo3.jpg'],
  {
    title: 'Check out these photos! 📸',
    user: 'my-profile',
    platforms: ['instagram', 'facebook', 'x'],
    
    // Optional: Add to queue instead of posting immediately
    addToQueue: true,
    
    // Platform-specific
    instagramMediaType: 'IMAGE', // or 'STORIES'
    facebookPageId: 'your-page-id',
  }
);

Upload Text Posts

const response = await client.uploadText({
  title: 'Just shipped a new feature! 🚀 Check it out at example.com',
  user: 'my-profile',
  platforms: ['x', 'linkedin', 'threads'],
  
  // Optional: Create a poll on X
  xPollOptions: ['Option A', 'Option B', 'Option C'],
  xPollDuration: 1440, // 24 hours in minutes
  
  // Optional: Post to a LinkedIn company page
  targetLinkedinPageId: 'company-page-id',
});

Upload Documents (LinkedIn)

const response = await client.uploadDocument('./presentation.pdf', {
  title: 'Q4 2024 Report',
  user: 'my-profile',
  description: 'Check out our latest quarterly results!',
  linkedinVisibility: 'PUBLIC',
  targetLinkedinPageId: 'company-page-id', // Optional: post to company page
});

Check Upload Status

For async uploads, check the status using the request_id:

const status = await client.getStatus('request_id_from_upload');
console.log(status);

For scheduled or queued posts, check the status using the job_id:

const status = await client.getJobStatus('job_id_from_scheduled_post');
console.log(status);

Get Upload History

const history = await client.getHistory({ page: 1, limit: 20 });
console.log(history.uploads);

Scheduled Posts

// List all scheduled posts
const scheduled = await client.listScheduled();

// Edit a scheduled post
await client.editScheduled('job-id', {
  scheduledDate: '2024-12-26T15:00:00Z',
  timezone: 'America/New_York',
});

// Cancel a scheduled post
await client.cancelScheduled('job-id');

User Management

// List all profiles
const users = await client.listUsers();

// Create a new profile
await client.createUser('new-profile');

// Delete a profile
await client.deleteUser('old-profile');

// Generate JWT for platform integration (white-label)
const jwt = await client.generateJwt('my-profile', {
  redirectUrl: 'https://yourapp.com/callback',
  platforms: ['tiktok', 'instagram'],
  // Optional: force the connection page language for this profile.
  // Supported: 'en' | 'es' | 'de' | 'fr' | 'pt'. When omitted, the page
  // auto-detects the visitor's browser language and falls back to English.
  language: 'es',
});

Get Analytics

const analytics = await client.getAnalytics('my-profile', {
  platforms: ['instagram', 'tiktok'],
});
console.log(analytics);

Get Media

Retrieve recent posts from a connected social account. Supported platforms: instagram, tiktok, youtube, linkedin, facebook, x, threads, pinterest, bluesky, reddit.

const { media } = await client.getMedia('linkedin', 'my-profile');

// Force the personal LinkedIn profile of an account connected as an org admin:
await client.getMedia('linkedin', 'my-profile', { pageUrn: 'me' });

// Target a specific LinkedIn organization page:
await client.getMedia('linkedin', 'my-profile', { pageUrn: '12345' });

Helper Methods

// Get Facebook pages for a profile
const fbPages = await client.getFacebookPages('my-profile');

// Get LinkedIn pages for a profile
const liPages = await client.getLinkedinPages('my-profile');

// Get Pinterest boards for a profile
const boards = await client.getPinterestBoards('my-profile');

Platform-Specific Options

TikTok (Video)

  • tiktokPrivacyLevel - PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLY
  • tiktokDisableDuet - Disable duet
  • tiktokDisableComment - Disable comments
  • tiktokDisableStitch - Disable stitch
  • tiktokCoverTimestamp - Timestamp in ms for cover
  • tiktokIsAigc - AI-generated content flag
  • tiktokPostMode - DIRECT_POST or MEDIA_UPLOAD
  • brandContentToggle - Branded content toggle
  • brandOrganicToggle - Brand organic toggle

TikTok (Photos)

  • tiktokAutoAddMusic - Auto add music
  • tiktokPhotoCoverIndex - Index of photo for cover (0-based)
  • tiktokDisableComment - Disable comments

Instagram

  • instagramMediaType - REELS, STORIES, IMAGE
  • instagramShareToFeed - Share to feed (for Reels/Stories)
  • instagramCollaborators - Comma-separated collaborator usernames
  • instagramCoverUrl - Custom cover URL
  • instagramAudioName - Audio track name
  • instagramUserTags - Comma-separated user tags
  • instagramLocationId - Location ID
  • instagramThumbOffset - Thumbnail offset

YouTube

  • youtubeTags - Array or comma-separated tags
  • youtubeCategoryId - Category ID (default: "22" People & Blogs)
  • youtubePrivacyStatus - public, unlisted, private
  • youtubeEmbeddable - Allow embedding
  • youtubeLicense - youtube, creativeCommon
  • youtubePublicStatsViewable - Show public stats
  • youtubeThumbnailUrl - Custom thumbnail URL
  • youtubeSelfDeclaredMadeForKids - Made for kids (COPPA)
  • youtubeContainsSyntheticMedia - AI/synthetic content flag
  • youtubeDefaultLanguage - Title/description language (BCP-47)
  • youtubeDefaultAudioLanguage - Audio language (BCP-47)
  • youtubeAllowedCountries / youtubeBlockedCountries - Country restrictions
  • youtubeHasPaidProductPlacement - Paid placement flag
  • youtubeRecordingDate - Recording date (ISO 8601)

LinkedIn

  • linkedinVisibility - PUBLIC, CONNECTIONS, LOGGED_IN, CONTAINER
  • targetLinkedinPageId - Page ID for organization posts

Facebook

  • facebookPageId - Facebook Page ID (required)
  • facebookVideoState - PUBLISHED, DRAFT
  • facebookMediaType - REELS, STORIES, VIDEO (VIDEO for normal page videos with no 9:16 restriction)
  • thumbnailUrl - URL for custom video thumbnail (only when facebookMediaType is VIDEO)
  • facebookLinkUrl - URL for text posts

Pinterest

  • pinterestBoardId - Board ID
  • pinterestLink - Destination link
  • pinterestAltText - Alt text for photos
  • pinterestCoverImageUrl - Cover image URL (video)
  • pinterestCoverImageKeyFrameTime - Key frame time in ms

X (Twitter)

  • xReplySettings - everyone, following, mentionedUsers, subscribers, verified
  • xNullcast - Promoted-only post
  • xTaggedUserIds - User IDs to tag
  • xPlaceId / xGeoPlaceId - Location place ID
  • xQuoteTweetId - Tweet ID to quote
  • xPollOptions - Poll options (2-4)
  • xPollDuration - Poll duration in minutes (5-10080)
  • xForSuperFollowersOnly - Exclusive for super followers
  • xCommunityId - Community ID
  • xShareWithFollowers - Share community post with followers
  • xCardUri - Card URI for Twitter Cards
  • xLongTextAsPost - Post long text as single post
  • xThreadImageLayout - Comma-separated image layout for thread (e.g. "4,4" or "2,3,1")

Threads

  • threadsLongTextAsPost - Post long text as single post (vs thread)
  • threadsThreadMediaLayout - Comma-separated list of how many media items to include in each Threads post. Each value must be 1-10, and the total must equal the number of files. Example: '5,5' splits 10 items into 2 posts with 5 each. If omitted and more than 10 items are provided, auto-chunks into groups of 10.
  • threadsTopicTag - Topic tag for the Threads post (1-50 characters, no periods or ampersands). One tag per post. Helps increase reach.

Reddit

  • redditSubreddit - Subreddit name (without r/)
  • redditFlairId - Flair template ID

Common Options

These options work across all upload methods:

| Option | Description | |--------|-------------| | title | Post title/caption (required) | | user | Profile name (required) | | platforms | Target platforms array (required) | | firstComment | First comment to post | | altText | Alt text for accessibility | | scheduledDate | ISO date for scheduling | | timezone | Timezone for scheduled date | | addToQueue | Add to posting queue | | maxPostsPerSlot | Max posts per queue slot (overrides profile setting) | | asyncUpload | Process asynchronously (default: true) | | autogenerate | If true, AI generates native per-platform title/description from the media and fills any field left empty | | autogenerateTitle / autogenerateDescription | Generate only the title or only the description with AI | | autogenerateLanguage | Force the AI output language (ISO code); omit to auto-detect from the media | | idempotencyKey | Collapses duplicate uploads within 24h. Reuse the same value when retrying. Alias: requestId |

Google Business Profile

Pass the target location on the upload itself. There is no separate "select a location" call — the API resolves the location per post.

const { locations } = await client.getGoogleBusinessLocations('myprofile');

await client.upload('video.mp4', {
  user: 'myprofile',
  platforms: ['google_business'],
  title: 'Now open on Sundays',
  gbpLocationId: locations[0].name,   // "accounts/123/locations/456"
});

gbpLocationId is required when the account has more than one location — the API only auto-selects when exactly one exists. Beyond a standard post you can publish an event or an offer:

await client.uploadText({
  user: 'myprofile',
  platforms: ['google_business'],
  title: 'Summer sale',
  gbpLocationId: locations[0].name,
  gbpTopicType: 'OFFER',
  gbpOfferCoupon: 'SUMMER25',
  gbpOfferRedeemUrl: 'https://example.com/redeem',
  gbpOfferTerms: 'One per customer',
});

Also available: gbpTopicType: 'EVENT' with gbpEventTitle / gbpEventStartDate / gbpEventStartTime / gbpEventEndDate / gbpEventEndTime, a call-to-action via gbpCtaType + gbpCtaUrl, and gbpMediaUrl / gbpMediaFormat.

Retrying an upload safely

An upload that times out may still have been accepted by the API. Retrying it without an idempotency key publishes the post a second time.

Pass the same idempotencyKey on every attempt and the API returns the original job instead of creating a new one:

import { randomUUID } from 'crypto';

const idempotencyKey = randomUUID();   // generate ONCE, outside the retry loop

for (let attempt = 0; attempt < 3; attempt++) {
  try {
    return await client.upload('video.mp4', { user, platforms: ['tiktok'], title, idempotencyKey });
  } catch (err) {
    if (attempt === 2) throw err;
  }
}

Generating the key inside the loop defeats the mechanism: each attempt would look like a new upload.

TypeScript Support

Full TypeScript support with comprehensive type definitions:

import { UploadPost, UploadVideoOptions, UploadResponse } from 'upload-post';

const client = new UploadPost('YOUR_API_KEY');

const options: UploadVideoOptions = {
  title: 'My video',
  user: 'my-profile',
  platforms: ['tiktok', 'instagram'],
  tiktokPrivacyLevel: 'PUBLIC_TO_EVERYONE',
};

const response: UploadResponse = await client.upload('./video.mp4', options);

Error Handling

try {
  const response = await client.upload('./video.mp4', options);
  console.log('Upload successful:', response);
} catch (error) {
  console.error('Upload failed:', error.message);
}

Links

License

MIT