Use the API and MCP Server
Everything the dashboard does at the click of a button — atomize a page, generate posts, publish them — can be driven from your own pipeline instead. The API is the same product through a different door: every endpoint calls the exact same code the dashboard buttons do, so a post created over the API is indistinguishable from one created in the app.
Before you start
- Buy API credits in Settings → Billing (API credits card). Your first purchase also unlocks creating API keys.
- Create a key in Settings → API. The key is shown once — copy it immediately. Only a hash is stored; if you lose it, revoke it and mint a new one.
- Keys act as the whole workspace, so only workspace admins can create or revoke them. Every member can see which keys exist and when each was last used.
Authentication
Send the key on every request:
Authorization: Bearer psp_live_...
One 401 shape covers every auth failure — missing, malformed, unknown, and revoked keys are deliberately indistinguishable.
Use the www. host in every request. The bare apex domain answers with a 308 redirect, and many HTTP clients won't re-send a POST through a redirect — the request looks like it silently did nothing.
What calls cost
Reads are free. The expensive calls draw on your prepaid API credit balance (Settings → Billing):
| Call | Cost |
|---|---|
POST /api/v1/generate |
$0.15 per post created |
POST /api/v1/atomize |
$0.40 per call |
POST /api/v1/posts/{id}/publish |
$0.05 per call |
POST /api/v1/network/connections |
$0.15 per post written |
POST /api/v1/network/campaigns |
$0.15 for the first post (none with write_first_post: false) |
POST /api/v1/network/campaigns/{id}/run |
$0.15 per post written |
POST /api/v1/network/drafts/publish |
$0.05 per post published |
Generate is billed per post, not per request: one call drafts a post for every connected channel of the page's domain, so a call that creates posts for 4 channels debits $0.60. The balance is checked before any work runs — an out-of-credits call is refused with 402 payment_required and costs nothing. A call is only debited when it succeeds, and a generate that created nothing (every channel already covered) is free. Publishing to X (Twitter) additionally debits your X credit wallet, exactly as it does from the dashboard — two balances, two different costs.
Note that generate also spends your plan's monthly distribution credits (the same pool the dashboard's generate button uses). The API response separates the two: out_of_plan_credits means upgrade your plan, 402 means buy API credits.
Rate limits
Every key gets 60 requests per minute, reads included. Over the limit you get 429 rate_limited with a Retry-After header saying how many seconds to wait.
Errors
Every error has the same shape, with a stable machine-readable code your pipeline can branch on:
{ "error": { "code": "payment_required", "message": "This call costs $0.10 and your API credit balance is $0.00. ..." } }
Codes: unauthorized (401), forbidden (403), not_found (404), invalid_request (400), payment_required (402), rate_limited (429), server_error (500).
Endpoints
GET /api/v1/posts — list the workspace's posts. Free.
Query filters: domain_id, platform, status, limit (default 50, max 200).
Each post includes image_url and video_url — the media attached by generation. This matters for Pinterest, which cannot publish a text-only pin: a Pinterest post with a null image_url will not go out, so check that field before publishing one.
curl -H "Authorization: Bearer psp_live_..." \
"https://www.parasiteseoposter.com/api/v1/posts?status=generated&limit=10"
POST /api/v1/atomize — fetch a URL, extract content atoms, store the page. The URL's domain must already exist in the workspace (add it in the dashboard first). The domain is matched on host, so www. and apex forms both resolve; pass domain_id explicitly to skip matching entirely.
curl -X POST -H "Authorization: Bearer psp_live_..." -H "Content-Type: application/json" \
-d '{"url": "https://yoursite.com/some-page"}' \
"https://www.parasiteseoposter.com/api/v1/atomize"
Returns page_id — hold onto it for the next step.
Images
Posts need pictures — Pinterest cannot publish a post without one, and LinkedIn posts with an image perform better.
Most of the time this is automatic. Atomizing captures the page's own share image (og:image / twitter:image), re-hosts it, and attaches it to the page — generation then carries it onto every post. The atomize response tells you whether that worked: image_attached: true means you're done. Only when it's false (the page has no share image) do you need to attach one yourself.
Images attach to the page, and generation carries whatever the page has onto its posts, so the order is: atomize → attach an image, if needed → generate.
GET /api/v1/images/search?q=… — search Pexels and Unsplash, the same library the dashboard's picker uses. Free.
curl -H "Authorization: Bearer psp_live_..." \
"https://www.parasiteseoposter.com/api/v1/images/search?q=coffee%20roasting"
POST /api/v1/pages/{page_id}/images — attach one. Free. Two accepted shapes:
# Your own image
curl -X POST -H "Authorization: Bearer psp_live_..." -H "Content-Type: application/json" \
-d '{"image_url": "https://yoursite.com/hero.jpg"}' \
"https://www.parasiteseoposter.com/api/v1/pages/PAGE_ID/images"
# A stock result — echo the object back from the search response
curl -X POST -H "Authorization: Bearer psp_live_..." -H "Content-Type: application/json" \
-d '{"image": {"source":"unsplash","source_url":"https://images.unsplash.com/...","attribution_text":"Photo by … on Unsplash"}}' \
"https://www.parasiteseoposter.com/api/v1/pages/PAGE_ID/images"
Either way the file is downloaded, stripped of EXIF, re-encoded and re-hosted on our storage, so a post never depends on a URL we don't control. GET the same path to list what a page already has. Stock photos carry attribution_text — both providers require visible credit where the image is shown.
POST /api/v1/generate — draft posts for an atomized page, one per connected channel of the page's domain. Honors your approval setting: with approval required, posts land at needs_review; without it, they're auto-scheduled.
curl -X POST -H "Authorization: Bearer psp_live_..." -H "Content-Type: application/json" \
-d '{"page_id": "..."}' \
"https://www.parasiteseoposter.com/api/v1/generate"
Returns created, skipped (channels that already had a live post for this page), failed, and out_of_plan_credits.
POST /api/v1/posts/{id}/publish — publish a post now, or schedule it:
curl -X POST -H "Authorization: Bearer psp_live_..." -H "Content-Type: application/json" \
-d '{"scheduled_at": "2026-09-01T12:00:00Z"}' \
"https://www.parasiteseoposter.com/api/v1/posts/POST_ID/publish"
An empty body (or {}) publishes at the next available slot. Posts written for hosts without a publishing API (Substack, Google Sites, …) are refused here — those go out through the dashboard's hand-off flow.
Network
Everything on the Network tab is in the API too: find pages that already rank, point posts at them, and run it on autopilot. Every Network endpoint is per domain, so start with GET /api/v1/domains.
| Endpoint | What it does | Role |
|---|---|---|
GET /api/v1/domains |
The workspace's domains | — |
GET /api/v1/network?domain_id= |
Overview: each network, drafts waiting, autopilot mode | — |
GET /api/v1/network/candidates?domain_id= |
What to start a network from: best-ranking posts and host pages | — |
GET /api/v1/network/accounts?domain_id= |
Accounts that can carry a link (the account_ids the calls below take) |
— |
GET /api/v1/network/nodes/{id}/posts |
One network: its centre and every post pointing at it | — |
GET /api/v1/network/performance?domain_id= |
Published posts ranked by how they're doing | — |
GET /api/v1/network/connections?domain_id= |
The planner's next suggested posts, with reasons | — |
POST /api/v1/network/connections |
Write one suggestion {node_id, account_id, reasons} |
editor |
POST /api/v1/network/start |
Make a node from {domain_id, url, to_page_id?} or a candidate {kind, id} |
editor |
GET /api/v1/network/campaigns?domain_id= |
The domain's campaigns | — |
POST /api/v1/network/campaigns |
Build a campaign {node_id, account_ids, duration?, posts_per_week?, content_strategy?, write_first_post?} |
editor |
POST /api/v1/network/campaigns/{id} |
{status: "active" | "paused" | "cancelled"} |
editor |
POST /api/v1/network/campaigns/{id}/run |
Write the campaign's next post now | editor |
GET/POST /api/v1/network/autopilot |
Read or set {domain_id, mode: "auto" | "review" | "off", posts_per_day?, account_ids?, content_strategy?} |
admin |
POST /api/v1/network/drafts/publish |
Publish every waiting network draft {domain_id, node_id?} |
editor |
Roles. A key acts as the member who created it, with that member's current role. Only admins can create keys, so keys normally have admin. If the creator is later demoted, their keys can only do what the new role allows. If the creator is removed from the workspace, the Network write calls return 403 forbidden, and a new key fixes it.
Autopilot decides whether posts go live. A post written through the API follows the domain's autopilot mode, the same as one written in the app. Under auto it is scheduled to publish. Under review or off it waits as a draft. Setting autopilot doesn't publish the drafts already waiting: call drafts/publish for those. If the workspace requires approval for everything (Settings), auto is saved but posts still wait, and the response says so with approval_required_by_workspace: true.
A typical run from nothing:
H='Authorization: Bearer psp_live_...'; API=https://www.parasiteseoposter.com/api/v1
curl -H "$H" "$API/domains" # → domain_id
curl -H "$H" "$API/network/accounts?domain_id=D" # → account ids
curl -X POST -H "$H" -H 'Content-Type: application/json' \
-d '{"domain_id":"D","url":"https://www.reddit.com/r/.../comments/..."}' "$API/network/start" # → node_id
curl -X POST -H "$H" -H 'Content-Type: application/json' \
-d '{"node_id":"N","account_ids":["A1","A2"],"duration":"4w"}' "$API/network/campaigns"
curl -X POST -H "$H" -H 'Content-Type: application/json' \
-d '{"domain_id":"D","mode":"auto"}' "$API/network/autopilot"
MCP
The API is also an MCP server, so Claude and other MCP clients can drive it conversationally. Point a Streamable HTTP client at:
https://www.parasiteseoposter.com/api/v1/mcp
From Claude (claude.ai or the desktop app):
- Open Settings → Connectors → Add custom connector.
- Paste the URL above and click Connect.
- Sign in to Parasite SEO Poster, pick the workspace, and click Allow.
There's no key to copy. It uses OAuth, and only a workspace admin or owner can allow it. It's available on paid plans, or on any workspace that has bought API credits. If a workspace drops to Free, the connection stops within the hour. The same steps, with a copy button for the URL, are on Settings → API and at the bottom of Network → Overview. The connection acts as the person who allowed it, with their current role, and draws on the same API credit balance as a key. It shows up in Settings → API next to your keys, and Disconnect there ends its access immediately.
With an API key, send the same Authorization: Bearer psp_live_... header. For Claude Code:
claude mcp add --transport http parasiteseoposter https://www.parasiteseoposter.com/api/v1/mcp \
--header "Authorization: Bearer psp_live_..."
The tools — list_posts, atomize_page, search_images, attach_image, generate_posts, publish_post, and the Network set (list_domains, get_network_overview, list_network_start_candidates, list_network_accounts, get_network_posts, get_network_performance, suggest_network_connections, write_network_connection, start_network, list_network_campaigns, create_network_campaign, set_network_campaign_status, run_network_campaign, get_network_autopilot, set_network_autopilot, publish_network_drafts) — are thin wrappers over the endpoints above, with the same billing, limits, roles and errors. So once Claude is connected, you can ask it things like "start a network around this thread and turn autopilot on."
Each tool carries safety hints the client reads before it runs anything. The reads are marked read-only. publish_post is marked destructive, and so is every Network tool that can put a post in public or end a campaign. That marking is what makes a well-behaved client stop and confirm before acting on a social account. Machine-readable details are at /mcp.json.
The Claude Code plugin
There is a packaged version of the same connection, which also installs the workflow — what order the calls go in, which ones cost money, and why an image has to be attached before you generate:
/plugin marketplace add bryandoreian/parasiteseoposter-claude
/plugin install parasite-seo-poster@parasite-seo-poster
Claude Code prompts for your API key on install and stores it as a plugin secret; change it later with /plugin config. It adds three commands:
| Command | |
|---|---|
/parasite-seo-poster:distribute <url> |
Atomize a URL through to reviewable drafts, stopping before anything publishes |
/parasite-seo-poster:drafts |
List pending drafts and flag the ones that can't publish as they stand |
/parasite-seo-poster:check-setup |
Verify the key, connection and credit state using only free calls |
Full write-up: parasiteseoposter.com/mcp.
Revoking a key
Revoke in Settings → API. Revocation is immediate — anything still using the key starts getting 401s on its next request. Revoked keys stay listed (with when they were revoked) so the audit trail survives.
Monitoring usage per key
Every metered call records which key spent the credits, so Settings → API shows a Spend (30d) column per key — dollars and call count, with all-time totals on hover. Give each pipeline its own key and you can see which one is burning the balance, then revoke just that one.