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
| Feature | Supported | Notes |
|---|---|---|
| Publish + update + unpublish | Yes | Full CRUD via /cms/v3/blogs/posts. |
| Native scheduling | Yes | state: SCHEDULED + publishDate (epoch ms). HubSpot scheduler handles flip. |
| Draft mode | Yes | state: DRAFT. Review in HubSpot under Marketing → Website → Blog → Drafts. |
| JSON-LD in <head> | Yes | headHtml field — HubSpot injects raw HTML into <head> on the published page. |
| Featured image upload | Yes | POST to /files/v3/files first; receive URL; attach as featuredImage. |
| Tags | Partial | HubSpot tags are numeric IDs. We resolve by name; tags must already exist in the portal. |
| Authors | Yes | blogAuthorId — resolved by name from /blogs/authors. Must exist in HubSpot first. |
| Campaigns | Partial | campaign field accepts a campaign GUID. Pass via adapter_metadata.default_campaign on the connection. |
| Smart content / personalisation | — No | We don't insert HubSpot personalization tokens. Add manually in the editor after publish if needed. |
| A/B testing | — No | Available in HubSpot UI; not exposed via the public Posts API. |
| Articles + FAQ pages | Yes | Publishes 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. |
Setup
- 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
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-…orpat-eu1-…depending on your portal’s region. - 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 yourblog_id. - 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
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
| Property | Type | Description |
|---|---|---|
| name | string | Article title (HubSpot calls it "name"). |
| slug | string | URL slug — appended to your blog's URL prefix. |
| contentGroupId | number | Your blog_id from setup step 3. |
| postBody | HTML | Pre-sanitised HTML. HubSpot renders directly inside the post body. |
| metaDescription, htmlTitle | string | Meta description + page title separately from the article name. |
| featuredImage, featuredImageAltText | string | Hero URL (uploaded to HubSpot Files first) + alt. |
| state | enum | PUBLISHED / SCHEDULED / DRAFT. |
| publishDate | epoch ms | Required when state is SCHEDULED. HubSpot uses epoch milliseconds — we convert from ISO automatically. |
| tagIds | number[] | Resolved from tag names; tags must exist in the portal first. |
| blogAuthorId | string | Resolved from author name; author must exist in /blogs/authors. |
| headHtml | string | Joined JSON-LD scripts. HubSpot injects this into <head> on the published page. |
| useFeaturedImage | boolean | true — 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
- 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
- Use a dedicated Private App for SEORAV — don’t bundle scopes with another integration.
- Rotate the token quarterly. HubSpot lets you regenerate without renaming the app — paste the new token into SEORAV after rotating.
- Review the Private App’s audit log every month: Settings → Private Apps → SEORAV → Logs. Every SEORAV publish + image upload + verify-fetch should appear there.
- If your portal switches data centre regions (rare), tokens become invalid — reissue + reconnect.