Managing 5–20+ HubSpot client portals? This guide covers the portal column pattern, OAuth token management, rate limits, and the tools that support multi-portal Google Sheets reporting natively.
If you run an agency with five clients on HubSpot, you have five separate portals with five separate sets of contacts, deals, form submissions, and pipeline data. Your team probably spends meaningful time each week logging into each one separately, exporting reports, and stitching them together in a spreadsheet.
There is no native HubSpot feature that consolidates multiple portals into a single Google Sheet view. Every portal has its own authentication, its own API rate limits, and its own data schema. Building a consolidated reporting sheet requires working around all three.
This guide explains the practical patterns agencies actually use, the tools that support multi-portal natively, and the manual approach that works when your budget does not allow a dedicated tool.
Why Multi-Portal Consolidation Is Harder Than It Looks
The core challenge is authentication. Each HubSpot portal is a completely separate OAuth environment. When you connect a tool to HubSpot, you are authorizing it to access a specific portal's data using that portal's OAuth tokens. To access five portals, you need five separate authorizations, and those tokens expire and need to be refreshed independently.
Most consumer integrations (including Zapier's standard HubSpot connection and the native HubSpot Workflow action) are designed for a one-portal-one-connection model. They handle a single authorization and a single data stream. Scaling that to 20 portals means 20 separate connections, each needing its own setup, monitoring, and maintenance.
Beyond authentication, there are two more structural problems:
Data schema differences. Each client portal may have different custom properties, pipeline stage names, and deal structures. A consolidated sheet needs to either normalize these into a common schema or handle differences gracefully per row.
Rate limits per portal. HubSpot's API enforces limits at the portal level: 100 requests per 10 seconds for standard accounts, 150 for Enterprise. A script pulling data from 10 portals simultaneously must manage 10 separate rate-limit budgets, or it will start receiving 429 errors that are hard to debug across portals.
The Portal Column Pattern
The most fundamental design decision for any multi-portal Sheet is adding a dedicated portal identifier column. This is simple but non-negotiable.
Every row written to the consolidated sheet should include a portal identifier: either the HubSpot portal ID (a numeric value found at Settings > Account Setup > Account Information) or a human-readable client name.
Why this matters:
- You cannot distinguish which client a row belongs to without it
- Filtering and pivot tables by client require this column
- When a sync error occurs, you need to know which portal it came from
- If a client's data needs to be removed (end of contract), you filter by portal ID and delete
Implementation:
In Apps Script, hardcode the portal identifier as a constant at the top of each portal-specific script:
const PORTAL_ID = '12345678'; const CLIENT_NAME = 'Client A';
Write it as the first or last column of every row you append. In third-party tools, look for a "custom column" or "static value" field in the mapping configuration.
Recommended portal column placement:
Put the portal identifier in column A. This makes filtering by client the first operation when you open the sheet, and keeps it visible when scrolling right through many data columns.
Per-Portal OAuth Token Management
If you are building a custom solution with Apps Script or a server-side script, token management is the most operationally demanding aspect of multi-portal work.
HubSpot OAuth tokens have a 6-hour expiry for access tokens and 6-month expiry for refresh tokens. Your script needs to:
- Store each portal's refresh token securely (not in the spreadsheet itself)
- Use the refresh token to obtain a new access token before each API call
- Handle token revocation gracefully if a client changes their HubSpot admin password or revokes app access
In Apps Script, use PropertiesService to store tokens server-side rather than in spreadsheet cells:
PropertiesService.getScriptProperties().setProperty('ACCESS_TOKEN_12345678', token);This keeps credentials out of the sheet and away from anyone with view access.
Rotation schedule: Build a function that refreshes all tokens on a daily trigger regardless of whether they are needed. This prevents stale token errors from disrupting reporting overnight and gives you a predictable maintenance window.
When a client offboards: Revoke the app authorization in their HubSpot portal (Settings > Integrations > Connected Apps) and delete their tokens from your PropertiesService storage. Leaving stale credentials is a security risk.
Handling Per-Portal Rate Limits
Each HubSpot portal has its own rate limit bucket. A script making concurrent API calls to 10 portals is making calls against 10 separate buckets, in theory, you can make 100 requests per 10 seconds per portal, for a total of 1,000 requests per 10 seconds across 10 portals.
In practice, the problem is not raw volume but burst handling. If your script tries to pull all 10 portals simultaneously at startup, it may overload your own execution environment or hit Google's URL fetch quotas (Apps Script allows 20,000 URL fetch calls per day on free accounts, 100,000 on Workspace accounts).
Best practice: sequential portal processing with delay
Process portals one at a time in a loop, with a small delay between portals:
const portals = ['12345678', '87654321', '11223344'];
portals.forEach((portalId, index) => {
Utilities.sleep(1000); // 1 second between portals
syncPortalData(portalId);
});This reduces burst load, makes error logs easier to read (you know exactly which portal errored), and stays well within rate limits.
For high-volume portals: If a single portal has tens of thousands of records to sync, use cursor-based pagination (HubSpot's after parameter) and process in batches of 100 records, storing the cursor in PropertiesService so you can resume if the script times out.
Method 1: One Script Per Portal + Concatenate
This is the manual approach that works without any paid tools. It trades setup complexity for zero ongoing cost.
Architecture:
- Create one Google Sheet per client portal (or one tab per portal in a shared sheet)
- Write one Apps Script per portal that syncs that portal's data to its tab
- Create a master "Consolidated" tab that uses
IMPORTRANGEorQUERYto pull from each portal tab
Setup steps:
- Create the per-portal scripts following the pattern in the Apps Script + API section of the form submissions guide
- Add the portal identifier column to each script
- Set time-driven triggers for each script (stagger them to avoid simultaneous execution, for example, Portal A at :00, Portal B at :05, Portal C at :10 past each hour)
- In the master tab, use QUERY to pull and union all portal tabs:
=QUERY({ClientA!A:Z; ClientB!A:Z; ClientC!A:Z}, "SELECT * WHERE Col1 is not null")Advantages: Free, fully customizable, no third-party dependency.
Disadvantages: High setup time, maintenance burden grows with each new client, staggered triggers mean data freshness varies by portal, and Apps Script's 6-minute execution limit can be a problem for portals with large datasets.
Method 2: Tools With Native Multi-Portal Support
Several tools are built specifically for the multi-portal use case and handle authentication, rate limiting, and schema normalization automatically.
Dataslayer
Dataslayer is a Google Sheets add-on focused on marketing analytics that supports connecting multiple HubSpot portals from a single account. You authenticate each portal separately within the tool, then define data pulls that can reference any connected portal. The portal identifier is automatically added to output. Strong for CRM metrics and marketing data combined.
G-Integrator
G-Integrator supports multiple HubSpot portal connections and is designed for agencies. It offers scheduled syncs, field mapping configuration, and a portal management dashboard. Better suited for operational data (contacts, deals) than pure marketing metrics.
Two Minute Reports
Two Minute Reports focuses on reporting for agencies and supports multiple HubSpot portals as data sources within a single Google Sheets report. Offers report templates that make initial setup faster. Pricing is per-report rather than per-portal, which can be cost-effective for agencies with many clients.
Brooked.io
Brooked.io is built for HubSpot-to-Sheets sync and handles multi-portal scenarios with separate authentication per portal and unified output to a single sheet. Handles incremental sync, timestamp conversion, and property mapping without custom scripting.
Method 3: HubSpot Partner Portal Approach
If you are a HubSpot Solutions Partner, you have access to a partner account that gives you visibility into your clients' portals through a centralized dashboard. This does not directly solve the Google Sheets consolidation problem, but it reduces some of the authentication friction.
Through the partner portal, you can use a single API token scoped to your partner account to access client portal data via the HubSpot Partner API, rather than requiring separate OAuth grants from each client. This simplifies token management significantly.
Not all agencies are Solutions Partners. The program has requirements around certified staff and minimum client counts. But if you qualify, it is worth incorporating into your technical architecture.
Tool Comparison Table
| Tool | Multi-Portal Native | Portal Column Auto-Added | Scheduling | Best For |
|---|---|---|---|---|
| HubSpot Workflow (native) | No | No | Real-time | Single portal only |
| Zapier | No (separate Zaps per portal) | Manual | Polling | Low portal count |
| Apps Script (custom) | Yes (with development) | Manual | Configurable | Dev teams |
| Dataslayer | Yes | Yes | Scheduled | Marketing analytics |
| G-Integrator | Yes | Yes | Scheduled | Operational CRM data |
| Two Minute Reports | Yes | Yes | Scheduled | Agency reporting |
| Brooked.io | Yes | Yes | Incremental | HubSpot-Sheets sync |
Troubleshooting
Data from one portal is overwriting another portal's rows
This happens when the QUERY formula in a master tab uses is not null filtering on a column that sometimes has null values in valid rows. Use a more stable filter such as the portal ID column: WHERE Col1 != ''.
One portal's sync stopped working while others continue
Almost always an expired or revoked OAuth token. Check the portal's HubSpot settings under Connected Apps to confirm the integration still shows as authorized. Re-authorize if needed and refresh the stored token.
Sync is too slow for 20 portals
Sequential processing with 1-second delays means a 20-portal sync cycle takes at least 20 seconds plus API response time. For large portals this can breach the Apps Script 6-minute execution limit. Consider splitting portals into two triggers (portals 1-10 at :00, portals 11-20 at :30) or using a server-side solution outside Apps Script.
Client names or portal IDs changed
Update the constant in each portal's script. If the portal name is used as a filter in master-tab formulas, update those too. This is a maintenance argument for using numeric portal IDs rather than human-readable names: portal IDs never change.
A new client was onboarded and their data needs to be backfilled
Run the script manually in historical mode, passing a start date of the client's HubSpot account creation. Most connectors support a one-time historical import option. For custom scripts, remove the high-water timestamp filter for a single run.
Bottom Line
Multi-portal HubSpot reporting in Google Sheets is entirely achievable but requires explicit architecture decisions upfront. The portal column is non-negotiable. Token management and rate limit handling are the two areas most likely to cause silent failures over time. For agencies with more than five portals or non-developers managing the setup, a native multi-portal tool eliminates the maintenance burden that custom scripts create at scale. For smaller agencies with a developer available, the one-script-per-portal pattern with a master QUERY tab is free and works reliably with proper trigger staggering.
Consolidate Your Client Portals Without the Overhead
Managing authentication, rate limits, and schema differences across 10+ HubSpot portals is a full maintenance job by itself. Brooked.io handles multi-portal HubSpot-to-Sheets sync so your team spends time on analysis, not on keeping the pipeline running.
Related articles:
- HubSpot to Google Sheets: Why Your Data All Goes to One Cell (and How to Fix It)
- Send HubSpot Form Submissions to Google Sheets Automatically (Step-by-Step)
- HubSpot Email Performance Data in Google Sheets: What's Possible and What Isn't
Troubleshooting quick reference
Frequently asked questions
Does HubSpot have any native multi-portal reporting feature?
No. HubSpot's reporting and dashboards are fully scoped to a single portal. There is no cross-portal aggregate view within the HubSpot product. This is a deliberate separation because each portal is treated as an independent account.
Can I use one Zapier account to connect multiple HubSpot portals?
Yes, but each portal requires a separate HubSpot connection in Zapier, and each connection has its own task count. For 10 portals with moderate submission volume, Zapier costs can become significant. At that scale, a dedicated multi-portal tool is usually more cost-effective.
How do I handle clients with different custom properties?
The portal column pattern is important here. When a custom property exists in some portals but not others, those cells will be empty for portals that lack the property. Use conditional formatting or IFERROR wrapping in formulas to handle gracefully. Avoid designing your consolidated schema around a property that only one client has.
What if two portals use the same property name for different things?
This is rare but happens when clients configure properties independently. Document the semantic difference in a comment column or in a schema reference tab. Consider renaming the column in one portal to disambiguate before syncing, or use a portal-specific column name in your output (e.g., "Deal Stage (Client A)" vs "Deal Stage (Client B)").
Is there a rate limit per HubSpot app (not per portal)?
Yes. If you build a private app per portal, each app is subject to that portal's rate limit. If you build a single public app and authorize it across multiple portals, the rate limit is still per portal. Your app's calls to Portal A do not count against Portal B's limit. This makes the per-app architecture cleaner but the rate budgets remain the same.
How do I prevent a client from seeing another client's data if they have sheet access?
Apply row-level filtering by using QUERY formulas that filter to a specific portal ID, and share client-specific views rather than the master sheet. Alternatively, create separate "Client View" tabs for each client using QUERY to filter the master data, and share only those tabs (via sheet protection or separate files with IMPORTRANGE).
Ready to connect your data to Google Sheets?
Brooked's free tier covers 100 imports per month with AI Analyst included, no credit card required.
Install Brooked free →

