• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » How to securely integrate HubSpot Contacts endpoints into WordPress custom plugins using Shortcode API

How to securely integrate HubSpot Contacts endpoints into WordPress custom plugins using Shortcode API

Prerequisites and Setup

Before diving into the integration, ensure you have a working WordPress environment and a HubSpot account. You’ll need to generate a HubSpot API key. Navigate to your HubSpot account settings, then to “Integrations” > “Private Apps”. Create a new private app and grant it “Contacts” read and write permissions. Copy the generated API key; this will be your authentication credential.

For this integration, we’ll create a custom WordPress plugin. The plugin will leverage the WordPress Shortcode API to expose a shortcode that fetches and displays HubSpot contacts. This approach offers flexibility and allows content creators to easily embed contact lists within posts and pages without direct code manipulation.

Plugin Structure and Initialization

Create a new directory for your plugin, e.g., hubspot-contacts-shortcode, within your WordPress installation’s wp-content/plugins/ directory. Inside this directory, create a main plugin file, e.g., hubspot-contacts-shortcode.php.

<?php
/**
 * Plugin Name: HubSpot Contacts Shortcode
 * Description: Integrates HubSpot Contacts API with WordPress using a shortcode.
 * Version: 1.0
 * Author: Antigravity
 */

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

// Define constants for API key and portal ID.
// It's highly recommended to store sensitive information like API keys securely,
// for example, in wp-config.php or via WordPress options, not directly in the plugin file.
// For demonstration purposes, we'll use constants here.
define( 'HUBSPOT_API_KEY', 'YOUR_HUBSPOT_PRIVATE_APP_API_KEY' ); // Replace with your actual API key
define( 'HUBSPOT_PORTAL_ID', 'YOUR_HUBSPOT_PORTAL_ID' ); // Replace with your actual Portal ID

// Include the main class file.
require_once plugin_dir_path( __FILE__ ) . 'includes/class-hubspot-contacts-api.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-hubspot-contacts-shortcode.php';

// Initialize the shortcode class.
function initialize_hubspot_contacts_shortcode() {
    new HubSpot_Contacts_Shortcode();
}
add_action( 'plugins_loaded', 'initialize_hubspot_contacts_shortcode' );

// Function to check if HubSpot API key and Portal ID are set.
function check_hubspot_settings() {
    if ( ( HUBSPOT_API_KEY === 'YOUR_HUBSPOT_PRIVATE_APP_API_KEY' || empty( HUBSPOT_API_KEY ) ) ||
         ( HUBSPOT_PORTAL_ID === 'YOUR_HUBSPOT_PORTAL_ID' || empty( HUBSPOT_PORTAL_ID ) ) ) {
        add_action( 'admin_notices', 'display_hubspot_settings_warning' );
    }
}
add_action( 'admin_init', 'check_hubspot_settings' );

function display_hubspot_settings_warning() {
    <<<HTML
    <div class="notice notice-warning is-dismissible">
        <p><strong>HubSpot Contacts Shortcode:</strong> Please configure your HubSpot API Key and Portal ID in the plugin file (or via secure methods) for the shortcode to function correctly.</p>
    </div>
    HTML;
}

Create an includes directory within your plugin folder. Inside includes, create two files: class-hubspot-contacts-api.php and class-hubspot-contacts-shortcode.php.

HubSpot API Interaction Class

This class will handle all direct communication with the HubSpot Contacts API. It will manage authentication, making HTTP requests, and parsing responses. We’ll use WordPress’s built-in HTTP API for making requests, which provides a robust and secure way to interact with external services.

<?php
// includes/class-hubspot-contacts-api.php

class HubSpot_Contacts_API {

    private $api_key;
    private $portal_id;
    private $base_url = 'https://api.hubapi.com';

    public function __construct() {
        $this->api_key = HUBSPOT_API_KEY;
        $this->portal_id = HUBSPOT_PORTAL_ID;
    }

    /**
     * Fetches contacts from HubSpot.
     *
     * @param array $args Optional arguments for filtering or pagination.
     * @return array|WP_Error An array of contacts or a WP_Error object on failure.
     */
    public function get_contacts( $args = array() ) {
        if ( empty( $this->api_key ) || empty( $this->portal_id ) ) {
            return new WP_Error( 'hubspot_api_error', __( 'HubSpot API key or Portal ID is not configured.', 'hubspot-contacts-shortcode' ) );
        }

        $endpoint = "{$this->base_url}/crm/v3/objects/contacts";
        $query_args = array(
            'limit' => isset( $args['limit'] ) ? intval( $args['limit'] ) : 10,
            'properties' => isset( $args['properties'] ) ? implode( ',', $args['properties'] ) : 'firstname,lastname,email',
            // Add more query parameters as needed, e.g., 'filterGroups', 'sorts'
        );

        // Add filters if provided
        if ( ! empty( $args['filters'] ) ) {
            $query_args['filterGroups'] = json_encode( $args['filters'] );
        }

        $url = add_query_arg( $query_args, $endpoint );
        $headers = array(
            'Authorization' => "Bearer {$this->api_key}",
            'Content-Type'  => 'application/json',
        );

        $response = wp_remote_get( $url, array(
            'headers' => $headers,
            'timeout' => 30, // Adjust timeout as needed
        ) );

        if ( is_wp_error( $response ) ) {
            return $response;
        }

        $body = wp_remote_retrieve_body( $response );
        $data = json_decode( $body, true );
        $status_code = wp_remote_retrieve_response_code( $response );

        if ( $status_code >= 200 && $status_code < 300 ) {
            return $data['results'] ?? array(); // Return contacts or empty array if no results
        } else {
            // Log the error for debugging purposes
            error_log( "HubSpot API Error: Status Code {$status_code}, Response: " . print_r( $data, true ) );
            return new WP_Error( 'hubspot_api_error', __( 'Failed to retrieve contacts from HubSpot.', 'hubspot-contacts-shortcode' ), $data );
        }
    }

    /**
     * Creates a new contact in HubSpot.
     *
     * @param array $properties Contact properties.
     * @return array|WP_Error The created contact data or a WP_Error object on failure.
     */
    public function create_contact( $properties ) {
        if ( empty( $this->api_key ) || empty( $this->portal_id ) ) {
            return new WP_Error( 'hubspot_api_error', __( 'HubSpot API key or Portal ID is not configured.', 'hubspot-contacts-shortcode' ) );
        }

        $endpoint = "{$this->base_url}/crm/v3/objects/contacts";
        $headers = array(
            'Authorization' => "Bearer {$this->api_key}",
            'Content-Type'  => 'application/json',
        );

        $body = json_encode( array(
            'properties' => $properties,
        ) );

        $response = wp_remote_post( $endpoint, array(
            'headers' => $headers,
            'body'    => $body,
            'timeout' => 30,
        ) );

        if ( is_wp_error( $response ) ) {
            return $response;
        }

        $body = wp_remote_retrieve_body( $response );
        $data = json_decode( $body, true );
        $status_code = wp_remote_retrieve_response_code( $response );

        if ( $status_code >= 200 && $status_code < 300 ) {
            return $data;
        } else {
            error_log( "HubSpot API Error (Create Contact): Status Code {$status_code}, Response: " . print_r( $data, true ) );
            return new WP_Error( 'hubspot_api_error', __( 'Failed to create contact in HubSpot.', 'hubspot-contacts-shortcode' ), $data );
        }
    }

    // Add more methods for other HubSpot API operations (update, delete, etc.) as needed.
}

Shortcode API Integration Class

This class will register the shortcode and define its behavior. It will instantiate the HubSpot_Contacts_API class to fetch data and then format it for display within the WordPress content. We’ll also include basic error handling and a fallback message if data cannot be retrieved.

<?php
// includes/class-hubspot-contacts-shortcode.php

class HubSpot_Contacts_Shortcode {

    private $api;

    public function __construct() {
        $this->api = new HubSpot_Contacts_API();
        add_shortcode( 'hubspot_contacts', array( $this, 'render_shortcode' ) );
    }

    /**
     * Renders the shortcode output.
     *
     * @param array $atts Shortcode attributes.
     * @return string HTML output for the shortcode.
     */
    public function render_shortcode( $atts ) {
        // Set default attributes and merge with user-provided ones.
        $atts = shortcode_atts( array(
            'limit'      => 10,
            'properties' => 'firstname,lastname,email', // Comma-separated list of HubSpot contact properties
            'filter_prop' => '', // Property to filter by
            'filter_val'  => '', // Value to filter by
        ), $atts, 'hubspot_contacts' );

        // Prepare filter arguments for the API call.
        $api_args = array(
            'limit'      => intval( $atts['limit'] ),
            'properties' => array_map( 'trim', explode( ',', $atts['properties'] ) ),
        );

        if ( ! empty( $atts['filter_prop'] ) && ! empty( $atts['filter_val'] ) ) {
            $api_args['filters'] = array(
                array(
                    'propertyName' => sanitize_text_field( $atts['filter_prop'] ),
                    'operator'     => 'EQ', // Exact match. Consider other operators like 'CONTAINS', 'GT', etc.
                    'value'        => sanitize_text_field( $atts['filter_val'] ),
                ),
            );
        }

        // Fetch contacts from HubSpot.
        $contacts = $this->api->get_contacts( $api_args );

        // Start output buffering.
        ob_start();

        if ( is_wp_error( $contacts ) ) {
            // Display error message if API call failed.
            echo '<p class="hubspot-error">' . esc_html( $contacts->get_error_message() ) . '</p>';
        } elseif ( empty( $contacts ) ) {
            // Display message if no contacts were found.
            echo '<p class="hubspot-no-contacts">' . esc_html__( 'No contacts found.', 'hubspot-contacts-shortcode' ) . '</p>';
        } else {
            // Display the contacts in a table or list.
            echo '<table class="hubspot-contacts-table">';
            echo '<thead><tr>';
            // Dynamically generate table headers based on requested properties.
            $requested_properties = $api_args['properties'];
            foreach ( $requested_properties as $prop ) {
                echo '<th>' . esc_html( ucwords( str_replace( '_', ' ', $prop ) ) ) . '</th>';
            }
            echo '</tr></thead>';
            echo '<tbody>';

            foreach ( $contacts as $contact ) {
                echo '<tr>';
                foreach ( $requested_properties as $prop ) {
                    $value = $contact['properties'][ $prop ] ?? '&nbsp;'; // Use   for empty cells
                    echo '<td>' . esc_html( $value ) . '</td>';
                }
                echo '</tr>';
            }
            echo '</tbody></table>';
        }

        // Return the buffered output.
        return ob_get_clean();
    }
}

Usage and Security Considerations

To use the shortcode, simply add [hubspot_contacts] to your WordPress posts or pages. You can customize the output using attributes:

  • limit: Number of contacts to display (e.g., [hubspot_contacts limit="5"]).
  • properties: Comma-separated list of HubSpot contact properties to fetch and display (e.g., [hubspot_contacts properties="firstname,lastname,phone,company"]). Ensure these properties exist in your HubSpot portal.
  • filter_prop and filter_val: To filter contacts based on a specific property and value (e.g., [hubspot_contacts filter_prop="lead_source" filter_val="Website"]).

Security Best Practices:

  • API Key Management: Never hardcode your HubSpot API key directly in the plugin file for production environments. Instead, use WordPress’s secure options API or define it in wp-config.php. For example, you could add define( 'HUBSPOT_API_KEY', getenv('HUBSPOT_API_KEY') ); to wp-config.php and manage the environment variable.
  • Input Sanitization: Always sanitize user inputs, especially when they are used in API requests (e.g., filter values). The example uses sanitize_text_field for filter values.
  • Error Handling and Logging: Implement robust error handling. Log API errors to the PHP error log for debugging. Avoid exposing sensitive error details to end-users.
  • Permissions: Ensure your HubSpot Private App has only the necessary permissions (e.g., read/write for contacts).
  • Rate Limiting: Be mindful of HubSpot API rate limits. For high-traffic sites, consider caching API responses to reduce the number of requests.
  • HTTPS: Always use HTTPS for your WordPress site and ensure all API calls are made over HTTPS.

Advanced Considerations: Caching and Performance

For performance optimization, especially on high-traffic websites, caching HubSpot API responses is crucial. You can implement a custom caching mechanism using WordPress Transients API. This will reduce the number of direct API calls and improve page load times.

<?php
// In class-hubspot-contacts-api.php, modify get_contacts method:

public function get_contacts( $args = array() ) {
    // ... (previous checks) ...

    // Generate a unique cache key based on arguments.
    $cache_key = 'hubspot_contacts_' . md5( json_encode( $args ) );
    $cached_data = get_transient( $cache_key );

    if ( false !== $cached_data ) {
        return $cached_data; // Return cached data if available.
    }

    // ... (API call logic as before) ...

    if ( $status_code >= 200 && $status_code < 300 ) {
        $results = $data['results'] ?? array();
        // Cache the results for a specific duration (e.g., 1 hour).
        set_transient( $cache_key, $results, HOUR_IN_SECONDS );
        return $results;
    } else {
        // ... (error handling) ...
    }
}

// In class-hubspot-contacts-shortcode.php, you might want to add an option to clear cache.
// For example, via an admin page or a specific shortcode attribute.
// For simplicity, this example doesn't include cache clearing UI.

Consider adding a shortcode attribute to control the cache duration or to force a cache refresh. For more complex scenarios, you might integrate with a dedicated caching plugin or a CDN.

Extending Functionality: Creating Contacts

You can extend this plugin to allow creating contacts via a form. This would involve adding a new shortcode, a form handler, and using the create_contact method from the HubSpot_Contacts_API class. Remember to implement thorough validation and security checks for any form submissions.

<?php
// Add this to class-hubspot-contacts-shortcode.php

// In the constructor:
add_shortcode( 'hubspot_create_contact', array( $this, 'render_create_contact_form' ) );

// New method to render the form:
public function render_create_contact_form( $atts ) {
    // Process form submission if data is present
    if ( isset( $_POST['hubspot_form_submit'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'hubspot_create_contact_nonce' ) ) {
        $properties = array();
        // Sanitize and collect properties from POST data
        // Example: assuming 'firstname', 'lastname', 'email' are submitted
        if ( isset( $_POST['firstname'] ) ) {
            $properties['firstname'] = sanitize_text_field( $_POST['firstname'] );
        }
        if ( isset( $_POST['lastname'] ) ) {
            $properties['lastname'] = sanitize_text_field( $_POST['lastname'] );
        }
        if ( isset( $_POST['email'] ) && is_email( $_POST['email'] ) ) {
            $properties['email'] = sanitize_email( $_POST['email'] );
        }
        // Add more properties as needed

        if ( ! empty( $properties ) ) {
            $result = $this->api->create_contact( $properties );

            if ( is_wp_error( $result ) ) {
                // Display error message
                echo '<p class="hubspot-error">' . esc_html( $result->get_error_message() ) . '</p>';
            } else {
                // Display success message
                echo '<p class="hubspot-success">' . esc_html__( 'Contact created successfully!', 'hubspot-contacts-shortcode' ) . '</p>';
                // Optionally clear cache if you are displaying contacts elsewhere
            }
        } else {
            echo '<p class="hubspot-error">' . esc_html__( 'No valid contact data provided.', 'hubspot-contacts-shortcode' ) . '</p>';
        }
    }

    // Start output buffering for the form
    ob_start();
    ?>
    <form method="post" action="" class="hubspot-create-contact-form">
        <p>
            <label for="firstname">:</label><br>
            <input type="text" id="firstname" name="firstname" required>
        </p>
        <p>
            <label for="lastname">:</label><>
            <input type="text" id="lastname" name="lastname" required>
        </p>
        <p>
            <label for="email">:</label><br>
            <input type="email" id="email" name="email" required>
        </p>
        <?php wp_nonce_field( 'hubspot_create_contact_nonce' ); ?>
        <input type="hidden" name="hubspot_form_submit" value="1">
        <p>
            <button type="submit"></button>
        </p>
    </form>
    <?php
    return ob_get_clean();
}

This provides a foundational structure for securely integrating HubSpot Contacts into your WordPress site using custom plugins and the Shortcode API, with considerations for performance and extensibility.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Performance Optimization: Tuning PHP-FPM and opcache pools for high-concurrency PayPal Checkout REST handlers
  • How to implement native Redis caching layers for high-volume custom taxonomy queries in Understrap styling structures
  • Step-by-Step Guide: Refactoring legacy hooks to use Repository and Interface Structure pattern in theme layers
  • How to build custom Elementor custom widgets extensions utilizing modern Shortcode API schemas
  • How to design secure Google Analytics v4 REST webhook listeners using signature validation and payload queues

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (658)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (872)
  • PHP (5)
  • PHP Development (38)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Server (23)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (17)
  • WordPress Plugin Development (17)
  • WordPress Plugin Development (330)
  • WordPress Theme Development (357)

Recent Posts

  • Performance Optimization: Tuning PHP-FPM and opcache pools for high-concurrency PayPal Checkout REST handlers
  • How to implement native Redis caching layers for high-volume custom taxonomy queries in Understrap styling structures
  • Step-by-Step Guide: Refactoring legacy hooks to use Repository and Interface Structure pattern in theme layers

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (872)
  • Debugging & Troubleshooting (658)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Business & Monetization (390)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala