← Back to RankBloom

Publish API

RankBloom writes SEO blog content for the websites you connect to it. The Publish API is how that finished content leaves RankBloom and reaches wherever you want it — a WordPress site, a CMS, an automation platform, or your own code.

It is the same API behind the official WordPress plugin and the Zapier and Make integrations.

Base URL

https://app.getrankbloom.com/api/publish

Authentication

Every request carries a connection token. Create one in RankBloom under Connections: pick the site, choose the platform, and click Create connection. The token is shown once and stored only as a hash, so keep a copy when you make it.

A token is scoped to a single site. Send it in any one of these headers:

Authorization: Bearer rb_live_xxxxxxxx
X-RankBloom-Token: rb_live_xxxxxxxx
X-API-KEY: rb_live_xxxxxxxx

Three are accepted because shared and managed hosts routinely strip Authorization, and some automation platforms send an X-API-KEY header by default.

Never put the token in a query string. It would be recorded in access logs and by every proxy along the way. The API does not read it from there.

Revoking a connection takes effect immediately. Revoked and unknown tokens return the same response, so a caller cannot use the difference to probe for valid tokens.

Endpoints

GET /v1/ping

Verifies a token and reports what it is bound to. Use it as a connection test.

curl https://app.getrankbloom.com/api/publish/v1/ping \
  -H "Authorization: Bearer rb_live_xxxxxxxx"
{
  "ok": true,
  "contract_version": 1,
  "label": "Example Site",
  "connection": { "id": "…", "name": "Make — Example Site", "platform": "make" },
  "site": { "id": "…", "name": "Example Site", "url": "https://example.com" }
}

label is a flat, human-readable name for the connection. Automation platforms use it to label the connected account.

GET /v1/posts

Recent posts for the connected site, newest first. Read-only — it never changes state, so it is safe to poll on a timer.

ParameterTypeDefaultNotes
limitinteger25Maximum 100
offsetinteger0Skip this many posts
sinceISO 8601Only posts created at or after this time
curl "https://app.getrankbloom.com/api/publish/v1/posts?limit=100&offset=0" \
  -H "Authorization: Bearer rb_live_xxxxxxxx"
{
  "ok": true,
  "contract_version": 1,
  "count": 46,
  "limit": 100,
  "offset": 0,
  "posts": [ … ]
}

Paging. Increase offset by limit to walk back through the archive. There is no total count: a page shorter than limit means you have reached the end. Results are ordered by creation time and then by id, so paging stays stable even when two posts share a timestamp.

POST /v1/status

Tell RankBloom what happened to a post. This is what turns "we handed it over" into "it is live", and it stops the post being offered again.

curl -X POST https://app.getrankbloom.com/api/publish/v1/status \
  -H "Authorization: Bearer rb_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "post_id": "b86cbffe-…",
    "status": "delivered",
    "external_url": "https://example.com/my-post",
    "external_id": "123"
  }'
FieldRequiredNotes
post_idyesThe id from /v1/posts
statusyesdelivered, failed or skipped
external_urlnoWhere it went live
external_idnoThe remote system's own id
errornoWhy it failed, if it did

A token may only report on posts belonging to its own site.

The post payload

One canonical shape, versioned by contract_version.

FieldTypeNotes
idstringStable. Use it as your idempotency key
title, slug, excerptstringslug is the final, de-duplicated one
content_html, content_mdstringThe body, in both formats
meta_title, meta_descriptionstringSEO metadata
primary_keyword, keywords, category, tagsstring / arrayTaxonomy
canonical_urlstringCanonical, when set
schema_json_ldobjectParsed, never a JSON string
featured_image_urlstringHosted image, ready to use
audio_urlstringNarration, when the post has it
audio_duration_seconds, audio_voicenumber / stringNarration detail
statusstringpublished or draft
author, author_title, author_urlstringByline
created_at, published_atISO 8601Timestamps

Every key is always present. A value that does not apply comes back null — it is never omitted. A post without narration simply has audio_url: null; that is the normal case, not an error. Write your integration to expect nulls rather than missing keys.

The contract is additive only. Fields are never renamed, retyped or removed, because integrations in the wild keep requesting version 1 indefinitely. A breaking change would ship as a new version served alongside this one.

Errors

Errors return a matching HTTP status and a JSON body with a human-readable message.

{ "ok": false, "error": "Invalid or revoked connection token." }
StatusMeaning
400A required field is missing or malformed
401Token missing, unknown or revoked
404The post does not belong to this connection

Ready-made integrations

You do not have to write code. The same API is already wrapped for:

Support

Questions about the API, or something not behaving as documented — email support@getrankbloom.com.