Account, keys and billing
Sign up, read usage and limits, manage API keys and the plan.
Every limit an account has is readable: GET /api/usage gives each one with what is used and what is left. A limit of null means unlimited.
POST /api/signup: Create an account (the API key is emailed as a confirmation link, not returned here)GET /api/me: Account info, usage and connectionsGET /api/account: Alias of GET /api/me: account info, usage and connectionsGET /api/usage: This month's posts, today's AI drafts and brands, against the plan's limitsGET /api/plan: The account's plan, what it includes, current usage and the next plan with a checkout linkGET /api/plans: List plans and limitsGET /api/billing: Plan, subscription state and where to manage billingPOST /api/billing/checkout: Start a Stripe checkout for a paid planPOST /api/billing/portal: A Stripe customer-portal link to change plan, update the card or cancelGET /api/trial: The no-card Pro trial: where it stands, what Pro did for this account, the offerPOST /api/trial/start: Start the no-card Pro trial now (an account that has published)GET /api/keys: List your API keys (prefixes only — the secret is never returned)POST /api/keys: Create a new API key, optionally with an expiryDELETE /api/keys/{id}: Revoke an API key immediately
Create an account (the API key is emailed as a confirmation link, not returned here)
/api/signupSignup never returns credentials. We create the account and email a one-time confirmation link; opening it proves you control the mailbox and issues your API key. This prevents anyone from registering an address they do not own and receiving a working key.
No API key needed.
Body (JSON)
| Field | Type | Description |
|---|---|---|
email required | string (email) |
Responses
| Status | Means |
|---|---|
200 | Account created; pending_verification=true, emailed=true|false |
curl -X POST "https://postwire.io/api/signup" \
-H "Content-Type: application/json" \
-d '{
"email": "you@example.com"
}'const res = await fetch("https://postwire.io/api/signup", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
"email": "you@example.com"
}),
});
console.log(res.status, await res.json());import os, requests
r = requests.post(
"https://postwire.io/api/signup",
json={
"email": "you@example.com",
},
)
print(r.status_code, r.json())Account info, usage and connections
/api/melimits carries posts_this_month, brands, ai_drafts_today, networks_per_post { limit } and scheduled_queue { used, limit, held, left } (null = no limit). A free account created before the 2-Oct-2026 limits also gets grandfathered { until, note } (it keeps the earlier limits until 2026-11-01). Free accounts get insights_teaser: how many What works patterns are ready and how strong each is, without the answer. With teams: `workspace` (id, name, plan, seats, members) and `member` (who is calling: email, role, effective_role, read_only, brand_ids, and `can`: which actions this role may take). The no-card Pro trial (when it is on): `trial` { kind: "reverse", plan, plan_name, until, ends_at, started_at, days_left, active, no_card } while it runs (a card trial is `trial` with kind "card"), `trial_ended` once it is over, `trial_line` (one sentence, no link), and for a free account that can still have it `reverse_trial_offer` { kind, plan, name, days, no_card, starts: "now" | "first_post", unlocks, extras, note } instead of `trial_offer`. A free account that looks like a business (3+ networks, 2+ brands, or a team seat wall) gets `bizcard` { show, dismissed, reasons, networks, brands, title, lead, benefits: [{ key: "approvals" | "team", title, text }], action: { kind: "trial", label, days, note } | { kind: "checkout", plan, label, url, secondary?: { plan, label, url }, note } }: approvals and team with the plans' real limits, and the no-card trial (POST /api/trial/start with from "bizcard") or Starter's signed checkout link. `show` is false once it was dismissed.
Needs an API key: Authorization: Bearer pw_live_….
Responses
| Status | Means |
|---|---|
200 | OK |
401 | Missing/invalid API key |
curl "https://postwire.io/api/me" \
-H "Authorization: Bearer $POSTWIRE_API_KEY"const res = await fetch("https://postwire.io/api/me", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}` },
});
console.log(res.status, await res.json());import os, requests
r = requests.get(
"https://postwire.io/api/me",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
)
print(r.status_code, r.json())Alias of GET /api/me: account info, usage and connections
/api/accountNeeds an API key: Authorization: Bearer pw_live_….
Responses
| Status | Means |
|---|---|
200 | Same body as /api/me |
401 | Missing/invalid API key |
curl "https://postwire.io/api/account" \
-H "Authorization: Bearer $POSTWIRE_API_KEY"const res = await fetch("https://postwire.io/api/account", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}` },
});
console.log(res.status, await res.json());import os, requests
r = requests.get(
"https://postwire.io/api/account",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
)
print(r.status_code, r.json())This month's posts, today's AI drafts and brands, against the plan's limits
/api/usage{ plan, plan_name, period, posts: { used, limit, left, resets_on }, ai_drafts_today: { used, limit, left, resets_at }, brands: { used, limit }, networks_per_post: { limit }, scheduled_queue: { used, limit, held, left }, grandfathered_until?, upgrade? } — a limit of null means unlimited; upgrade appears at 80 % of a limit.
Needs an API key: Authorization: Bearer pw_live_….
Responses
| Status | Means |
|---|---|
200 | usage |
curl "https://postwire.io/api/usage" \
-H "Authorization: Bearer $POSTWIRE_API_KEY"const res = await fetch("https://postwire.io/api/usage", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}` },
});
console.log(res.status, await res.json());import os, requests
r = requests.get(
"https://postwire.io/api/usage",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
)
print(r.status_code, r.json())Example response
{
"plan": "free",
"plan_name": "Free",
"period": "2026-10",
"posts": {
"used": 6,
"limit": 20,
"left": 14,
"resets_on": "2026-11-01"
},
"ai_drafts_today": {
"used": 1,
"limit": 5,
"left": 4
},
"brands": {
"used": 1,
"limit": 1
},
"networks_per_post": {
"limit": 2
},
"scheduled_queue": {
"used": 2,
"limit": 3,
"held": 0,
"left": 1
}
}Abridged. null as a limit means unlimited.
The account's plan, what it includes, current usage and the next plan with a checkout link
/api/planNeeds an API key: Authorization: Bearer pw_live_….
Responses
| Status | Means |
|---|---|
200 | { plan, name, price_usd_month, includes, limits: { posts_per_month, brands, ai_drafts_per_day, networks_per_post, scheduled_queue }, usage, grandfathered_until?, next? } |
curl "https://postwire.io/api/plan" \
-H "Authorization: Bearer $POSTWIRE_API_KEY"const res = await fetch("https://postwire.io/api/plan", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}` },
});
console.log(res.status, await res.json());import os, requests
r = requests.get(
"https://postwire.io/api/plan",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
)
print(r.status_code, r.json())List plans and limits
/api/plansEach plan: { name, price, brands, limit (posts a month, -1 = unlimited), genDay (AI drafts a day), networksPerPost (-1 = every network in each post), maxQueued (scheduled posts at a time, -1 = unlimited) }.
No API key needed.
Responses
| Status | Means |
|---|---|
200 | OK |
curl "https://postwire.io/api/plans"const res = await fetch("https://postwire.io/api/plans", {
method: "GET",
});
console.log(res.status, await res.json());import os, requests
r = requests.get(
"https://postwire.io/api/plans",
)
print(r.status_code, r.json())Plan, subscription state and where to manage billing
/api/billingChanging, cancelling or updating a card is done by a person: POST /api/billing/portal returns the portal link.
Needs an API key: Authorization: Bearer pw_live_….
Responses
| Status | Means |
|---|---|
200 | { plan, name, price_usd_month, subscription: active|none|unknown|null, manage, upgrade, refund } |
curl "https://postwire.io/api/billing" \
-H "Authorization: Bearer $POSTWIRE_API_KEY"const res = await fetch("https://postwire.io/api/billing", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}` },
});
console.log(res.status, await res.json());import os, requests
r = requests.get(
"https://postwire.io/api/billing",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
)
print(r.status_code, r.json())Start a Stripe checkout for a paid plan
/api/billing/checkoutNeeds an API key: Authorization: Bearer pw_live_….
Body (JSON)
| Field | Type | Description |
|---|---|---|
plan required | string | One of: starter, pro, agency, scale. |
interval | string | Billing period: month (default) or year (20% off, billed once a year). A running subscription is switched, prorated. One of: month, year. |
Responses
| Status | Means |
|---|---|
200 | { url } |
curl -X POST "https://postwire.io/api/billing/checkout" \
-H "Authorization: Bearer $POSTWIRE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"plan": "starter"
}'const res = await fetch("https://postwire.io/api/billing/checkout", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
"plan": "starter"
}),
});
console.log(res.status, await res.json());import os, requests
r = requests.post(
"https://postwire.io/api/billing/checkout",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
json={
"plan": "starter",
},
)
print(r.status_code, r.json())A Stripe customer-portal link to change plan, update the card or cancel
/api/billing/portalNeeds an API key: Authorization: Bearer pw_live_….
Responses
| Status | Means |
|---|---|
200 | { url } |
503 | billing not configured |
curl -X POST "https://postwire.io/api/billing/portal" \
-H "Authorization: Bearer $POSTWIRE_API_KEY"const res = await fetch("https://postwire.io/api/billing/portal", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}` },
});
console.log(res.status, await res.json());import os, requests
r = requests.post(
"https://postwire.io/api/billing/portal",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
)
print(r.status_code, r.json())The no-card Pro trial: where it stands, what Pro did for this account, the offer
/api/trial7 days of Pro with no card, once per person, started by the account's first successful post (or, for an account that already published, its next publish or the next free limit it hits). While it runs the account keeps plan "free" and gets Pro's limits, except X (paid per post on paid plans) and an AI budget of Free's month plus Pro's for the trial's days. When it ends nothing is deleted: scheduled posts past Free's queue cap are held (they go out on upgrade), brands made during the trial past Free's count are paused for publishing. Returns { trial: { kind, plan, until, started_at, days_left, active, ended_at? }, line, facts: { posts, network_posts, multi_network_posts, posts_this_month, free_post_limit, posts_over_free, ai_drafts_month_about, queued, held, queued_past_free, brands, brands_past_free }, did[], when_it_ends[], keeps[], paid_offer? } — or { trial: null, available, offer? } for an account that never had one.
Needs an API key: Authorization: Bearer pw_live_….
Responses
| Status | Means |
|---|---|
200 | OK |
401 | Missing/invalid API key |
curl "https://postwire.io/api/trial" \
-H "Authorization: Bearer $POSTWIRE_API_KEY"const res = await fetch("https://postwire.io/api/trial", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}` },
});
console.log(res.status, await res.json());import os, requests
r = requests.get(
"https://postwire.io/api/trial",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
)
print(r.status_code, r.json())Start the no-card Pro trial now (an account that has published)
/api/trial/startFor a free account that has published and never had the trial: starts the 7 days now (the dashboard's button on a free limit). An account that has not published yet is told its first post starts it — except `from: "bizcard"` (the dashboard's approvals-and-team card) for an account the server finds business-like (3+ networks connected, 2+ brands, or a team seat wall): it starts before the first post, with the same once-per-person rules. 409 `trial_unavailable` with `reason`: not_posted_yet, trial_used, same_person (another account of the same person had it), card_trial_used, paid_plan, off.
Needs an API key: Authorization: Bearer pw_live_….
Body (JSON)
| Field | Type | Description |
|---|---|---|
from | string | where the button was (measurement); "bizcard" may start it before the first post for a business-like account |
Responses
| Status | Means |
|---|---|
200 | { started: true, trial_started: { kind, plan, days, started_at, until, no_card }, trial_note, trial } |
401 | Missing/invalid API key |
409 | trial_unavailable |
curl -X POST "https://postwire.io/api/trial/start" \
-H "Authorization: Bearer $POSTWIRE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "dashboard"
}'const res = await fetch("https://postwire.io/api/trial/start", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
"from": "dashboard"
}),
});
console.log(res.status, await res.json());import os, requests
r = requests.post(
"https://postwire.io/api/trial/start",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
json={
"from": "dashboard",
},
)
print(r.status_code, r.json())List your API keys (prefixes only — the secret is never returned)
/api/keysNeeds an API key: Authorization: Bearer pw_live_….
Responses
| Status | Means |
|---|---|
200 | keys[] |
curl "https://postwire.io/api/keys" \
-H "Authorization: Bearer $POSTWIRE_API_KEY"const res = await fetch("https://postwire.io/api/keys", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}` },
});
console.log(res.status, await res.json());import os, requests
r = requests.get(
"https://postwire.io/api/keys",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
)
print(r.status_code, r.json())Create a new API key, optionally with an expiry
/api/keysNeeds an API key: Authorization: Bearer pw_live_….
Body (JSON)
| Field | Type | Description |
|---|---|---|
name | string | |
expires_in_days | integer | 1 to 3650. |
Responses
| Status | Means |
|---|---|
200 | api_key shown once |
curl -X POST "https://postwire.io/api/keys" \
-H "Authorization: Bearer $POSTWIRE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "n8n production",
"expires_in_days": 365
}'const res = await fetch("https://postwire.io/api/keys", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
"name": "n8n production",
"expires_in_days": 365
}),
});
console.log(res.status, await res.json());import os, requests
r = requests.post(
"https://postwire.io/api/keys",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
json={
"name": "n8n production",
"expires_in_days": 365,
},
)
print(r.status_code, r.json())Revoke an API key immediately
/api/keys/{id}Needs an API key: Authorization: Bearer pw_live_….
Parameters
| Field | Type | Description |
|---|---|---|
id required | path, string |
Responses
| Status | Means |
|---|---|
200 | revoked |
404 | not found or already revoked |
curl -X DELETE "https://postwire.io/api/keys/7c2a6f0e-5b1d-4c8a-9e3f-2d4b6a8c0e1f" \
-H "Authorization: Bearer $POSTWIRE_API_KEY"const res = await fetch("https://postwire.io/api/keys/7c2a6f0e-5b1d-4c8a-9e3f-2d4b6a8c0e1f", {
method: "DELETE",
headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}` },
});
console.log(res.status, await res.json());import os, requests
r = requests.delete(
"https://postwire.io/api/keys/7c2a6f0e-5b1d-4c8a-9e3f-2d4b6a8c0e1f",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
)
print(r.status_code, r.json())