@saranshkhulbe/upload-post-publisher
v0.1.1
Published
TypeScript package for publishing to X, Instagram, and Facebook through Upload Post.
Downloads
21
Maintainers
Readme
@saranshkhulbe/upload-post-publisher
Clean TypeScript package for publishing to X, Instagram, and Facebook through the Upload Post API.
This package is built as a reusable npm library, not as a CLI. A consumer installs it, sets environment variables, and calls publish() with platform booleans such as { x: true, instagram: true, facebook: false }.
Install
With npm:
npm install @saranshkhulbe/upload-post-publisherWith Bun:
bun add @saranshkhulbe/upload-post-publisherQuick Start
Set these environment variables:
export UPLOAD_POST_API_KEY="your-upload-post-api-key"
export UPLOAD_POST_PROFILE="your-upload-post-profile"Optional environment variables:
export UPLOAD_POST_BASE_URL="https://api.upload-post.com/api"
export UPLOAD_POST_POLL_INTERVAL_MS="1000"
export UPLOAD_POST_POLL_TIMEOUT_MS="60000"Create a script like this:
import { publish } from "@saranshkhulbe/upload-post-publisher";
const result = await publish(
{
kind: "photos",
caption: "New product shots are live.",
media: [
"https://example.com/photo-1.jpg",
"https://example.com/photo-2.jpg",
],
platformOverrides: {
facebook: {
pageId: "your-facebook-page-id",
},
},
},
{
x: true,
instagram: true,
facebook: false,
},
{
showLogs: false,
},
);
console.log(result);If the publish fails or is partial, inspect the normalized error codes:
if (result.overallStatus !== "success") {
console.error(result.error);
console.error(result.platforms.instagram.error);
}Public API
The package exports:
publish(input, platformSelection?, options?)createPublisherService(dependencies)getPublishStatus(requestId)PublishInputPlatformSelectionPublishOptionsPublishResultPublishError
Useful publish options:
showLogs: prints detailed progress logs whentrueshowSummary: prints the compact end summary whentrueor omittedonProgress: custom progress callbacklogger: custom logger sink
Platform Selection
All platform flags default to false.
{
x: true,
instagram: false,
facebook: true,
}That means nothing is published unless the caller explicitly opts in.
Input Shapes
Text:
{
kind: "text",
caption: "Shipping an update today.",
}Photos:
{
kind: "photos",
caption: "Gallery post",
media: ["./photo-1.jpg", "./photo-2.jpg"],
}Video:
{
kind: "video",
caption: "Watch the launch reel.",
media: "./launch-reel.mp4",
}Local file paths and public URLs are both supported.
createPublisherService() Example
Use this if you want to inject credentials directly instead of reading from process.env:
import { createPublisherService } from "@saranshkhulbe/upload-post-publisher";
const publisher = createPublisherService({
apiKey: process.env.UPLOAD_POST_API_KEY!,
profile: process.env.UPLOAD_POST_PROFILE!,
baseUrl: "https://api.upload-post.com/api",
});
const result = await publisher.publish(
{
kind: "text",
caption: "Shipping an update today.",
platformOverrides: {
facebook: {
pageId: "your-facebook-page-id",
linkUrl: "https://example.com/article",
},
},
},
{
x: true,
instagram: false,
facebook: true,
},
);
console.log(result);Behavior Notes
- Instagram text-only publishing is not supported in this package. If Instagram is requested for a text payload, it is returned as skipped with
instagram_text_only_not_supported. - Facebook publishing is for connected Facebook Pages, not personal profiles.
- If your Upload Post profile has multiple connected Facebook Pages, set
platformOverrides.facebook.pageId. - Photo payloads accept image files only.
- Video payloads accept exactly one video.
- Mixed photo and video cross-post payloads are intentionally rejected before any API call.
Preflight Limits
This package validates a few important limits locally before it sends the request:
- If
x: trueon a photo post, the package allows up to 4 images. - If
instagram: trueon a photo post, the package allows up to 10 images. - For local image files, the package checks these size limits before upload:
- X: 5 MB per image
- Instagram: 8 MB per image
- Facebook: 10 MB per image
- For local Instagram videos, the package checks 300 MB maximum.
- For local Facebook
VIDEOuploads, the package checks 10 GB maximum.
Remote URLs are still accepted, but file size validation for URLs is left to Upload Post because the package cannot reliably inspect remote assets ahead of time.
Graceful Error Handling
Errors are normalized into a stable PublishError shape so your app does not need to parse Upload Post messages directly.
Common normalized error codes include:
authentication_errorprofile_not_foundplan_restrictedmonthly_limit_exceededdaily_platform_limit_exceededrate_limitedservice_unavailableaccount_reconnect_requiredaccount_not_linkedaccount_permission_erroraccount_restrictedfacebook_page_selection_requiredunsupported_contentpolicy_violationvalidation_errorpublish_timeout
Each normalized error can also include:
retryablesuggestionplatformusageviolationsavailablePages
Example:
const result = await publish(payload, {
x: true,
instagram: true,
facebook: true,
});
if (result.platforms.facebook.error?.code === "facebook_page_selection_required") {
console.log(result.platforms.facebook.error.availablePages);
}
if (result.error?.code === "daily_platform_limit_exceeded") {
console.log(result.error.violations);
}Logging UX
The package supports two layers of output:
- Detailed progress logs with
showLogs: true - A compact final summary, shown by default
Example:
await publish(payload, {
x: true,
instagram: true,
facebook: false,
}, {
showLogs: false,
});That keeps the output short, for example:
Publish summary: success
x: published
instagram: published
facebook: skippedIf you want the detailed polling logs as well:
await publish(payload, platforms, {
showLogs: true,
});Package Structure
The repo is organized as a library-first package:
src/index.ts: public package entrypointsrc/config/: environment loadingsrc/domain/: public types and validationsrc/lib/upload-post/: low-level Upload Post HTTP client and endpoint wrapperssrc/services/: publish orchestrationexamples/: non-published usage examplestests/: validation and publisher tests
Only the built dist/ output, README.md, and LICENSE are included in the published tarball.
Local Development
Install dependencies:
bun installType-check:
bun run checkRun tests:
bun testBuild:
bun run buildInspect the tarball:
bun run pack:checkPublish Commands
According to npm's docs for scoped public packages, publish with --access public:
Use these commands from the package root:
npm login
npm whoami
bun run check
bun test
bun run build
bun run pack:check
npm publish --access publicAfter publishing, verify:
npm view @saranshkhulbe/upload-post-publisher version