API operational
Node.js / TypeScript

Use PostGate from Node.js

Authenticate with a server-side API key, send one request per unified post, and read per-platform delivery status back.

01

Install

PostGate's SDKs & MCP guide confirms that PostGate generates a typed Node.js/TypeScript client from one OpenAPI 3.1 contract, but the published npm package name for that client isn't documented publicly. Rather than guess one, this page calls the documented REST endpoints directly with the built-in fetch (Node 18+), which is guaranteed to match what actually ships — no dependency required.

No install neededPostGate API
node --version   # 18 or later ships global fetch()
02

Prefer a typed client instead?

Because every operation is generated from the same live contract, you can point an OpenAPI code generator at the public schema and get typed TypeScript request/response types without waiting on PostGate to publish a client package for you.

Generate types from the live schemaPostGate API
npm install -D openapi-typescript
npx openapi-typescript https://postgate.studio/openapi.json -o postgate.d.ts
03

Authenticate

Every /api request needs a PostGate key in the Authorization header. Read it from an environment variable that only your server process can see — never from a NEXT_PUBLIC_-prefixed variable, and never bundled into client-side JavaScript. See Authentication for the full scope model: keys carry read, publish, or full, and pg_test_ keys run the complete lifecycle against a sandbox adapter without reaching a real network.

.env (server-only)PostGate API
POSTGATE_API_KEY=pg_test_...
04

Create a post

Pass profile IDs for deterministic routing and a stable Idempotency-Key so a dropped connection and a retry never publish twice — see Idempotency & safe retries. With no draft or scheduled_at field, delivery starts immediately.

create-post.tsPostGate API
const API_URL = "https://postgate.studio";
const API_KEY = process.env.POSTGATE_API_KEY!;

const response = await fetch(`${API_URL}/api/posts?profile_group_id=grp_acme123`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "launch-post-001",
  },
  body: JSON.stringify({
    post: { body: "Launch day. \u{1F680}" },
    profiles: ["pro_instagram123", "pro_linkedin456"],
    media: ["https://cdn.example.com/launch.jpg"],
  }),
});

if (!response.ok) {
  const { error } = await response.json();
  throw new Error(`${error.code}: ${error.message}`);
}

const post = await response.json();
console.log(post.id, post.status);
05

Confirm delivery

The create response is acceptance, not proof of publication — read the delivery lifecycle. Poll GET /api/posts/{id} and inspect the targets array for the operational truth, or subscribe to webhooks for production traffic instead of polling.

poll-post.tsPostGate API
const statusResponse = await fetch(`${API_URL}/api/posts/${post.id}`, {
  headers: { Authorization: `Bearer ${API_KEY}` },
});
const status = await statusResponse.json();

for (const target of status.targets) {
  console.log(target.platform, target.status, target.permalink);
}
06

Handle errors

Every error returns one structured envelope with a stable code and a retryable flag — see Errors & retries. Only retry when retryable is true, and reuse the same Idempotency-Key when you do.

Structured error envelopePostGate API
{
  "error": {
    "code": "platform_transient",
    "message": "Instagram is temporarily unavailable.",
    "platform": "instagram",
    "retryable": true,
    "details": {},
    "request_id": "req_01JXYZ"
  }
}
Don't retry blindly
A caught fetch network error does not mean the post failed to send — it may already be queued. Retry with the same Idempotency-Key instead of assuming nothing happened.
Python instead?
See the Python quickstart for the same flow with requests.