How to Export Non-Mapped HubSpot Form Submissions (The Data You Can't Get From Contacts API)

Use the Form Integrations Submissions API to retrieve unmapped form fields and export to CSV

·Matija Žiberna·
How to Export Non-Mapped HubSpot Form Submissions (The Data You Can't Get From Contacts API)

📚 Get Practical Development Guides

Join developers getting comprehensive guides, code examples, optimization tips, and time-saving prompts to accelerate their development workflow.

No spam. Unsubscribe anytime.

I ran into a frustrating problem last week while extracting form data from HubSpot for a client. They were using Elementor forms that submitted to HubSpot, but several form fields weren't mapped to contact properties. When I pulled contact data using the Contacts API, those unmapped fields were completely missing.

The HubSpot documentation doesn't address this scenario directly. Every article and API reference focuses on contact properties—not the raw submission data that sits in form submissions but never makes it to the contact record. After hours of testing different endpoints and reading through forum threads, I found the workaround and built a tool to solve it permanently.

The Problem With Non-Mapped Form Fields

Here's the specific issue: when you have a non-HubSpot form (Elementor, custom HTML forms, etc.) submitting to HubSpot, you can collect any field data you want. But if those fields don't map to existing contact properties, that data only exists in the form submission detail view. You can see it in the HubSpot UI when you click into individual submissions, but there's no straightforward way to export it in bulk.

The Contacts API endpoint returns contact properties. If your form field doesn't map to a contact property, that data never becomes part of the contact record. The standard Forms API gives you form definitions, but not submission details. I spent significant time searching for an export method in the HubSpot documentation—nothing directly addressed accessing these non-mapped submission fields programmatically.

The Solution: Form Integrations API

The endpoint that actually works is the form integrations submissions API. This is what finally retrieved the complete submission data:

curl --request GET \
  --url 'https://api.hubapi.com/form-integrations/v1/submissions/forms/{form_guid}?limit=50' \
  --header 'Authorization: Bearer YOUR_API_TOKEN'

The critical detail is using the correct form GUID. You can find this in the HubSpot URL when viewing submissions:

https://app.hubspot.com/submissions/{portal_id}/form/{form_guid}/submissions/{submission_id}

The form_guid is the identifier between /form/ and /submissions/. This endpoint returns all submission data including fields that were never mapped to contact properties. Each submission includes a values array with every field that was submitted, regardless of mapping status.

Building the Export Tool

Once I confirmed this endpoint worked, I built a Python script to automate the entire export process. The tool fetches all forms from your HubSpot account, retrieves every submission using the form integrations endpoint, and exports each form to a separate CSV file.

The key implementation handles pagination automatically since HubSpot uses cursor-based pagination with an after parameter:

# File: main.py
def get_all_form_submissions(form_guid):
    """Get ALL submissions for a form with pagination"""
    all_submissions = []
    url = f"{BASE_URL}/form-integrations/v1/submissions/forms/{form_guid}"

    params = {"limit": 50}
    page = 1

    while True:
        response = requests.get(url, headers=HEADERS, params=params)
        response.raise_for_status()
        data = response.json()

        submissions = data.get("results", [])
        all_submissions.extend(submissions)

        # Check for next page
        paging = data.get("paging", {})
        next_info = paging.get("next", {})

        if next_info and "after" in next_info:
            params["after"] = next_info["after"]
            page += 1
            time.sleep(0.2)
        else:
            break

    return all_submissions

The submission data extraction pulls every field dynamically from the values array:

# File: main.py
def extract_submission_data(submission):
    """Extract ALL fields dynamically"""
    data = {
        "submission_id": submission.get("conversionId"),
        "submitted_at": submission.get("submittedAt"),
        "page_url": submission.get("pageUrl")
    }

    for value_obj in submission.get("values", []):
        field_name = value_obj.get("name")
        field_value = value_obj.get("value")

        if field_name in data:
            data[field_name] = f"{data[field_name]}; {field_value}"
        else:
            data[field_name] = field_value

    return data

This approach captures everything submitted through the form, including fields that don't exist as contact properties in HubSpot.

Handling Forms That Don't Appear in the API

Another issue I encountered: some forms don't show up in the Forms API response at all. This happened with the Elementor forms that were submitting directly to HubSpot through the forms endpoint. The form GUID exists and submissions are recorded, but the form itself isn't returned by /forms/v2/forms.

The workaround is manually adding these forms to the script:

# File: main.py
ADDITIONAL_FORMS = [
    {
        "guid": "f5d75534-81cf-4daa-a8e2-ec847fd43704",
        "name": "#launch_contactform_id .elementor-form"
    },
    {
        "guid": "2dc57586-08ae-4d19-a88b-526eb174a593",
        "name": "#vendorcontactform .elementor-form"
    }
]

The script combines forms from the API response with these manually specified forms, ensuring you can export submissions for any form where you have the GUID.

Using the Tool

I've released this as an open-source project. Setup takes about two minutes:

git clone https://github.com/matija2209/hs-forms-submissions-exporter.git
cd hs-forms-submissions-exporter
pip install -r requirements.txt
cp .env.example .env

Add your HubSpot API token to the .env file. Generate a token in HubSpot under Integrations → Private Apps with the forms read scope enabled:

HUBSPOT_API_TOKEN=your_actual_token_here

Run the export:

python main.py

The script fetches all forms, retrieves every submission with pagination handling, and saves separate CSV files in the exports directory. Each CSV includes all submission fields—both mapped and non-mapped to contact properties.

Why This Matters

If you're dealing with custom forms, Elementor forms, or any submission source where fields don't perfectly align with HubSpot contact properties, you need access to raw submission data. The Contacts API won't give you this. The form integrations submissions endpoint is the only way I found to programmatically access this data in bulk.

This solved a critical gap in my workflow where client reporting required the full submission dataset, not just the subset that made it into contact records. The tool now runs on a schedule for several clients who need regular exports of their complete form data.


The repository is available at github.com/matija2209/hs-forms-submissions-exporter. If you've struggled with this same problem or find other use cases for the tool, let me know in the comments.

Thanks, Matija

0

Comments

Leave a Comment

Your email will not be published

10-2000 characters

• Comments are automatically approved and will appear immediately

• Your name and email will be saved for future comments

• Be respectful and constructive in your feedback

• No spam, self-promotion, or off-topic content

Matija Žiberna
Matija Žiberna
Full-stack developer, co-founder

I'm Matija Žiberna, a self-taught full-stack developer and co-founder passionate about building products, writing clean code, and figuring out how to turn ideas into businesses. I write about web development with Next.js, lessons from entrepreneurship, and the journey of learning by doing. My goal is to provide value through code—whether it's through tools, content, or real-world software.