How to Export Data from HubSpot Service Hub: Methods & API Limits
Learn how to export data from HubSpot Service Hub — native UI exports, API endpoints, rate limits, attachment extraction, and what data you can't get out.
Planning a migration?
Get a free 30-min call with our engineers. We'll review your setup and map out a custom migration plan — no obligation.
Schedule a free call- 1,500+ migrations completed
- Zero downtime guaranteed
- Transparent, fixed pricing
- Project success responsibility
- Post-migration support included
TL;DR — HubSpot Service Hub Data Export
You can export most core CRM objects — Tickets, Contacts, Companies, Custom Objects — from HubSpot Service Hub via the native UI or API. But no single method covers everything. The native UI export handles up to 1,000,000 rows per object as flat CSVs but drops engagements (emails, calls, notes), attachments, conversation threads, and relational context. Attachments are never included in any standard export and require the Files API with the files.ui_hidden.read scope. Email engagement bodies require the sales-email-read scope — without it, the content is silently redacted. Knowledge Base articles have no public REST API for bulk export. For a full-fidelity extraction that preserves ticket-to-contact-to-company relationships with engagement history and attachments, you need the CRM REST API, the Engagements endpoints, the Conversations API, and the Files API. Most teams under 10,000 records can self-serve with the native UI. Above that — or if you need engagement history and attachments — you need engineering resources, a third-party ETL connector, or a managed service.
Last verified: July 2025, against HubSpot API documentation v3/v4. HubSpot is actively rolling out date-versioned API paths (e.g., /crm/objects/2026-03/). Check the HubSpot API changelog for updates after this date.
What Data Can You Export from HubSpot Service Hub?
HubSpot Service Hub stores customer support data across several distinct object types, each with its own export path and limitations. The table below maps every core object to its available export methods.
| Object | Native UI Export | API Export | Format | Known Limitations |
|---|---|---|---|---|
| Tickets | Yes | Yes (GET /crm/v3/objects/tickets) |
CSV, XLS, XLSX, JSON | UI export includes properties and association IDs but not conversation threads, activity history, or engagement bodies |
| Contacts | Yes | Yes (GET /crm/v3/objects/contacts) |
CSV, XLS, XLSX, JSON | Association to multiple companies flattened; secondary emails require explicit include; XLS limited to 256 columns, XLSX to 16,384 columns |
| Companies | Yes | Yes (GET /crm/v3/objects/companies) |
CSV, XLS, XLSX, JSON | Multi-domain companies may lose secondary domain mappings |
| Engagements (Emails) | No | Yes (GET /crm/v3/objects/emails) |
JSON | Requires sales-email-read scope; without it, email body is silently redacted — no error, no warning |
| Engagements (Calls, Notes, Tasks, Meetings) | No | Yes (GET /crm/v3/objects/{type}) |
JSON | Must be fetched per-type; not included in any UI export |
| Conversations (Inbox threads) | No | Yes (Conversations API) | JSON | No native UI export; separate API surface from CRM object endpoints |
| Knowledge Base Articles | Yes (CMS export) | No public REST API | HTML, CSV | No public APIs for the knowledge base tool exist. Articles can only be created or modified in the HubSpot UI. UI export available for article metadata and text. |
| Customer Feedback Submissions | Yes (via reports) | Yes (GET /crm/v3/objects/feedback_submissions) |
CSV, XLS, XLSX, JSON | Survey response data available; limited filtering in UI |
| Attachments / Files | No | Yes (GET /files/v3/files/{fileId}/signed-url) |
Binary (original format) | Never included in CSV exports; requires multi-step API extraction |
| Custom Objects | Yes | Yes (GET /crm/v3/objects/{objectType}) |
CSV, XLS, XLSX, JSON | Custom object schema must be retrieved separately via /crm/v3/schemas; multi-level custom object associations require separate v4 API calls; property types (enumeration, date, etc.) need explicit handling during transformation |
| Associations (Ticket→Contact, etc.) | Partial (IDs only) | Yes (GET /crm/v4/associations/{type}/{id}) |
JSON | By default, only the properties and associations in the view will be included, and for associations, this includes up to one thousand associated record ID values per association column. Full graph requires v4 Associations API. |
The UI export silently omits engagement bodies, attachment binary content, and the full association graph. If your migration depends on preserving ticket-to-contact-to-company relationships with engagement history attached, the UI export alone will produce an incomplete dataset.
As a practical summary:
- Fully exportable via UI or API: Tickets, Contacts, Companies, Custom Objects, Feedback Submissions (property-level data).
- API-only: Engagements (emails, calls, notes, tasks, meetings), Conversations, Attachments.
- Hardest to extract: Conversation threads with inline attachments. This is where most HubSpot Service Hub export projects stop being a CSV exercise and become an engineering exercise.
- Effectively locked in the platform: Knowledge Base article content via API, workflow configurations, SLA settings.
Export Methods: Every Way to Get Data Out of HubSpot Service Hub
There are seven practical export methods, and most serious projects use more than one.
| Method | Data Coverage | Volume Limit | Speed | Complexity | Best For |
|---|---|---|---|---|---|
| Native UI Export | CRM records, KB articles, survey responses | 1,000,000+ rows split into multiple files; 300 exports/24h, 3 simultaneous | Minutes to hours | Low | One-time snapshots, small datasets, zero engineering |
| CRM Exports API | Same as UI, programmatically triggered | Async file generation; same row limits as UI | Minutes to hours | Medium | Scheduled or automated flat-file exports |
| CRM REST API (pagination) | All CRM objects including engagements | API rate limits (see tier table below) | Depends on dataset + follow-up calls | High | Full extraction with engagement history |
| Conversations API | Inbox threads and messages | API rate limits | Multi-pass, slower | High | Full-fidelity support conversation history |
| Files API (attachments) | Binary files only | Per-file | Depends on file count | High | Attachment extraction |
| Third-party ETL / reverse ETL | CRM objects, some engagements | Varies by connector | Continuous or scheduled | Medium | Ongoing sync to warehouse; middle ground between UI and custom scripts |
| Managed export service | All objects, engagements, attachments, associations | No hard limit | Days for end-to-end | Low (for your team) | Full migration or compliance export |
Third-Party ETL Connectors: The Middle Ground
For teams that need more than the native UI but don't want to build custom extraction scripts, third-party ETL tools with HubSpot connectors offer a practical middle path:
| Tool | HubSpot Objects Supported | Engagement Support | Attachments | Pricing Model |
|---|---|---|---|---|
| Fivetran | Tickets, Contacts, Companies, Deals, Engagements, Custom Objects | Yes (emails, calls, notes, meetings, tasks) | No (metadata only) | Per-row pricing; HubSpot connector included in Standard tier |
| Airbyte (open-source) | Tickets, Contacts, Companies, Engagements, Feedback Submissions | Yes | No | Free (self-hosted) or cloud pricing |
| Hevo Data | Tickets, Contacts, Companies, Engagements | Yes | No | Per-event pricing |
| HubSpot native Snowflake integration | All CRM objects via data share | Partial (depends on share configuration) | No | Requires Operations Hub Enterprise ($2,000/mo as of 2025) |
Key limitation of ETL connectors: None of these tools download attachment binary files. They sync metadata and properties to your data warehouse. For attachments, you still need the Files API. They also flatten associations into foreign key columns rather than preserving HubSpot's native many-to-many association graph — you'll need transformation logic (dbt models, SQL joins) to reconstruct relationships in your warehouse.
ETL connectors are strongest for ongoing backup and analytics use cases. For one-time migration with full fidelity, you'll still need the CRM REST API or a managed service for the attachment and association layers.
Native UI Export: Step-by-Step
The fastest way to export HubSpot Service Hub records without engineering support:
- Navigate to CRM > Tickets (or Contacts, Companies, Custom Objects — click More > CRM > Tickets if your navigation is collapsed).
- Open the view you want to export. Use All Tickets for a complete extract. To include associated record data (e.g., Company Name on a Ticket), ensure those columns are visible in the view.
- Click Export in the top-right of the table view.
- Select your file format: CSV, XLS, or XLSX.
- Choose which properties to include: Properties and associations in your view (default), All properties, or All properties and associations. Associations export as record IDs only.
- Click Export. HubSpot emails you a download link when the file is ready.
For Knowledge Base articles, the export path is different: go to Service > Knowledge Base > Actions > Export knowledge base articles and choose your format.
For Customer Feedback submissions, export from the feedback response view after applying any filters you need.
What the native export misses: Conversation threads, email bodies, call recordings, notes, task details, and file attachments are not included in native ticket exports. You get property values and association IDs — not the full activity timeline.
Concrete limits to expect:
- You can complete up to 300 exports within a rolling 24-hour window, and up to three exports simultaneously. Any additional exports will be queued.
- Large exports may take a few hours to complete, and you may receive multiple files delivered in a zip file. Multiple files are expected when exporting CSV or XLSX files with more than 1,000,000 rows and XLS files with more than 65,535 rows.
- For XLS and XLSX files, there's a limit to the number of columns that can be exported (256 for XLS and 16,384 for XLSX). For exports with a large number of columns, CSV is recommended as it has no column limit.
- Files over 2MB are zipped automatically.
- For accounts with export approvals enabled, the export may require Super Admin approval before processing.
- Sandbox environments: Export behavior in HubSpot Sandbox accounts mirrors production, but Sandbox data is a snapshot — exports from Sandbox will not reflect live production changes. Always export from your production portal for migration or compliance purposes.
Any user with Export permissions can do this. Budget 5–30 minutes depending on dataset size.
Can You Export Associated Records from HubSpot Service Hub?
Partially. You cannot export associated records in a segment export. If you want to export records with their associations, export a view instead.
Even view-based exports flatten associations. By default, only the properties and associations in the view will be included, and for associations, this includes up to one thousand associated record ID values per association column and the associated record name. You get association IDs, not the full related record.
To build a complete relational map from native exports, you need to:
- Export Tickets with association IDs.
- Export Contacts separately.
- Export Companies separately.
- Join the datasets using HubSpot Record IDs.
The native export doesn't export the associations between objects. Those live in a separate data layer that the standard export tool doesn't touch. For full many-to-many association data, use the v4 Associations API: GET /crm/v4/objects/{objectType}/{objectId}/associations/{toObjectType}.
This is one of the most underestimated gaps in HubSpot exports. Because HubSpot relies on a graph-like Association API rather than simple foreign keys on object records, your team must write transformation logic to re-link Tickets, Contacts, Companies, and Engagements after extraction. The switching cost imposed by HubSpot isn't in getting the data out — it's in putting it back together.
For context: Zendesk uses straightforward foreign keys (a ticket has a requester_id field pointing to a user). Freshdesk similarly uses requester_id on tickets. Intercom uses contact_id. HubSpot's many-to-many association model is more flexible but significantly harder to export and reconstruct. A ticket can be associated with multiple contacts, each of which can be associated with multiple companies — and these associations carry labeled types (e.g., "primary" vs. "secondary"). Flattening this into CSV columns loses the label metadata entirely.
API-Based Export: Endpoints, Pagination, and Rate Limits
To extract a complete, relational dataset from HubSpot Service Hub, you need the API. HubSpot provides two API-based export paths: the CRM Exports API (async, server-side) and the CRM REST API (object-by-object, client-side pagination).
CRM Exports API
The Exports API lets you export records in views or lists from your HubSpot account. Start an export with POST /crm/v3/exports/export/async, then poll for status with GET /crm/v3/exports/export/async/tasks/{exportId}/status.
- Possible statuses include COMPLETE, PENDING, PROCESSING, or CANCELED. For exports with a COMPLETE status, a URL is returned to download the exported file. The download URL expires five minutes after the completed request.
- The number of associations per row is limited to 1,000 by default. To override this, include the
overrideAssociatedObjectsPerDefinitionPerRowLimitfield set totrue. - Requires the
crm.exportscope. The user installing the app must be a Super Admin to grant the crm.export scope.
The CRM Exports API is useful for scheduling automated CSV backups of your Ticket or Contact lists to a data warehouse. But it has the same relational and engagement limitations as the UI export — it produces flat files, not a relational dataset.
CRM REST API (Object-by-Object)
For granular control, paginate through objects directly:
| Endpoint | Object | Max per Page | Pagination | Notes |
|---|---|---|---|---|
GET /crm/v3/objects/tickets |
Tickets | 100 | Cursor (after) |
Use properties param to specify fields |
GET /crm/v3/objects/contacts |
Contacts | 100 | Cursor (after) |
|
GET /crm/v3/objects/companies |
Companies | 100 | Cursor (after) |
|
GET /crm/v3/objects/emails |
Email engagements | 100 | Cursor (after) |
Requires sales-email-read scope |
GET /crm/v3/objects/notes |
Note engagements | 100 | Cursor (after) |
|
GET /crm/v3/objects/calls |
Call engagements | 100 | Cursor (after) |
Call recording URLs require media_bridge.read scope |
GET /crm/v3/objects/meetings |
Meeting engagements | 100 | Cursor (after) |
|
GET /crm/v3/objects/tasks |
Task engagements | 100 | Cursor (after) |
|
POST /crm/v3/objects/tickets/batch/read |
Tickets (batch) | 100 per request | N/A | Send up to 100 IDs per call |
POST /crm/v3/objects/tickets/search |
Tickets (filtered) | 200 | Cursor (after); capped at 10,000 total results |
4 req/s shared limit |
POST /crm/v4/associations/{from}/{to}/batch/read |
Associations (batch) | Up to 1,000 source IDs per call | N/A | Returns association labels |
For pagination, list and search endpoints under /crm/v3/ and /crm/v4/ use cursor pagination: each response includes a paging.next.after value, which you pass back as the after query parameter to fetch the next page. Do not attempt offset/limit pagination — it will fail on large datasets.
Authentication uses Private App access tokens (bearer tokens) or OAuth 2.0. Legacy API keys were sunset in November 2022 and are no longer functional.
API versioning note: HubSpot is rolling out date-versioned API paths (e.g., /crm/objects/2026-03/tickets). Some newer docs use these paths while many Service Hub guides still reference /crm/v3/. For existing scripts, /crm/v3/ endpoints remain functional as of July 2025. Check HubSpot's API changelog for deprecation notices.
Rate Limits and 429 Errors
HubSpot enforces strict rate limits based on your subscription tier:
| Subscription Tier | Burst Limit | Daily Limit |
|---|---|---|
| Free / Starter (private app) | 100 requests / 10 seconds | 250,000 / day |
| Professional (private app) | 190 requests / 10 seconds | 650,000 / day |
| Enterprise (private app) | 190 requests / 10 seconds | 1,000,000 / day |
| API Limit Increase add-on | — | +1,000,000 / day (stacks on tier limit) |
| Public marketplace apps | 110 requests / 10 seconds per account | — |
| CRM Search API | 4 requests / second (shared across all search endpoints) | Subject to daily limit of tier |
The daily limit for Professional is 650,000 requests per day with a burst of 190 per 10 seconds. For Enterprise, the daily limit is 1,000,000 per day with a burst of 190 per 10 seconds.
Exceeding a limit returns HTTP 429 with a JSON body containing errorType: "RATE_LIMIT" and a policyName of SECONDLY or DAILY telling you which limit you hit. Your extraction script must read the Retry-After header in the 429 response and implement exponential backoff. On high-volume sync bursts, you can also receive a 423 Locked response — pause for at least two seconds before retrying.
Complete Export Script (Python)
The following script extracts tickets with their associated contacts, engagements, and attachments. It handles 429/423 errors, authentication, and outputs structured JSON suitable for transformation.
import requests
import time
import json
import os
import sys
BASE_URL = "https://api.hubapi.com"
HEADERS = {"Authorization": "Bearer YOUR_ACCESS_TOKEN"}
OUTPUT_DIR = "hubspot_export"
def api_get(url, params=None):
"""GET request with retry logic for 429 and 423 responses."""
max_retries = 5
for attempt in range(max_retries):
resp = requests.get(url, headers=HEADERS, params=params)
if resp.status_code == 429:
retry_after = int(resp.headers.get("Retry-After", 10))
print(f"Rate limited (429). Waiting {retry_after}s...")
time.sleep(retry_after)
continue
if resp.status_code == 423:
print("Locked (423). Waiting 2s...")
time.sleep(2)
continue
if resp.status_code == 401:
sys.exit("Authentication failed. Check your access token and scopes.")
resp.raise_for_status()
return resp.json()
raise Exception(f"Max retries exceeded for {url}")
def api_post(url, payload):
"""POST request with retry logic."""
max_retries = 5
for attempt in range(max_retries):
resp = requests.post(url, headers={**HEADERS, "Content-Type": "application/json"}, json=payload)
if resp.status_code == 429:
retry_after = int(resp.headers.get("Retry-After", 10))
time.sleep(retry_after)
continue
if resp.status_code == 423:
time.sleep(2)
continue
resp.raise_for_status()
return resp.json()
raise Exception(f"Max retries exceeded for {url}")
def paginate_objects(object_type, properties=None):
"""Fetch all records of a given object type using cursor pagination."""
all_records = []
params = {"limit": 100}
if properties:
params["properties"] = ",".join(properties)
while True:
data = api_get(f"{BASE_URL}/crm/v3/objects/{object_type}", params)
all_records.extend(data["results"])
next_page = data.get("paging", {}).get("next", {}).get("after")
if not next_page:
break
params["after"] = next_page
time.sleep(0.06) # ~16 req/s, safely under 190/10s burst
return all_records
def get_associations(from_type, to_type, record_ids):
"""Batch-fetch associations using the v4 API. Max 1000 IDs per call."""
all_associations = {}
for i in range(0, len(record_ids), 1000):
batch = record_ids[i:i+1000]
payload = {"inputs": [{"id": rid} for rid in batch]}
data = api_post(
f"{BASE_URL}/crm/v4/associations/{from_type}/{to_type}/batch/read",
payload
)
for result in data.get("results", []):
from_id = result["from"]["id"]
all_associations[from_id] = [
{"id": assoc["toObjectId"], "label": assoc.get("associationTypes", [{}])[0].get("label", "")}
for assoc in result.get("to", [])
]
return all_associations
def get_engagement_bodies(engagement_type, record_ids, association_map):
"""Fetch engagements associated with records."""
engagements_by_record = {}
for record_id in record_ids:
associated = association_map.get(record_id, [])
for assoc in associated:
eng = api_get(
f"{BASE_URL}/crm/v3/objects/{engagement_type}/{assoc['id']}",
{"properties": "hs_timestamp,hs_body_preview,hs_attachment_ids,hs_email_html,hs_email_subject"}
)
engagements_by_record.setdefault(record_id, []).append(eng)
time.sleep(0.06)
return engagements_by_record
def download_attachment(file_id, output_dir):
"""Download a single attachment via signed URL."""
signed = api_get(f"{BASE_URL}/files/v3/files/{file_id}/signed-url")
url = signed.get("url")
if not url:
print(f"No signed URL for file {file_id}")
return None
resp = requests.get(url)
resp.raise_for_status()
filepath = os.path.join(output_dir, f"{file_id}_{signed.get('name', 'unknown')}")
with open(filepath, "wb") as f:
f.write(resp.content)
return filepath
def main():
os.makedirs(OUTPUT_DIR, exist_ok=True)
os.makedirs(os.path.join(OUTPUT_DIR, "attachments"), exist_ok=True)
# 1. Export tickets
print("Exporting tickets...")
tickets = paginate_objects("tickets", ["subject", "content", "hs_pipeline_stage", "createdate", "hs_ticket_priority"])
print(f" Exported {len(tickets)} tickets")
# 2. Export contacts
print("Exporting contacts...")
contacts = paginate_objects("contacts", ["firstname", "lastname", "email", "phone"])
print(f" Exported {len(contacts)} contacts")
# 3. Export companies
print("Exporting companies...")
companies = paginate_objects("companies", ["name", "domain", "industry"])
print(f" Exported {len(companies)} companies")
# 4. Fetch ticket→contact associations
print("Fetching ticket→contact associations...")
ticket_ids = [t["id"] for t in tickets]
ticket_contact_assoc = get_associations("tickets", "contacts", ticket_ids)
# 5. Fetch ticket→notes associations and note bodies
print("Fetching ticket→notes associations...")
ticket_note_assoc = get_associations("tickets", "notes", ticket_ids)
# 6. Fetch note bodies (includes attachment IDs)
print("Fetching note engagement bodies...")
notes_by_ticket = get_engagement_bodies("notes", ticket_ids, ticket_note_assoc)
# 7. Download attachments from notes
print("Downloading attachments...")
attachment_map = {}
for ticket_id, notes in notes_by_ticket.items():
for note in notes:
att_ids = note.get("properties", {}).get("hs_attachment_ids", "")
if att_ids:
for file_id in att_ids.split(";"):
file_id = file_id.strip()
if file_id:
filepath = download_attachment(file_id, os.path.join(OUTPUT_DIR, "attachments"))
attachment_map.setdefault(ticket_id, []).append({"file_id": file_id, "local_path": filepath})
# 8. Assemble and write output
export_data = {
"tickets": tickets,
"contacts": contacts,
"companies": companies,
"associations": {
"ticket_to_contact": ticket_contact_assoc,
"ticket_to_notes": ticket_note_assoc,
},
"engagements": {"notes_by_ticket": {k: v for k, v in notes_by_ticket.items()}},
"attachments": attachment_map,
"export_metadata": {
"total_tickets": len(tickets),
"total_contacts": len(contacts),
"total_companies": len(companies),
"total_attachments": sum(len(v) for v in attachment_map.values()),
}
}
with open(os.path.join(OUTPUT_DIR, "full_export.json"), "w") as f:
json.dump(export_data, f, indent=2, default=str)
print(f"\nExport complete. {export_data['export_metadata']}")
if __name__ == "__main__":
main()Required scopes for this script:
crm.objects.contacts.readcrm.objects.companies.readticketse-commerce(for some engagement types)sales-email-read(if fetching email engagements — add email association fetching)files.ui_hidden.read(for attachment downloads)crm.objects.custom.read(if exporting custom objects)
Throughput math: At 100 records per page and a conservative 10 requests per second (safely under the 190/10s burst limit for Pro/Enterprise), you can extract ~60,000 records per minute for a single object type. In practice, real throughput drops to ~5,000–15,000 enriched records per hour once you add the secondary calls required to fetch associations, engagements, and attachments for each ticket. A 100,000-ticket database with full engagement history (averaging 8–12 engagements per ticket) requires roughly 800,000–1,200,000 API calls total and can take 6–12 hours on an Enterprise plan accounting for rate limit pauses.
Do not build a full export on the Search API. The CRM Search API is the most common bottleneck because its 4 requests/second limit is much stricter than the general burst limit. The Search API also caps any single query at 10,000 total results. For complete exports, use list endpoints with the after cursor instead. Reserve the Search API for filtered subsets only.
Does HubSpot Service Hub Export Include Attachments?
No. Attachments are never included in HubSpot's native CSV or XLSX exports. Extracting attachments requires a multi-step API workflow:
- Get the engagement associated with the ticket: Query Notes or Emails via
GET /crm/v3/objects/notes/{noteId}?properties=hs_attachment_idsto find records with attachments. - Extract the file ID from the
hs_attachment_idsproperty (semicolon-delimited if multiple). - Get a signed download URL: Make a
GETrequest to/files/v3/files/{fileId}/signed-url. - Download the file from the returned signed URL before it expires, and map the local file path back to the original Ticket ID.
Attachments are linked to records in the CRM via the Note object. In order to retrieve the actual file, there are a number of things we need to do. There isn't a single endpoint we can hit to retrieve this data.
Scope requirements for attachments:
- When using an OAuth access token, the
files.ui_hidden.readscope is required to get a signed URL for any files that are set to hidden. Hidden files include files attached to CRM records. Without this scope, a 404 error will be returned.
The email scope trap: The sales-email-read scope is required in order to receive the details of any email-type engagements. Without the sales-email-read scope, email engagement details are redacted in the response — the html field returns "The content of this email has been redacted" and the attachments array returns empty. This is the most common silent failure in HubSpot data exports.
How to verify before a full run:
- Fetch a single known email engagement:
GET /crm/v3/objects/emails/{emailId}?properties=hs_email_html,hs_email_subject - Check whether
hs_email_htmlcontains actual HTML content or the redaction string. - If redacted, add
sales-email-readto your Private App scopes and re-authorize. - For attachments, fetch a known file ID via
/files/v3/files/{fileId}/signed-url. If you get a 404, addfiles.ui_hidden.read.
What Data Cannot Be Exported from HubSpot Service Hub?
No export method — API or UI — covers 100% of what lives in the platform. Here is an honest accounting:
| Data Type | Exportable? | Workaround |
|---|---|---|
| Knowledge Base articles (full content via API) | No | No public APIs for the knowledge base tool exist. Use CMS bulk HTML export or scrape published pages. This is a long-standing request in HubSpot's Ideas forum with no announced timeline as of July 2025. |
| Workflow configurations (logic, branching, actions) | No | Workflow exports don't include performance data or the workflow's history. Export as PNG for visual reference; metadata spreadsheet available. Must be manually rebuilt in destination. |
| Audit logs (detailed, bulk) | Partial | HubSpot retains detailed action logs for 90 days (Starter), 180 days (Pro), or up to 1 year (Enterprise). No bulk export API. Use property history (GET /crm/v3/objects/{type}/{id}?propertiesWithHistory=property_name) for record-level change tracking. |
| Custom views / saved filters | No | Recreate manually in destination platform. |
| User permissions and role assignments | No | Document manually. User list available via User Provisioning API (GET /settings/v3/users). |
| Integration configurations | No | Document connected apps and sync settings manually before decommissioning. |
| Deleted/archived records | Partial | API supports archived=true parameter; UI export does not include archived records. Records past the 90-day recycle bin window are permanently purged. |
| SLA configurations | No | Document manually. Not accessible via any API. |
| Analytics and dashboard data | Partial | Individual table-format reports can be exported as CSV; visual dashboards cannot. Custom report API available for some report types. |
| Chatbot / conversational bot flows | No | Document manually. Bot logic must be rebuilt in destination. |
The Knowledge Base gap is the single biggest portability issue for Service Hub teams. If you have a large article library, extracting it requires HTML export from the CMS tool or scraping published pages — there is no supported REST API path.
Workflow definitions and user/permission configuration are separate from CRM record export. Treat them as configuration migration rather than data export — they must be manually rebuilt in the destination platform.
HubSpot GraphQL API: Limited Applicability for Exports
HubSpot provides a GraphQL API primarily for CMS content and some CRM queries. As of July 2025, its export utility is limited: it supports CRM data tables and HubDB rows but does not provide access to engagements, conversations, or the full CRM association graph in a way that outperforms the REST API for bulk extraction. It may be useful for targeted queries that join CRM data with CMS content (e.g., pulling contact data alongside blog subscription preferences), but it is not a replacement for REST-based full export.
For guidance on handling export gaps during a transition, see our Help Desk Data Migration Playbook.
Data Format, Encoding, and Cleanup After Export
HubSpot exports CSVs in UTF-8 encoding with a BOM (byte order mark). Most modern tools handle this correctly, but legacy Excel versions on Windows may display encoding artifacts with special characters (particularly with non-Latin scripts). Always import CSVs using Excel's Data > From Text/CSV wizard to preserve encoding, or open directly in Google Sheets which handles UTF-8 BOM correctly.
Common data quality issues in HubSpot exports:
- HTML in text fields: Rich text properties (like ticket descriptions and email bodies) export with raw HTML tags. Strip tags before importing into a destination that expects plain text. Use a library like Python's
beautifulsoup4orbleachrather than regex. - Multi-line text breaking CSV rows: Line breaks inside Notes fields get interpreted as new rows by naive CSV parsers. Use a parser that respects RFC 4180 quoted fields (Python's
csvmodule,pandas.read_csv, or similar). - Timestamps in Unix epoch: API responses return timestamps as Unix milliseconds (e.g.,
1556890404000= May 3, 2019 14:33:24 UTC). UI exports use human-readable date strings in your portal's timezone. Convert to ISO 8601 or your destination system's required format before import. Verify timezone consistency — HubSpot API timestamps are always UTC. - Orphaned association IDs: Exported association ID columns may reference records that exist in a different object's export, or records that have been deleted. Cross-validate all association IDs against exported record sets before importing.
- Pipeline and stage values: Map using internal IDs (e.g.,
12345678), not display labels (e.g., "New" or "In Progress"). Labels can be renamed in HubSpot without changing the underlying ID. Retrieve the mapping viaGET /crm/v3/pipelines/tickets. - Enumeration properties: Multi-select properties export as semicolon-delimited values. Your destination may need them split into separate values or mapped to different IDs.
- Calculated and read-only properties: Some properties (e.g.,
hs_time_to_close_in_minutes) are calculated server-side and cannot be written to a destination. Identify these before import.
Post-export validation checklist:
- Row count in export matches record count shown in HubSpot UI view
- All custom properties present (check against
GET /crm/v3/properties/tickets) - Association IDs cross-reference correctly between Ticket, Contact, and Company files
- Email engagement bodies are present (not redacted) if email history was extracted
- Attachment file IDs resolve to downloadable files and sampled files open correctly
- Date formats are consistent across all exported objects (all UTC or all converted)
- No duplicate Record IDs within a single object export
- Conversation samples show correct chronological ordering, sender direction, and timestamps
- Custom object records match expected count per schema from
/crm/v3/schemas - Enumeration values map to known value sets (no orphaned option IDs)
Export for Migration vs. Backup vs. Compliance
The same data requires different treatment depending on why you are exporting it.
Migration requires relational integrity. You need Tickets, Contacts, Companies, and their associations exported with matching IDs so the destination platform can rebuild the relationship graph. Engagement history needs to travel with the ticket it belongs to. Attachments must be downloaded as physical files. HubSpot IDs must be mapped to the new system's IDs — for example, mapping HubSpot's hs_ticket_to_contact association to Zendesk's requester_id field, or Freshdesk's requester_id, or Intercom's contact_id. For a deep dive on this process, see our guide on how to migrate from HubSpot Service Hub to Zendesk. Our HubSpot Service Hub Migration Checklist covers the full scoping and validation workflow.
Backup requires a full-fidelity snapshot. HubSpot has no built-in "export everything" button. You must export each object type separately, download attachments via API, and store the combined dataset. Standard approaches:
- Scheduled API scripts dumping JSON payloads into S3/GCS/Azure Blob on a daily or weekly cron job.
- ETL connectors (Fivetran, Airbyte) syncing incrementally to Snowflake, BigQuery, or Redshift.
- Webhook-based incremental export: Configure HubSpot workflow-triggered webhooks to push record changes to your warehouse in near-real-time. This avoids full re-extraction but requires webhook infrastructure and deduplication logic.
- HubSpot Operations Hub (Enterprise tier): Provides a native Snowflake data share, eliminating the need for API-based extraction for warehouse sync. However, at $2,000+/month, this is cost-effective only for large portals with continuous analytics needs.
Relational mapping is less critical for backup until restoration is actually needed — but store association data alongside object data so you don't have to re-extract it.
Compliance (GDPR/DSAR) requires chain of custody. Under GDPR Article 20, data subjects have a right to data portability in a structured, machine-readable format. HubSpot's CSV exports satisfy the format requirement, but a comprehensive DSAR response must include engagement history (emails, calls, notes) and attachments tied to that contact. The native UI export alone will not satisfy a comprehensive DSAR because it omits engagement bodies and attachments. Do not confuse a DSAR response with a full operational backup — GDPR portability is about delivering the specific data subject's personal data, not your entire Service Hub database. For teams operating under GDPR, see our GDPR Compliant Data Migration Blueprint.
Vendor Lock-In and Data Portability Assessment
Rating: Mostly Portable — with significant effort on engagements and attachments.
Here's the breakdown:
- ~80% of structured data (Tickets, Contacts, Companies, Custom Objects, Feedback Submissions) is fully exportable via UI or API with complete property coverage. This is comparable to Zendesk's data portability (full API coverage for tickets, users, organizations).
- ~15% of data (engagements, conversations, association graphs) requires API scripts with correct scopes and pagination handling. This layer is harder to extract than equivalents in Zendesk (which exposes comments/audits inline on the ticket endpoint) or Freshdesk (which includes conversations on the ticket detail endpoint).
- ~5% of data (Knowledge Base content via API, workflow logic, SLA configs, integration settings) is effectively locked in the platform. For comparison, Zendesk provides a Knowledge Base API with full CRUD; Freshdesk provides a Solutions API. HubSpot's lack of a KB API is a notable gap.
The real switching cost isn't the data extraction itself — it's rebuilding relational integrity outside HubSpot. Ticket→Contact→Company→Engagement associations must be reconstructed in the destination platform using ID mapping, which HubSpot's native export doesn't facilitate. This typically requires a developer building custom transformation scripts or using a managed migration service.
HubSpot imposes no contractual restrictions on data export. Your data is yours. The friction is operational, not legal. HubSpot's Data Processing Agreement confirms data portability rights.
For teams evaluating a move, our guides cover how to migrate from HubSpot Service Hub to Zendesk and HubSpot to Front — both of which start with the export patterns described here.
Timeline and Resourcing for a Full Data Export
If you are planning a full extraction from HubSpot Service Hub, budget your time accordingly:
| Dataset Size | Scoping | Extraction | Validation & Cleanup | Total | Team Needed |
|---|---|---|---|---|---|
| < 10K records | 1–2 hours | 1–2 hours (UI export) | 2–4 hours | ~1 day | No developer needed for flat export; developer needed if engagements required |
| 10K–100K records | 2–4 hours | 4–8 hours (API required for engagements) | 1–2 days | 2–4 days | 1 developer + 1 PM |
| 100K–500K records | 1 day | 1–3 days (API + attachment download) | 2–3 days | 5–8 days | 1–2 developers + 1 PM |
| 500K+ records | 1–2 days | 3–7 days (parallelized API extraction, rate limit management) | 3–5 days | 8–14 days | 2 developers + 1 PM; consider API Limit Increase add-on |
These estimates assume you need engagement history and attachments. If you only need flat property exports (no emails, no files), cut extraction time by 60–70%.
The bottleneck for large exports is rarely the raw data volume — it's the attachment download loop and the engagement-per-ticket fan-out. A single ticket may have 20+ associated notes and emails, each requiring separate API calls. With a 100,000-ticket portal averaging 10 engagements per ticket, that's ~1,000,000 engagement API calls on top of the base ticket extraction. Factor in rate limit pauses (particularly the 4 req/s Search API limit if you're using filtered queries) and the total wall-clock time grows quickly.
To make sure you don't miss critical steps during planning, use our HubSpot Service Hub Migration Checklist.
When a Managed Export Makes Sense
If your export involves more than 50,000 tickets with engagement history, attachments, and multi-object associations, the engineering overhead of building and debugging custom extraction scripts — including scope configuration, rate limit handling, association reconstruction, and post-export validation — often exceeds the cost of a managed service.
ClonePartner has completed 1,500+ data migrations including HubSpot Service Hub extractions with full attachment and engagement preservation, handling rate limit management, association mapping, and post-export validation as a standard part of the process.
If you are staying on HubSpot, test your export path now before you need it under a deadline — run the validation checklist above against a sample export to identify scope gaps and data quality issues. If you are planning a move, start with our HubSpot Service Hub Migration Checklist. If Zendesk is the destination, our HubSpot to Zendesk guide covers the mapping and cutover decisions.
Frequently Asked Questions
- Can I export all my data from HubSpot Service Hub?
- You can export most structured data — Tickets, Contacts, Companies, Custom Objects — via the native UI or API. Engagements (emails, calls, notes), attachments, and conversations require the API with specific scopes. Knowledge Base articles have no public REST API and must be extracted via CMS content export. No single tool or button exports everything.
- Does HubSpot Service Hub export include attachments?
- No. Attachments are never included in native CSV or XLSX exports. You must use the Files API (GET /files/v3/files/{fileId}/signed-url) to generate signed download URLs for each file. The files.ui_hidden.read scope is required for files attached to CRM records, and the sales-email-read scope is required for email attachment metadata.
- How long does it take to export data from HubSpot Service Hub?
- A native UI export of under 100,000 tickets typically completes in under 30 minutes. API-based full extractions with engagements and attachments for 100,000+ records take 5–8 days of total effort including scripting, extraction, and validation. The limiting factors are API rate limits (190 requests per 10 seconds for Pro/Enterprise) and the per-ticket engagement fan-out.
- What format does HubSpot Service Hub export data in?
- Native UI exports produce CSV, XLS, or XLSX files (your choice). The REST API returns JSON. The CRM Exports API produces ZIP files containing CSVs. Files over 2MB are automatically zipped. Attachments are downloaded in their original binary format.
- What are HubSpot's API rate limits for data export?
- Professional accounts get 190 requests per 10 seconds and 650,000 per day. Enterprise accounts get 190 per 10 seconds and 1,000,000 per day. The CRM Search API has a stricter limit of 4 requests per second shared across all search endpoints. Exceeding any limit returns an HTTP 429 error with a Retry-After header.

