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

@spade-dev/social-api

v0.1.0

Published

TypeScript SDK for the Spade social media API

Readme

@spade-dev/social-api

TypeScript SDK for the Spade social media API. Schedule posts, cross-post to 13 platforms, pull analytics, manage contacts, and wire up webhooks — all with full type safety.

Install

npm install @spade-dev/social-api
# or
pnpm add @spade-dev/social-api

Quickstart

import { SocialApiClient } from '@spade-dev/social-api'

const client = new SocialApiClient({ apiKey: 'sk_...' })
// Or set SOCIAL_API_KEY in your environment and call new SocialApiClient()

Authentication

Pass your key directly or export it as an environment variable:

export SOCIAL_API_KEY=sk_your64hexkey
const client = new SocialApiClient()            // reads SOCIAL_API_KEY
const client = new SocialApiClient({ apiKey: 'sk_...' }) // explicit

Keys follow the format sk_ + 64 lowercase hex characters. Create them in the Spade dashboard.


Self-hosted / local override

const client = new SocialApiClient({
  apiKey: 'sk_...',
  baseUrl: 'http://localhost:3000/v1',
})

Error handling

All errors throw a SpadeError with typed code, message, and status fields:

import { SocialApiClient, SpadeError } from '@spade-dev/social-api'

try {
  await client.posts.get('nonexistent-id')
} catch (err) {
  if (err instanceof SpadeError) {
    console.error(err.code)    // 'NOT_FOUND'
    console.error(err.status)  // 404
    console.error(err.message) // 'Post not found'
  }
}

Error codes: UNAUTHORIZED · FORBIDDEN · NOT_FOUND · VALIDATION_ERROR · BAD_REQUEST · CONFLICT · RATE_LIMITED · PLATFORM_ERROR · OAUTH_ERROR · INTERNAL_ERROR


Examples

Schedule a post

const profile = await client.profiles.create({ name: 'Acme Brand' })

const post = await client.posts.create({
  profileId: profile.id,
  content: 'Big launch day! 🚀',
  scheduledFor: '2026-07-04T14:00:00Z',
  timezone: 'America/New_York',
  platforms: [
    { platform: 'twitter',   accountId: 'acc-twitter-uuid' },
    { platform: 'linkedin',  accountId: 'acc-linkedin-uuid' },
    { platform: 'instagram', accountId: 'acc-ig-uuid' },
  ],
})

console.log(post.id, post.status) // 'scheduled'

Cross-post with per-platform content

const post = await client.posts.create({
  profileId: 'prof-uuid',
  content: 'Default caption for all platforms.',
  publishNow: true,
  platforms: [
    {
      platform: 'linkedin',
      accountId: 'acc-linkedin-uuid',
      customContent: 'B2B angle: here is why this matters for your team.',
      platformSpecificData: { orgId: 'li-org-id', visibility: 'PUBLIC' },
    },
    {
      platform: 'tiktok',
      accountId: 'acc-tiktok-uuid',
      platformSpecificData: {
        privacy_level: 'PUBLIC_TO_EVERYONE',
        disable_duet: false,
        disable_comment: false,
      },
    },
    {
      platform: 'youtube',
      accountId: 'acc-yt-uuid',
      platformSpecificData: {
        title: 'My Video Title',
        privacy: 'public',
        category: '22',
      },
    },
  ],
})

Upload media and attach to a post

const file = new Blob([fs.readFileSync('./photo.jpg')], { type: 'image/jpeg' })
const { mediaId } = await client.media.upload(file, 'photo.jpg')

const post = await client.posts.create({
  profileId: 'prof-uuid',
  content: 'Check out this photo!',
  publishNow: true,
  platforms: [{ platform: 'instagram', accountId: 'acc-ig-uuid' }],
  mediaItems: [{ mediaId, position: 0 }],
})

Get analytics

// Post-level snapshots
const snapshots = await client.analytics.getPosts({
  accountId: 'acc-uuid',
  platform: 'twitter',
  from: '2026-06-01',
  to: '2026-06-30',
})

// Daily time-series for charting
const daily = await client.analytics.getDaily({
  accountId: 'acc-uuid',
  from: '2026-06-01',
})

// Best time to post
const { recommendations } = await client.analytics.getBestTime({ accountId: 'acc-uuid' })
// recommendations[0] = { dayOfWeek: 3, hour: 14, avgEngagement: 42.5, sampleSize: 10 }
// dayOfWeek: 0=Sun, 6=Sat  |  hour: 0–23 UTC

Set up webhooks

const webhook = await client.webhooks.create({
  url: 'https://your-app.com/webhooks/spade',
  events: ['post.published', 'post.failed', 'post.scheduled', 'account.connected'],
})

// webhook.secret is shown only once — store it securely
console.log(webhook.secret)

Verify incoming webhook signatures:

import { createHmac, timingSafeEqual } from 'crypto'

function verifyWebhook(rawBody: string, secret: string, signature: string): boolean {
  const expected = createHmac('sha256', secret).update(rawBody).digest('hex')
  return timingSafeEqual(Buffer.from(expected), Buffer.from(signature))
}

// In your webhook handler:
const sig = req.headers['x-spade-signature']
const event = req.headers['x-spade-event']
if (!verifyWebhook(req.rawBody, process.env.WEBHOOK_SECRET!, sig)) {
  return res.status(401).send('Invalid signature')
}

Webhook event catalog:

| Event | When | |---|---| | post.published | A platform target published successfully | | post.failed | A platform target failed | | post.partial | Some targets published, some failed | | post.scheduled | A post was queued for future delivery | | account.connected | OAuth flow completed | | account.disconnected | Account manually disconnected | | webhook.test | Fired by webhooks.test() |


Manage contacts & broadcasts

// Import contacts
await client.contacts.bulkCreate([
  { name: 'Alice', email: '[email protected]', tags: ['vip'] },
  { name: 'Bob',   phone: '+15551234567' },
])

// Create a broadcast DM campaign
const broadcast = await client.broadcasts.create({
  name: 'Summer Sale',
  message: 'Hey {name}, our summer sale is live — 30% off everything!',
  accountId: 'acc-ig-uuid',
})

// Add recipients
const contacts = await client.contacts.list({ tag: 'vip' })
await client.broadcasts.addRecipients(
  broadcast.id,
  contacts.data.map(c => c.id),
)

// Send immediately or schedule
await client.broadcasts.schedule(broadcast.id, '2026-07-01T09:00:00Z')

Automated DM sequences

const sequence = await client.sequences.create({
  name: 'Welcome Series',
  accountId: 'acc-ig-uuid',
  steps: [
    { message: 'Hey, welcome! Thanks for following us.', delayMinutes: 0 },
    { message: "Here's a tip to get started...",          delayMinutes: 1440 },  // 1 day
    { message: 'Any questions? Reply and we will help.',  delayMinutes: 4320 },  // 3 days
  ],
})

await client.sequences.activate(sequence.id)

// Enroll new followers
await client.sequences.enroll(sequence.id, { contactIds: ['contact-uuid-1', 'contact-uuid-2'] })

Comment automations (Instagram / Facebook)

const automation = await client.automations.create({
  name: 'Price inquiry',
  accountId: 'acc-ig-uuid',
  keyword: 'price',
  replyMessage: 'Hi! DM us for pricing details 🙌',
  isActive: true,
})

// Check trigger logs
const logs = await client.automations.getLogs(automation.id, { triggered: true, limit: 20 })

Paid ads

// List Meta campaigns
const campaigns = await client.ads.listCampaigns({ accountId: 'acc-fb-uuid' })

// Boost a published post
const boost = await client.ads.boost({
  accountId: 'acc-fb-uuid',
  postId: '1234567890_9876543210',
  budget: 100,
  durationDays: 7,
  targeting: { age_min: 25, age_max: 45 },
})

// Pull campaign analytics
const stats = await client.ads.getAnalytics({
  accountId: 'acc-fb-uuid',
  campaignId: boost.id,
  from: '2026-07-01',
  to: '2026-07-07',
})

Inbox (DMs, comments, reviews)

// List DM conversations
const conversations = await client.inbox.listConversations('acc-ig-uuid')

// Reply to a DM
await client.inbox.sendMessage(conversations[0]!.id, 'acc-ig-uuid', 'Thanks for reaching out!')

// Reply to a comment
const comments = await client.inbox.listComments('acc-ig-uuid')
await client.inbox.replyToComment(comments[0]!.postId, 'acc-ig-uuid', '❤️ Thanks!')

// Reply to a Google Business review
const reviews = await client.inbox.listReviews('acc-gbp-uuid')
await client.inbox.replyToReview(reviews[0]!.id, 'acc-gbp-uuid', 'Thank you for the kind words!')

Resource reference

| client.X | Methods | |---|---| | profiles | list · get · create · update · delete | | accounts | list · get · health · disconnect | | connect | getAuthUrl · connectBluesky · connectTelegram | | posts | create · list · get · update · delete · retry | | analytics | getPosts · getDaily · getBestTime · getAccount · getInstagramDemographics · getYouTubeDailyViews | | webhooks | list · create · update · delete · getLogs · test | | media | requestUpload · getUploadStatus · upload (convenience) | | inbox | listConversations · getMessages · sendMessage · listComments · getPostComments · replyToComment · listReviews · replyToReview | | contacts | list · create · bulkCreate · get · update · delete · setField · deleteField · listChannels | | broadcasts | list · create · get · update · delete · send · schedule · cancel · listRecipients · addRecipients | | sequences | list · create · get · update · delete · activate · pause · enroll · unenroll · listEnrollments | | automations | list · create · get · update · delete · getLogs | | ads | listCampaigns · createCampaign · getCampaign · updateCampaign · deleteCampaign · listAdSets · createAdSet · listAds · createAd · boost · getAnalytics · searchInterests |

Supported platforms

instagram · facebook · twitter · linkedin · tiktok · youtube · pinterest · reddit · bluesky · threads · snapchat · telegram · google_business

License

MIT