X (Twitter) credits
Post to X as a pay-per-use add-on of the paid plans: what each post uses, what is left, and top-ups.
X charges for every post, so posting to X uses X credits: 1 X credit = 1 X post; a post with a link uses 10 (X charges about 13 times more for links); each image or video adds 1. Each paid plan includes X credits every month (Starter 30, Pro 150, Agency 600, Scale 2,000; they renew on the 1st and do not roll over) and a daily cap of X posts (5, 20, 60, 150). Top-up credits (80 for $5, 340 for $20, one-time) are used after the plan's credits and do not expire. X is not available on Free or during the trial. Connect X like any OAuth network (POST /api/oauth/x/url) and publish with platforms: ["x"]: the credits are taken before anything is sent to X and come back if X rejects the post. Text is fitted to 280 characters as X counts them (a link counts 23, an emoji 2); up to 4 images or one video.
GET /api/x: X (Twitter) credits: what posting to X uses and what this account has leftGET /api/x/quote: How many X credits one post would use, before it is published or scheduledPOST /api/x/credits/checkout: A Stripe Checkout link to buy X top-up credits (one-time payment)
X (Twitter) credits: what posting to X uses and what this account has left
/api/xX is a pay-per-use add-on of the paid plans (Starter, Pro, Agency, Scale; not Free, not during a trial). 1 X credit = 1 X post; a post with a link uses 10 (X charges about 13 times more for links); each image or video adds 1. Each paid plan includes X credits every month (Starter 30, Pro 150, Agency 600, Scale 2,000; they renew on the 1st and do not roll over) and a daily cap of X posts (5, 20, 60, 150); top-up credits (80 for $5, 340 for $20, one-time) are used after them and do not expire. An X post without enough credits is not sent to X: its result has code x_credits_required and the links to buy more.
Needs an API key: Authorization: Bearer pw_live_….
Responses
| Status | Means |
|---|---|
200 | { network: "x", available, mode: paid|free|trial|internal, allowed, killed, pricing: { rule, credits, plans, packs, notes }, credits?: { plan_allowance, allowance_used, allowance_left, topup, total_left, resets_on }, daily?: { used, max }, buy?: [{ pack, usd, credits, url, label }], history: [{ at, kind, what, credits, from_plan, from_topup }], upgrade? (Free), house? (internal: { spent_usd, cap_usd }) } |
curl "https://postwire.io/api/x" \
-H "Authorization: Bearer $POSTWIRE_API_KEY"const res = await fetch("https://postwire.io/api/x", {
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/x",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
)
print(r.status_code, r.json())Example response
{
"network": "x",
"available": true,
"mode": "paid",
"allowed": true,
"credits": {
"plan_allowance": 150,
"allowance_used": 12,
"allowance_left": 138,
"topup": 80,
"total_left": 218,
"resets_on": "2026-11-01"
},
"daily": {
"used": 3,
"max": 20
},
"buy": [
{
"pack": "5",
"usd": 5,
"credits": 80,
"url": "https://postwire.io/api/x/credits/go?t=…&pack=5"
},
{
"pack": "20",
"usd": 20,
"credits": 340,
"url": "https://postwire.io/api/x/credits/go?t=…&pack=20"
}
]
}Abridged: it also returns pricing (the rule, every plan's X credits and daily cap, the packs) and history (the last movements).
How many X credits one post would use, before it is published or scheduled
/api/x/quoteNeeds an API key: Authorization: Bearer pw_live_….
Parameters
| Field | Type | Description |
|---|---|---|
text | query, string | The X post's text. A link anywhere in it (also a bare domain such as postwire.io/x) makes it a post with a link. |
images | query, integer | Images attached (X takes up to 4). |
video | query, boolean | A video attached (one, on its own). |
Responses
| Status | Means |
|---|---|
200 | { credits, has_link, images, video, sentence } — e.g. sentence "This post uses 10 X credits: it contains a link." |
curl "https://postwire.io/api/x/quote?text=The+new+docs+are+live%3A+https%3A%2F%2Fpostwire.io%2Fdocs%2F" \
-H "Authorization: Bearer $POSTWIRE_API_KEY"const res = await fetch("https://postwire.io/api/x/quote?text=The+new+docs+are+live%3A+https%3A%2F%2Fpostwire.io%2Fdocs%2F", {
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/x/quote?text=The+new+docs+are+live%3A+https%3A%2F%2Fpostwire.io%2Fdocs%2F",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
)
print(r.status_code, r.json())Example response
{
"credits": 10,
"has_link": true,
"images": 0,
"video": false,
"sentence": "This post uses 10 X credits: it contains a link."
}A Stripe Checkout link to buy X top-up credits (one-time payment)
/api/x/credits/checkoutPacks: "5" = 80 credits for $5, "20" = 340 credits for $20. Nothing is charged until the person pays on the Stripe page. Paid plans only (403 x_paid_plan_required otherwise, with the plan that has X).
Needs an API key: Authorization: Bearer pw_live_….
Body (JSON)
| Field | Type | Description |
|---|---|---|
pack required | string | One of: 5, 20. |
Responses
| Status | Means |
|---|---|
200 | { url } of the Stripe Checkout page |
403 | x_paid_plan_required (Free or a running trial) or internal_account |
curl -X POST "https://postwire.io/api/x/credits/checkout" \
-H "Authorization: Bearer $POSTWIRE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"pack": "5"
}'const res = await fetch("https://postwire.io/api/x/credits/checkout", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
"pack": "5"
}),
});
console.log(res.status, await res.json());import os, requests
r = requests.post(
"https://postwire.io/api/x/credits/checkout",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
json={
"pack": "5",
},
)
print(r.status_code, r.json())Example response
{
"url": "https://checkout.stripe.com/c/pay/cs_live_…"
}