API operational
Python

Use PostGate from Python

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 typed clients — including Python — from one OpenAPI 3.1 contract, but the published package name for each generated client isn't documented publicly. Rather than guess one, this page uses Python's standard requests library directly against the documented REST endpoints, which is guaranteed to match what's actually shipped.

Install requestsPostGate API
pip install requests
02

Prefer a typed client instead?

Because every operation is generated from the same live contract, you can point any OpenAPI code generator at the public schema and get a typed Python client without waiting on PostGate to publish one for you.

Generate a typed client from the live schemaPostGate API
pip install openapi-python-client
openapi-python-client generate --url https://postgate.studio/openapi.json
03

Authenticate

Every /api request needs a PostGate key in the Authorization header. Load it from an environment variable or secret manager — never hardcode it, and never ship it to a browser, mobile app, or notebook that a customer can open. 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.

Environment variablePostGate API
export 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.pyPostGate API
import os
import requests

API_URL = "https://postgate.studio"
API_KEY = os.environ["POSTGATE_API_KEY"]

response = requests.post(
    f"{API_URL}/api/posts",
    params={"profile_group_id": "grp_acme123"},
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
        "Idempotency-Key": "launch-post-001",
    },
    json={
        "post": {"body": "Launch day. \U0001F680"},
        "profiles": ["pro_instagram123", "pro_linkedin456"],
        "media": ["https://cdn.example.com/launch.jpg"],
    },
)
response.raise_for_status()
post = response.json()
print(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.pyPostGate API
status = requests.get(
    f"{API_URL}/api/posts/{post['id']}",
    headers={"Authorization": f"Bearer {API_KEY}"},
).json()

for target in status["targets"]:
    print(target["platform"], target["status"], target.get("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 requests.exceptions.RequestException from a timeout does not mean the post failed to send — it may already be queued. Retry with the same Idempotency-Key instead of assuming nothing happened.
Node.js instead?
See the Node.js quickstart for the same flow with fetch.