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

ugcinc

v4.1.32

Published

TypeScript/JavaScript client for the UGC Inc API

Readme

ugcinc

Official TypeScript/JavaScript client for the UGC Inc API.

Installation

npm install ugcinc

Quick Start

import { UGCClient } from 'ugcinc';

const client = new UGCClient({
  apiKey: 'your-api-key-here'
});

// Example: Get accounts
const response = await client.accounts.getAccounts();

if (response.ok) {
  console.log(response.data);
}

Get Your API Key

To get your API key:

  1. Visit ugc.inc
  2. Schedule a call with our team
  3. You'll receive your API key after the call

Important: Keep your API key secure! Never commit it to version control or expose it in client-side code.

Documentation

For complete API documentation, including all endpoints, parameters, and examples, visit:

docs.ugc.inc

API Features

Accounts

  • Get accounts with filters (tag, org_group, user_group, status)
  • Get account status (tasks)
  • Update account info (tags, groups, keywords, profiles)
  • Update social profile (avatar, nickname, bio)
  • Delete all posts from accounts (automated batch deletion)
  • Reset warmup activity (reset or delete tasks)

Posts

  • Create video posts (with specific account or auto-select)
  • Create slideshow posts (with specific account or auto-select)
  • Auto-selection modes:
    • strict: true - Picks least recently posted account, must post at exact time
    • strict: false - Picks account with closest available time slot
  • Auto-account selection supports filtering by tag, user_group, and org_group
  • Get post status (includes platform URL when complete - TikTok or Instagram)
  • Get posts with filters (by accountIds, postIds, date range)
  • Get post statistics

Comments

  • Create comment tasks (comment on TikTok posts using your accounts)
  • Get comment task status (check if comment was posted successfully)
  • Returns comment URL when completed

Tasks

  • Get scheduled tasks
  • View task history
  • Retry tasks
  • Delete tasks

Statistics

  • Get account statistics (latest or date range)
  • Get post statistics (latest or date range)
  • Get daily aggregated stats (for charts/dashboards)
  • Get top accounts by metric (followers, views, likes, etc.)
  • Get top posts by metric (views, likes, comments, etc.)
  • Refresh statistics

Organization

  • Get API keys (metadata only, excludes key values for security)
  • Delete API keys
  • Edit API key names

Render

  • Render videos from editor configurations (supports video, audio, text, image segments)
  • Render static images from editor configurations (supports text and image segments only)
  • Check render job status
  • Download rendered files (MP4 for videos, PNG/JPEG for images)
  • Type-safe methods enforce correct segment types at compile time

Examples

Get accounts with filters:

// Get all accounts
const response = await client.accounts.getAccounts();

// Get accounts by status
const response = await client.accounts.getAccounts({
  status: 'pending' // 'pending' | 'initialized' | 'setup' | 'error'
});

// Get accounts with multiple filters
const response = await client.accounts.getAccounts({
  tag: 'production',
  org_group: 'group1',
  status: 'setup'
});

if (response.ok) {
  response.data.forEach(account => {
    console.log(`Account ${account.id}: ${account.username} (${account.status})`);
  });
}

Update account info (tags, groups, keywords, profiles, description, warmup version):

// Update multiple accounts at once
const response = await client.accounts.updateInfo({
  updates: [
    {
      accountId: 'account-uuid-1',
      tag: 'production',
      org_group: 'team-a',
      user_group: 'creators',
      keywords: 'fitness,health,workout',
      profiles: '@fitinfluencer1,@healthguru2',
      description: 'health and wellness content',
      warmupVersion: 'v1_smart'  // 'original' or 'v1_smart'
    },
    {
      accountId: 'account-uuid-2',
      tag: 'staging',
      warmupVersion: 'original'
    }
  ]
});

if (response.ok) {
  console.log(`Updated ${response.data.successful} accounts`);
  if (response.data.failed > 0) {
    console.log('Failed updates:', response.data.results.filter(r => !r.success));
  }
}

Update social profile (avatar, nickname, bio):

// Update social profiles for multiple accounts at once
// Note: Rate limits apply per account:
// - Nickname: once every 7 days
// - Avatar: once every 24 hours
// - Bio: once every 24 hours
const response = await client.accounts.updateSocial({
  updates: [
    {
      accountId: 'account-uuid-1',
      nickName: 'Cool Creator ✨',
      bio: 'Content creator 🎥 | Follow for daily tips'
    },
    {
      accountId: 'account-uuid-2',
      avatarUrl: 'https://example.com/new-avatar.jpg'
    }
  ]
});

if (response.ok) {
  console.log(`Profile updates scheduled for ${response.data.successful} accounts`);
  if (response.data.failed > 0) {
    response.data.results.filter(r => !r.success).forEach(r => {
      console.log(`Account ${r.accountId} failed: ${r.error}`);
    });
  }
}

Reset warmup activity:

// Reset warmup (tasks will re-run)
const response = await client.accounts.resetWarmup({
  accountId: 'account-uuid',
  delete_activity: false  // Default: resets run count so tasks re-execute
});

if (response.ok) {
  console.log(response.data.message); // "Successfully reset warmup for account (tasks will re-run)"
  console.log(`Tasks affected: ${response.data.tasks_affected}`);
  console.log(`Action: ${response.data.action}`); // "reset"
}

// Delete all warmup activity (removes tasks completely)
const response = await client.accounts.resetWarmup({
  accountId: 'account-uuid',
  delete_activity: true  // Deletes all warmup tasks
});

if (response.ok) {
  console.log(response.data.message); // "Successfully deleted all warmup activity for account"
  console.log(`Tasks affected: ${response.data.tasks_affected}`);
  console.log(`Action: ${response.data.action}`); // "deleted"
}

Delete all posts from accounts:

// Delete all posts from one or more accounts
// This creates a clear_posts task that automatically:
// - Finds all complete posts with a social_id
// - Deletes them one by one from the platform
// - Verifies each deletion using the platform API
// - Continues until all posts are deleted
const response = await client.accounts.deleteAllPosts({
  accountIds: ['account-uuid-1', 'account-uuid-2']
});

if (response.ok) {
  console.log(`Deletion scheduled for ${response.data.successful} account(s)`);
  
  if (response.data.failed > 0) {
    console.log(`Failed for ${response.data.failed} account(s)`);
    response.data.errors?.forEach(err => {
      console.log(`  - ${err.accountId}: ${err.error}`);
    });
  }
}

// Monitor deletion progress using account status
const statusResponse = await client.accounts.getStatus({
  accountIds: ['account-uuid-1']
});

if (statusResponse.ok) {
  const clearPostsTask = statusResponse.data.find(task => task.type === 'clear_posts');
  if (clearPostsTask) {
    console.log(`Deletion task status: ${clearPostsTask.status}`);
  }
}

Get your organization's API keys:

const response = await client.org.getApiKeys(); // POST /org/api-key

if (response.ok) {
  console.log(response.data); // [{ id: "...", name: "...", created_at: "..." }]
}

Delete an API key:

const response = await client.org.deleteApiKey({
  apiKeyId: 'api-key-uuid'
}); // POST /org/api-key/delete

if (response.ok) {
  console.log('API key deleted:', response.data.id);
}

Edit an API key's name:

const response = await client.org.editApiKey({
  apiKeyId: 'api-key-uuid',
  name: 'New Key Name'
}); // POST /org/api-key/edit

if (response.ok) {
  console.log('Updated API key:', response.data); // { id, name, created_at }
}

Create a video post:

// Create video post with specific account
const response = await client.posts.createVideo({
  accountId: 'account-uuid',
  videoUrl: 'https://example.com/video.mp4',
  caption: 'Check out this video!',
});

// Auto-select with strict=true: picks least recently posted account, must post at exact time
const response = await client.posts.createVideo({
  accountId: null,
  videoUrl: 'https://example.com/video.mp4',
  caption: 'Auto-selected account!',
  postTime: '2024-01-15T10:00:00Z',
  strict: true  // Will error if that account can't post at exactly 10:00 AM
});

// Auto-select with strict=false: picks account with closest available time slot
const response = await client.posts.createVideo({
  accountId: null,
  videoUrl: 'https://example.com/video.mp4',
  caption: 'Posts as soon as possible!',
  postTime: '2024-01-15T10:00:00Z',
  strict: false  // Finds account that can post closest to 10:00 AM
});

// Auto-select with filters (tag, user_group, org_group)
const response = await client.posts.createVideo({
  accountId: null,
  videoUrl: 'https://example.com/video.mp4',
  caption: 'Filtered auto-selection!',
  tag: 'production',
  org_group: 'group1',
  strict: false
});

Create a slideshow post:

// Create slideshow with specific account
const response = await client.posts.createSlideshow({
  accountId: 'account-uuid',
  imageUrls: [
    'https://example.com/image1.jpg',
    'https://example.com/image2.jpg',
    'https://example.com/image3.jpg'
  ],
  caption: 'Amazing slideshow!',
});

// Auto-select with strict=true: picks least recently posted account
const response = await client.posts.createSlideshow({
  accountId: null,
  imageUrls: ['image1.jpg', 'image2.jpg'],
  caption: 'Posts at exact time or errors',
  postTime: '2024-01-15T14:00:00Z',
  strict: true,
  user_group: 'creators'
});

// Auto-select with strict=false: picks account with closest available time
const response = await client.posts.createSlideshow({
  accountId: null,
  imageUrls: ['image1.jpg', 'image2.jpg'],
  caption: 'Posts at nearest available time',
  postTime: '2024-01-15T14:00:00Z',
  strict: false
});

Get posts with filters:

// Get specific posts by ID
const response = await client.posts.getPosts({
  postIds: ['post-uuid-1', 'post-uuid-2']
});

// Get posts by account
const response = await client.posts.getPosts({
  accountIds: ['account-uuid']
});

if (response.ok) {
  response.data.forEach(post => {
    console.log(`Post ${post.id}: ${post.status}`);
    // For complete posts, postUrl will contain the platform URL
    // TikTok: https://www.tiktok.com/@username/video/social_id
    // Instagram: https://www.instagram.com/p/social_id/
    if (post.postUrl) {
      console.log(`Watch at: ${post.postUrl}`);
    }
  });
}

Check post status:

const response = await client.posts.getStatus({
  postId: 'post-uuid'
});

if (response.ok) {
  console.log(`Status: ${response.data.status}`);
  // postUrl is available when status is "complete"
  // Format depends on platform (TikTok or Instagram)
  if (response.data.postUrl) {
    console.log(`Post URL: ${response.data.postUrl}`);
  }
}

Create a comment task:

// Create a comment on a TikTok post
const response = await client.comments.createComment({
  accountId: 'account-uuid',
  postUrl: 'https://www.tiktok.com/@user/video/1234567890',
  commentText: 'Great video! 🔥'
});

if (response.ok) {
  console.log('Comment task created:', response.data.taskId);
  // Use taskId to check status later
}

Check comment task status:

const response = await client.comments.getStatus({
  taskId: 'task-uuid'
});

if (response.ok) {
  console.log(`Status: ${response.data.status}`);
  
  if (response.data.status === 'completed') {
    console.log(`Comment URL: ${response.data.commentUrl}`);
  } else if (response.data.status === 'failed') {
    console.log(`Error: ${response.data.error}`);
  } else if (response.data.status === 'pending' || response.data.status === 'running') {
    console.log('Comment is still being processed...');
  }
}

Retry tasks:

const response = await client.tasks.retryTasks({
  taskIds: ['task-uuid-1', 'task-uuid-2']
});

if (response.ok) {
  console.log(`Retried ${response.data.retried} task(s)`);
}

Delete tasks:

const response = await client.tasks.deleteTasks({
  taskIds: ['task-uuid-1', 'task-uuid-2']
});

if (response.ok) {
  console.log(`Deleted ${response.data.deleted} task(s)`);
}

Render a video:

// Submit a video render job
const videoJob = await client.render.renderVideo({
  editor: {
    width: 1080,
    height: 1920,
    fps: 30,
    duration: 5000,
    channels: [
      {
        id: 'background',
        segments: [
          {
            id: 'bg-video',
            type: 'video',  // All segment types allowed for video
            source: 'https://example.com/video.mp4',
            order: 0,
            offset: { type: 'absolute', value: 0 },
            xOffset: 0,
            yOffset: 0,
            width: 1080,
            height: 1920
          }
        ]
      },
      {
        id: 'text-layer',
        segments: [
          {
            id: 'title',
            type: 'text',
            source: '',
            text: 'My Video',
            order: 0,
            offset: { type: 'absolute', value: 0 },
            fontSize: 80,
            color: '#FFFFFF'
          }
        ]
      }
    ]
  }
});

// Poll for completion
let status;
do {
  await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds
  const statusResponse = await client.render.getStatus(videoJob.data.job_id);
  if (statusResponse.ok) {
    status = statusResponse.data;
    console.log(`Status: ${status.status} - ${status.message}`);
  }
} while (status?.status === 'pending' || status?.status === 'processing');

if (status?.status === 'completed') {
  console.log('Video ready:', status.download_url);
}

Render a static image:

// Submit an image render job (text and images only, no video/audio)
const imageJob = await client.render.renderImage({
  editor: {
    width: 1080,
    height: 1920,
    fps: 30,  // Required but not used for images
    duration: 1000,  // Required but not used for images
    channels: [
      {
        id: 'background',
        segments: [
          {
            id: 'bg-image',
            type: 'image',  // Only image and text segments allowed
            source: 'https://example.com/background.jpg',
            order: 0,
            offset: { type: 'absolute', value: 0 },
            xOffset: 0,
            yOffset: 0,
            width: 1080,
            height: 1920
          }
        ]
      },
      {
        id: 'text-layer',
        segments: [
          {
            id: 'title',
            type: 'text',
            source: '',
            text: 'Thumbnail Title',
            order: 0,
            offset: { type: 'absolute', value: 0 },
            fontSize: 100,
            fontWeight: 'bold',
            color: '#FFFFFF',
            strokeColor: '#000000',
            strokeWidth: 4
          }
        ]
      }
    ]
  },
  image_format: 'png'  // or 'jpeg'
});

// Check status
const statusResponse = await client.render.getStatus(imageJob.data.job_id);
if (statusResponse.ok && statusResponse.data.status === 'completed') {
  console.log('Image ready:', statusResponse.data.download_url);
}

Statistics:

// Get latest stats (one record per account/post)
const latestAccountStats = await client.stats.getAccountStats();
const latestPostStats = await client.stats.getPostStats();

// Get stats for a date range
const rangeStats = await client.stats.getAccountStats({
  startDate: '2024-01-01',
  endDate: '2024-01-31',
  tag: 'fitness'
});

// Get daily aggregated stats for charts
const dailyStats = await client.stats.getDailyAggregated({
  startDate: '2024-01-01',
  endDate: '2024-01-31',
  org_group: 'group1'
});

if (dailyStats.ok) {
  dailyStats.data.forEach(day => {
    console.log(`${day.date}: +${day.followers} followers, +${day.views} views, ${day.posts} posts`);
  });
}

// Get top 5 accounts by followers
const topAccounts = await client.stats.getTopAccounts({
  metric: 'followers',
  limit: 5,
  tag: 'fitness'
});

// Get top 10 posts by views
const topPosts = await client.stats.getTopPosts({
  metric: 'views',
  limit: 10
});

// Refresh stats (fetch live data from platforms)
// Can only be called once per hour per organization
const refreshResult = await client.stats.refresh({
  org_group: 'optional-group-filter'
});

if (refreshResult.ok) {
  console.log(`Refreshed ${refreshResult.data.accounts_refreshed} accounts`);
  console.log(`New account stats: ${refreshResult.data.account_stats.length}`);
  console.log(`New post stats: ${refreshResult.data.post_stats.length}`);
}

TypeScript Support

This package is written in TypeScript and provides full type definitions:

import type { Account, Post, Task, ApiKey, ApiResponse } from 'ugcinc';

License

MIT