HubSpot integration

Generate PDFs from HubSpot

Connect Quaterio to your HubSpot CMS Hub with a private app token, bind site pages, landing pages or blog posts to block-based templates and render paginated PDFs on demand or in batch.

The problem

HubSpot is the marketing automation backbone for most CMS Hub teams. PDF output is not part of the platform. Sales reps and marketers end up copy-pasting page content into Word and styling it from scratch, then drifting from the live HubSpot copy on the next iteration.

How Quaterio handles it

Quaterio's HubSpot connector hits the CMS Pages API with a private app token in the Bearer header. Pages, landing pages and blog posts all surface as JSON. You bind specific fields (title, slug, hero image, publish date) to a template and skip HubSpot's full theme-rendered htmlBody, which usually contains chrome you do not want in a PDF.

How it works

  1. 1

    Create a private app in HubSpot

    Settings → Integrations → Private Apps → Create private app. Under Scopes enable content (read) for CMS Pages. Save and copy the Access token (starts with pat-).

  2. 2

    Pick the HubSpot preset in Connections

    Settings → Connections → New Connection → pick "HubSpot" from the type dropdown. The URL defaults to /cms/v3/pages/site-pages. Switch to landing-pages or blog posts by editing the path. Paste the private app token into the Token field.

  3. 3

    Hit Detect Fields to confirm the shape

    Quaterio fetches the endpoint and shows the JSON paths. Default is results.0.htmlTitle. Pick the fields you actually want, name, slug, url, publishDate, meta description, instead of binding the full htmlBody directly.

  4. 4

    Generate the PDF via the editor or API

    Export from the editor for one-offs. For batch jobs hit POST /api/v1/generate/template with the template ID. The connector re-fetches at generation time so every PDF reflects the latest HubSpot page state.

What the connector handles

  • CMS Pages API /cms/v3/pages/site-pages by default
  • Landing pages (/cms/v3/pages/landing-pages) and blog posts (/cms/v3/blogs/posts) one URL change away
  • Private app token authentication via Authorization Bearer header
  • State filtering (?state=PUBLISHED) so drafts never sneak into PDFs
  • Pagination via after cursor when iterating large content sets
  • Single-page reads by ID for deterministic per-PDF templates
  • Detect Fields walks the response so you can pick paths in the editor
  • Honest about htmlBody: contains theme chrome, use it as the source for a stripped HTML field instead
  • 15 second fetch timeout, cached on the block when the API is slow or unreachable
  • Refresh on demand from the editor, or via API before batch generation

Generate via API

# 1. Create the connection via the dashboard (one-time setup). # 2. Bind a template's Remote Content block to it. # 3. Generate a PDF that pulls live HubSpot content: curl -X POST https://quaterio.com/api/v1/generate/template \ -H "Authorization: Bearer q_your_token" \ -H "Content-Type: application/json" \ -d '{ "templateId": "hubspot_page_export" }' # → { # "ok": true, # "data": { "downloadUrl": ".../page_en_a7f3b2c1.pdf" } # } # The connector hits /cms/v3/pages/site-pages at generation time # and renders the bound page field into the template block.

Available on Business plan and above.

HubSpot and PDF: the real questions

The HubSpot preset queries /cms/v3/pages/site-pages with a Private App token in the Authorization header. Site pages, blog posts and landing pages are three separate endpoints; the preset starts with site pages because that is the most common CMS surface. Sections below cover the private app setup, scope requirements and the htmlBody size gotcha.

Creating a private app with the right scopes

In HubSpot, go to Settings → Integrations → Private Apps → Create private app. Give it a name, then under the Scopes tab enable the content scopes you need. For site pages enable content (read). For blog posts the same content scope covers them. Save the app and copy the Access token from the next screen.

The token format is "pat-XX-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", paste the whole string into the Token field on the Quaterio connection. Private app tokens do not expire on a fixed schedule; rotate them manually under the same page when needed.

Site pages vs landing pages vs blog posts

HubSpot exposes three separate CMS endpoints. /cms/v3/pages/site-pages returns the marketing site pages (about, pricing, etc.). /cms/v3/pages/landing-pages returns landing pages (campaign-specific). /cms/v3/blogs/posts returns blog content.

The preset URL defaults to site-pages because it is the most common surface for static content. Change the path on the URL to read landing pages or blog posts; auth and response shape are identical across all three endpoints.

List response shape and pagination

The list response wraps pages in a results array with paging info at the top level: { results: [{...}, {...}], paging: { next: { after: "...", link: "..." } } }. The default Response Field results.0.htmlTitle points at the first page's title.

Pagination uses after cursors, not offset. For a specific page, query by ID directly: /cms/v3/pages/site-pages/{pageId}. That returns the single page object without the results wrapper, adjust the Response Field path to drop the .0.

Why the default field is htmlTitle, not htmlBody

HubSpot pages have an htmlBody field but it contains the full theme-rendered HTML, including header, footer, navigation and tracking scripts. Binding that straight to a PDF block almost never produces what you want, your PDF gets the entire site chrome instead of the article body.

For PDF templates use specific fields instead: name, htmlTitle, slug, url, publishDate, meta description. For body content the cleanest path is to render the page in HubSpot to plain HTML with theme chrome stripped (HubSpot has a module-level export option) and store it as a separate field. Then bind that field to the Quaterio block.

Filtering and querying state

HubSpot supports query parameters for state (PUBLISHED, DRAFT, etc.), language, archived status, sorting and limit. Example: ?state=PUBLISHED&sort=-publishDate&limit=10. Pass them through on the URL.

Note that HubSpot uses a normalized state field plus currentState which can differ during a publish-in-progress. For PDF output use state=PUBLISHED to keep things deterministic, drafts can flicker into the result set otherwise.

Tier requirements (HubSpot side)

CMS Hub Starter, Professional and Enterprise all expose the CMS Pages API. The free HubSpot CRM tier does not include CMS Hub, so the site-pages endpoint will return an empty list (or 403 depending on scope grants).

Private apps are available on every paid HubSpot tier, including Marketing Hub and Sales Hub Professional. The content scope specifically is gated to accounts with CMS Hub or specific marketing tier access. Check Settings → Integrations → Private Apps to confirm the scope appears in the picker.

Rate limits

HubSpot enforces 100 requests per 10 seconds per private app on the standard API (10/s sustained). Burst is allowed within the 10 second window. For typical PDF batch jobs this is more than enough; for very large batches lower the Quaterio batch concurrency.

HubSpot Enterprise and certain integrations qualify for higher limits. If you are doing thousands of PDFs per minute, check the HubSpot API limit docs for your specific tier or request an increase from HubSpot support.

Pricing and plan tier

CMS connections are part of the Quaterio Business plan and above ($30/mo). Pro plans get the editor and basic templates but not the Remote Content block. Free and Pro users who try to add a Remote Content block see an upgrade prompt.

HubSpot CMS Hub Starter is $25/mo. Professional is $400/mo. Enterprise is $1, 200/mo. The CMS Pages API is included on every CMS Hub tier; there is no per-call cost from Quaterio.

Try the HubSpot connector free

Set up the connection in two minutes. No credit card required to start.