LivePublicator API
Draft and publish press releases for your clients. JSON over HTTPS. The full spec is at /api/v1/openapi.json (OpenAPI 3.1).
Keys and the plan
Get a key in your account ($99 a month, 10 release credits a month, see the plan). Send it on every call:
Authorization: Bearer lp_live_...- Drafts are free. Publishing uses 1 credit. Credits reset at each renewal and don't roll over.
- Up to 10 releases a day and 120 calls a minute per key.
- Each live release gets one AI report 7 days after it goes live: what ChatGPT, Perplexity, Gemini and Google AI Overviews say about the client. You get it as a webhook and in
GET /releases/{id}. - The first 3 releases of each new key are checked by a person before they go live.
- Every POST accepts an
Idempotency-Keyheader. Send the same key with the same body again and you get the first answer back, never a second release.
Draft and publish
1. Create a draft from a brief (we write it) or your full text (we check and format it):
curl https://livepublicator.com/api/v1/releases \
-H "Authorization: Bearer $LP_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: acme-launch-1" \
-d '{
"company_name": "Acme Robotics",
"company_domain": "acmerobotics.com",
"brief": "Acme Robotics opens a second factory in Austin, Texas, adding 120 jobs. Production starts in March.",
"quote": "Austin gives us the people and space to grow.",
"quote_name": "Dana Lee",
"quote_title": "CEO, Acme Robotics"
}'The answer has the release id, status: "drafting" and a private preview_url. Writing takes about a minute. Check it with GET /releases/{id} until the status is draft.
2. Publish it (1 credit):
curl -X POST https://livepublicator.com/api/v1/releases/RELEASE_ID/publish \
-H "Authorization: Bearer $LP_KEY" \
-H "Idempotency-Key: acme-launch-1-publish"The status becomes live (with live_url), scheduled (if you sent publish_at) or review (a person checks it first).
The same in JavaScript:
const api = (path, init = {}) =>
fetch("https://livepublicator.com/api/v1" + path, {
...init,
headers: { Authorization: "Bearer " + process.env.LP_KEY, "Content-Type": "application/json", ...init.headers },
}).then(async (r) => ({ status: r.status, body: await r.json() }));
const draft = await api("/releases", {
method: "POST",
headers: { "Idempotency-Key": "acme-launch-1" },
body: JSON.stringify({ company_name: "Acme Robotics", company_domain: "acmerobotics.com", brief: "Acme Robotics opens a second factory in Austin..." }),
});
let r = draft.body;
while (r.status === "drafting") {
await new Promise((ok) => setTimeout(ok, 10000));
r = (await api("/releases/" + r.id)).body;
}
const published = await api("/releases/" + r.id + "/publish", { method: "POST", headers: { "Idempotency-Key": "acme-launch-1-publish" } });
console.log(published.body.status, published.body.live_url);Endpoints
POST /releasesCreate a draft: company_name, company_domain, brief or full_text, optional quote with quote_name and quote_title, an image as image_url (https) or image_base64 (JPEG, PNG or WebP, up to 5 MB), publish_at (ISO 8601, up to 60 days ahead). Free. No image: the release goes live without one.POST /releases/{id}/publishPublish a draft. 1 credit.GET /releases/{id}Status, preview_url, live_url, notes from the content check, report_due_at and report_url (the AI report, 7 days after it went live).GET /releases?limit=20Your releases, newest first (up to 100).GET /creditsCredits left, when they reset, plan status, releases published today.Errors
Errors are JSON with a code, a message and, for 422, the field names:
{ "error": { "code": "validation", "message": "Some fields need fixing.", "fields": { "company_domain": "Required: the company's public website, e.g. acme.com." } } }- 401 unauthorized: no key, or an unknown key.
- 402 no_credits: no credits left this month.
- 403 revoked (the key was revoked) or plan_paused (a payment failed, or the plan ended).
- 409 duplicate: already published, or an Idempotency-Key reused with a different body.
- 422 validation: see
fields. - 429 daily_cap: 10 releases a day per key (or 120 calls a minute).
Webhooks
Add a webhook URL (https) to a key in your account. We send release.published, release.held (waiting for a person, or not published) and report.ready (the AI report of a release, 7 days after it went live), and try again for about an hour if your server does not answer 2xx.
{ "id": "delivery id", "event": "release.published", "created_at": "2026-10-05T09:00:00Z", "data": { "id": "...", "status": "live", "live_url": "..." } }Check the X-LivePublicator-Signature header (t=unix seconds,v1=hex): an HMAC-SHA256 of t + "." + raw body with the key's webhook secret.
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody, header, secret) {
// header: "t=1759600000,v1=<64 hex characters>"
const { t, v1 } = Object.fromEntries((header ?? "").split(",").map((part) => part.split("=")));
if (!t || !v1 || v1.length !== 64 || Math.abs(Date.now() / 1000 - Number(t)) > 600) return false;
const want = createHmac("sha256", secret).update(t + "." + rawBody).digest("hex");
return timingSafeEqual(Buffer.from(want), Buffer.from(v1));
}MCP server for AI tools
The same API as tools for AI assistants: draft_release, publish_release, get_release, list_releases, get_credits. Server URL: https://livepublicator.com/api/mcp (Streamable HTTP). It signs in with your API key in the Authorization header, the same credits and limits apply, and publishing always uses a credit, so keep the key private.
Claude Code:
claude mcp add --transport http livepublicator https://livepublicator.com/api/mcp --header "Authorization: Bearer lp_live_..."Claude Desktop (Settings, Developer, Edit config), through the mcp-remote bridge:
{
"mcpServers": {
"livepublicator": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://livepublicator.com/api/mcp", "--header", "Authorization: Bearer lp_live_..."]
}
}
}ChatGPT and the OpenAI API: in the Responses API or the Agents SDK, add an MCP tool with server_url set to the URL above and headers set to { "Authorization": "Bearer lp_live_..." }. Connectors inside the ChatGPT app ask for a sign-in (OAuth), which we don't offer yet: use the API route for now.
By using the API you accept the API terms. Questions: use the contact page.