HubSpot logo

HubSpot

Available · Tier 1 (CMS Hub Pro+ required)

Built for marketing teams already on HubSpot Content Hub. Per-portal Private App tokens scope what we can do, the API is well-documented, and content publishes through HubSpot’s native blog module so it works with HubDB, smart content, and personalisation tokens out-of-the-box.

How it works

HubSpot Content Hub (formerly CMS Hub) hosts your blog under a numeric blog_id. We POST to /cms/v3/blogs/posts with that ID, the body HTML, meta fields, and a headHtmlchunk for JSON-LD. HubSpot renders the article on your domain’s blog page (URL pattern set in Settings → Website → Blog → URL structure).

We require CMS Hub Professionalor higher — Marketing-only portals don’t expose the blog module via API. Our probe checks this on connect and refuses the integration with a clear error if your tier doesn’t have it.

What we support

FeatureSupportedNotes
Publish + update + unpublish YesFull CRUD via /cms/v3/blogs/posts.
Native scheduling Yesstate: SCHEDULED + publishDate (epoch ms). HubSpot scheduler handles flip.
Draft mode Yesstate: DRAFT. Review in HubSpot under Marketing → Website → Blog → Drafts.
JSON-LD in <head> YesheadHtml field — HubSpot injects raw HTML into <head> on the published page.
Featured image upload YesPOST to /files/v3/files first; receive URL; attach as featuredImage.
Tags PartialHubSpot tags are numeric IDs. We resolve by name; tags must already exist in the portal.
Authors YesblogAuthorId — resolved by name from /blogs/authors. Must exist in HubSpot first.
Campaigns Partialcampaign field accepts a campaign GUID. Pass via adapter_metadata.default_campaign on the connection.
Smart content / personalisation NoWe don't insert HubSpot personalization tokens. Add manually in the editor after publish if needed.
A/B testing NoAvailable in HubSpot UI; not exposed via the public Posts API.
Articles + FAQ pages YesPublishes not only blog articles but also FAQ / answer-engine pages. One connection per content type — point each at a different HubSpot blog so the two surfaces stay distinct.
One connection per content type
HubSpot connections are scoped to a single content type. To publish both long-form articles AND FAQ / answer-engine pages, add two HubSpot connections — one for articles, one for answer pages. The same Private App token works for both; point each at a different HubSpot blog so editorial flow stays separate.

Setup

  1. 1

    Create a Private App

    In HubSpot: Settings (gear icon) → Integrations → Private Apps → Create a private app. Name it SEORAV.

    Private Apps replaced API keys (deprecated in late 2022). Tokens don’t expire unless revoked — but you can rotate them at any time from the same screen.
  2. 2

    Grant the right scopes

    On the Scopes tab enable:

    • content — Read + Write (the main one)
    • files — Read + Write (so we can upload featured images)
    • files.ui_hidden.read — Read (so we can verify post-publish)

    Save the app and copy the access token. Format: pat-na1-… or pat-eu1-… depending on your portal’s region.

  3. 3

    Find your Blog ID

    In HubSpot: Marketing → Website → Blog → pick your blog → Settings (top right). Look at the URL bar: /blog/{portalId}/{blogId}/.... The number after the portal ID is your blog_id.

  4. 4

    Connect inside SEORAV

    Integrations → Connect → HubSpot. Paste the access token + blog ID. Probe verifies the tier (Content Hub Pro+), scopes, and that the blog ID actually exists.

What we send to HubSpot

http
POST https://api.hubapi.com/cms/v3/blogs/posts
Authorization: Bearer pat-na1-•••
Content-Type: application/json

{
  "name":           "How to choose a reverse-osmosis system in 2026",
  "slug":           "how-to-choose-reverse-osmosis-system-2026",
  "contentGroupId": 86274513,                                  // your blog id
  "postBody":       "<h2>What you actually need to know</h2><p>…</p>",
  "metaDescription":"Membrane stages, recovery rate, remineralisation. Plain-English …",
  "htmlTitle":      "Reverse osmosis · the 3 specs that matter (2026)",
  "featuredImage":  "https://cdn.seorav.com/articles/9b1c5e0a/hero.webp",
  "featuredImageAltText": "Three reverse-osmosis systems on a kitchen counter",
  "state":          "PUBLISHED",
  "publishDate":    1714194000000,
  "tagIds":         [12345, 67890],
  "blogAuthorId":   "12345678",
  "campaign":       null,
  "headHtml":       "<script type=\"application/ld+json\">…</script>\n<script type=\"application/ld+json\">…</script>",
  "useFeaturedImage": true
}

Field mapping

PropertyTypeDescription
namestringArticle title (HubSpot calls it "name").
slugstringURL slug — appended to your blog's URL prefix.
contentGroupIdnumberYour blog_id from setup step 3.
postBodyHTMLPre-sanitised HTML. HubSpot renders directly inside the post body.
metaDescription, htmlTitlestringMeta description + page title separately from the article name.
featuredImage, featuredImageAltTextstringHero URL (uploaded to HubSpot Files first) + alt.
stateenumPUBLISHED / SCHEDULED / DRAFT.
publishDateepoch msRequired when state is SCHEDULED. HubSpot uses epoch milliseconds — we convert from ISO automatically.
tagIdsnumber[]Resolved from tag names; tags must exist in the portal first.
blogAuthorIdstringResolved from author name; author must exist in /blogs/authors.
headHtmlstringJoined JSON-LD scripts. HubSpot injects this into <head> on the published page.
useFeaturedImagebooleantrue — tells HubSpot's blog template to render the featured image at the top of the post.

Publishing options

Auto-publish

state: "PUBLISHED". The post goes live immediately at {your-blog-url}/{slug}.

Scheduled

state: "SCHEDULED" + publishDate (epoch ms). HubSpot’s scheduler handles the flip; HubSpot doesn’t need a cron service running on your end.

Draft

state: "DRAFT". Review in Marketing → Website → Blog → Drafts, edit if needed, then publish.

Activity timeline · what you’ll see

Activity timeline · what shows up in your dashboard
  • job_queuedidempotency_key=8b3c…
  • adapter_requestPOST/files/v3/files (upload featured image)
  • adapter_response2011.1s
  • adapter_requestPOST/cms/v3/blogs/posts
  • adapter_response201684mspost_id=1234567890
  • verify_startGET https://yoursite.com/blog/how-to-…
  • verify_complete200301msscore 92/100 · headHtml in <head> ✓
  • job_succeeded

After publishing — make it shine

Add personalisation tokens

HubSpot’s magic is the smart-content engine. After SEORAV publishes, open the post and add personalisation tokens for first-name CTAs, country-specific blocks, etc. We don’t auto-insert because we don’t know your contact-list segmentation.

Verify campaign tracking

If you tagged the post with a campaign (via adapter_metadata.default_campaign), check the Campaign report after a few days — UTM params, contact attribution, view counts should all be tied to the campaign.

Set up a follow-up workflow

Trigger a workflow when a contact reads the article: HubSpot → Workflows → enrolment trigger = “page view” on the published URL → email a related PDF or kick off lead scoring.

Submit the URL to GSC + Bing

HubSpot doesn’t auto-submit to search engines. Add the live URL to Google Search Console’s URL Inspection tool and request indexing. For Bing, the IndexNow API is the fastest path.

A/B test the headline

HubSpot allows A/B testing of blog post titles in the editor. After publish, click Edit → A/B test → Create variation. We’ll generate a second headline if you ask in the SEORAV Articles panel.

Watch organic search traffic in HubSpot Reports

HubSpot Reports → Marketing → Website → Filter by “new blog post”. After 4-6 weeks of indexing, look for impressions + click trajectory; if it’s flat after 6 weeks, the title choice is the issue (we can re-categorise).

Troubleshooting

“CMS Hub Pro+ required” on connect

Your portal is on Marketing Hub only (no CMS module), or on CMS Hub Starter (no API access to blog posts). Upgrade to CMS Hub Professional or higher; check at Account → Account & billing → Plan.

403 / “You do not have permissions”

Your private app is missing scopes. Go to Settings → Integrations → Private Apps → SEORAV → Scopes and ensure content and files are both checked with Read + Write. After adding scopes, click Update app and run a healthcheck in SEORAV — the existing token will start working without rotation.

Tags not appearing on the post

HubSpot tags must exist before we reference them. SEORAV doesn’t create tags via API (the public API doesn’t expose tag creation reliably). Create the tags you want in Marketing → Website → Blog → Settings → Tags first, then re-run the publish.

Featured image upload returns 415

HubSpot Files API requires multipart/form-data, not JSON. We use the right encoding internally — if you’re seeing 415s, it’s most likely the file extension. WebP support landed in HubSpot in 2023; older portals may still reject. Convert hero images to JPG/PNG via Settings → Integrations → Private Apps → SEORAV → Logs, find the failing call, and inspect the file headers.

Security & best practices

Storage
Private App access tokens encrypted with AES-256-GCM at rest. RLS blocks the authenticated role; only the publish worker decrypts. Plaintext is never logged. Tokens are scoped per-portal — leak of one token only affects one HubSpot account.

Useful links