HubSpot integration

This documentation is intended to help with the implementation of Internal Links modules in existing HubSpot CMS using Graphite's Internal Links API.

Graphite's Internal Links API integration with HubSpot

We maintain an integration application that runs a periodic job that will push the Graphite Internal Links API's data to HubSpot internal storage. This integration uses HubSpot CMS and CRM APIs to update Internal Links data extracted from the Internal Links API directly on HubSpot CMS sites.


HubSpot Account Requirements

The implementation described in this documentation requires a HubSpot Enterprise level account. Using another plan? Contact us for a discovery call to see how we can help.

Related links selection

Links for pages are selected to maximize relatedness subject to the constraint that every page receives at least k incoming links. The relatedness of a pair of pages is computed as the semantic similarity of the page text.

When a page does not have enough related pages, or a page has not yet been indexed, the API returns links to randomly selected pages to fulfill the SEO need for robust internal linking.


Internal Links selection information

The goal of the Internal Links API is to evenly distribute inlinks across pages on the site where the API is implemented. Relatedness can assist with improving the user experience to find other relevant content. Filling the remaining links will assist with SEO by allowing more pages to be picked and indexed by search algorithms.

  • New articles are discovered and crawled daily
  • Related links are computed weekly
  • When processing new articles that haven't had computation for Internal Links yet, the API will populate the Internal Links objects with random links. This is the default and recommended workflow.
    • This can be disabled upon request to only create new custom object instances when the related links data is available (at most once per week)
  • The integration can be ran daily or weekly, dependent on random link selection


Determining the number of Internal Links to return

We recommend 8 or more internal links per page to receive the most SEO benefit

Integration details


API key:

We will require access to an API key with the access to list, create and update CRM custom objects, and list Blog Post instances:

  • crm.schemas.custom.write and crm.schemas.custom.write to be able to create the schema for the custom objects where the data will be stored.
  • and crm.objects.custom.write to be able to list the existing objects, update them with the IL API and create the new ones.
  • content to be able to list the blog posts to process.

Within Graphite scope (Backend)

  • complete the Internal Links computation
  • create the custom object schema
  • create periodic jobs to update linking data

Outside of Graphite scope (Frontend)

  • Design of Internal Links modules


Storing Internal Links data in HubSpot

Internal Links data is stored in HubSpot CRM custom objects. The schema of these objects is designed by Graphite to contain the minimum data required to power the Internal Links modules.

The designed workflow has one instance of this custom object for each one of the published blog posts where the Internal Links module will be implemented. Each of these instance objects will contain a series of string properties that will represent the references to the corresponding related blog posts.

  • blog post URL
    • This property will store the URL of the blog post that the instance corresponds to. It will be used as the identifier of the instance and the object will contain the related links data of the post identified by this URL. The parameters that will be used to create this property are the following:
      • name: blog_post_url
      • label: Blog Post URL,
      • hasUniqueValue: true
      • type: string
      • field_type: text
  • number of links
    • Depending on the requested number of links to be provided by the API. There will be a series of n string properties that will hold the internal HubSpot ID of the nth related blog post.
      • name: related_post_n
      • label: Related Post Id N
      • type: string
      • field_type: text
  • parameters
    • The following parameters will be defined for the custom object created:
      • required_properties: [blog_post_url],
        searchable_properties: [blog_post_url],
        primary_display_property: "blog_post_url"

Using this two data, the designers can build the Internal Links sections filtering
the corresponding object for each post using the URL identifier and identifying the related blog posts using the IDs stored in the other properties mentioned.

When launching the integration for our clients sites, we create this schema using the HubSpot POST/crm/v3/schemas endpoint.

Updating Internal Links data in HubSpot

Graphite runs periodic jobs that will index the blog posts published on the site. For each one, we check if there is an instance of our custom CRM object created with this corresponding URL.

  • If the custom CRM object exists:
    • Graphite will update the outgoing links using the data provided by the Internal Links API data
  • If the custom CRM object is not found, and we have internal linking data:
    • Graphite will create a new instance using the current blog URL as the primary property and assign the data from the API to the remaining properties


  • We list the blog posts published in your site using the GET /cms/v3/blogs/posts endpoint. We prefer using the list of blogs instead of the sitemap for the integration for the following reasons:
    • access to the latest list of articles created
    • ability to filter articles based on data stored in the HubSpot blog posts properties in case it is needed
  • We list the existing instances of our custom Internal Links object, using HubSpot GET /cms/v3/objects/{objectsType} endpoint
  • Using the data stored in the blog post's properties, we build the list of URLs that will be queried by our API, and fetch this data
  • When the Internal Links API completes the response, we update the existing objects using the data fetched. To accomplish this, we use the PATCH /crm/v3/objects/{objectType}/{objectId} HubSpot endpoint.
  • For articles that don't have a corresponding object instance, we create a new instance using the data fetched for that particular post using PATCH /crm/v3/objects/{objectType}/{objectId}


Displaying Internal Links Data

The following snippet shows how to use the API in HubL and can be referenced for front-end implementation.

The following simplified example is for a design that contain 9 related links:

{% set blog_post_url = content.absolute_url %}
{% set internal_links = crm_object("internal_links", "blog_post_url=" ~ blog_post_url, "related_post_1,related_post_2,related_post_3,related_post_4,related_post_5,related_post_6,related_post_7,related_post_8,related_post_9") %}
{% set internal_link_post_ids = [internal_links.related_post_1,internal_links.related_post_2,internal_links.related_post_3,internal_links.related_post_4,internal_links.related_post_5,internal_links.related_post_6,internal_links.related_post_7,internal_links.related_post_8,internal_links.related_post_9] %}
{% set internal_link_posts = content_by_ids(internal_link_post_ids) %}
{% if internal_link_posts %}
  <h4>Similar articles</h4>
    {% for post in internal_link_posts %}
        <a href="{{ post.absolute_url }}">{{}}</a>
    {% endfor %}
{% endif %}

Please contact us with any questions