Skip to content

Can you write Python in HubSpot workflow custom code?

HubSpot supports Python in custom code actions, but the action itself requires Data Hub Professional or Enterprise, and the same 20-second and 128 MB limits apply. The available library set is fixed, with no pip install. An app-provided workflow action runs Python on the app's own infrastructure instead, with those constraints removed.

Last updated

Python is a first-class option in HubSpot’s custom code action. The language dropdown offers it alongside JavaScript. What surprises people is that choosing Python changes nothing about the constraints.

What Python inherits

Every limit on the JavaScript action applies identically:

  • Data Hub Professional or Enterprise is required for the action to be usable at all
  • 20 seconds of execution time
  • 128 MB of memory
  • A fixed library set with no requirements.txt and no pip install

That last one bites harder in Python than in JavaScript, because so much of Python’s value is its ecosystem. The reasons a team reaches for Python. Pandas for data manipulation, a mature SDK, a specific parsing library. Are mostly unavailable inside the native action.

The 128 MB limit is also a poorer fit for Python’s typical memory profile. Loading a moderately large dataset into memory to transform it is a natural Python approach and a fast route to an out-of-memory kill here.

What Python is genuinely good for here

Within the constraints, Python is a fine choice for:

  • Property transformation and normalisation
  • Calling the HubSpot API and reshaping the response
  • Text processing and regular expressions
  • Date arithmetic, where Python’s standard library is stronger than JavaScript’s

If your team writes Python and your job fits in 20 seconds with the built-in libraries, there’s no reason to prefer JavaScript. The lack of Python examples in HubSpot’s docs and community threads is a real friction, but it’s a documentation gap, not a capability one.

The shape of the handler

def main(event):
email = event["inputFields"]["email"]
object_id = event["object"]["objectId"]
return {
"outputFields": {
"output1": email.strip().lower(),
}
}

Two things trip people up:

Returning nothing. If you don’t return a dictionary with outputFields, the action reports success and downstream steps get empty values. This is the single most common confusion with HubSpot custom code in either language. The action doesn’t fail, it just quietly produces nothing.

Assuming the full record arrived. event["object"] contains only the properties the action was configured to request, not the entire contact. Anything else needs an API call, which spends part of your 20 seconds.

Running Python without the constraints

An app-provided workflow action executes on the app’s infrastructure, so the runtime, the libraries and the time budget are the app’s rather than HubSpot’s.

Turbyn runs Python actions this way:

import httpx
from dateutil.relativedelta import relativedelta
async def main(event, ctx):
domain = event["inputFields"]["domain"]
# No 20-second ceiling. This can take as long as it takes.
async with httpx.AsyncClient(timeout=60) as client:
res = await client.get(f"https://api.example.com/enrich?domain={domain}")
data = res.json()
renewal = relativedelta(months=+12)
ctx.log.info("enriched", {"domain": domain})
return {
"outputFields": {
"output1": data["industry"],
"output2": str(data["employees"]),
}
}

What changes:

  • async def main with a real event loop, so concurrent I/O is straightforward
  • Any pip package. requests, httpx, pydantic, pandas. Installed from your imports with no manifest to maintain
  • Minutes of runtime, so slow third-party APIs and multi-page pagination fit
  • ctx, adding a pre-authenticated HubSpot client, a secrets vault, a key-value store and structured logging
  • A test loop that runs against a real record without writing to it, so you can see the output before publishing

The plain def main(event) form works too. If you’re moving an existing action across, it runs as-is and you can adopt ctx later.

Python and JavaScript are equal citizens here. If Python is what your team writes, that’s what you should be writing.

FAQ

Frequently asked

Which Python version does HubSpot's custom code action use?

HubSpot has moved the supported runtime forward over time, so the specific version depends on when the action was created and what the editor offers you now. Actions created against an older runtime keep working, which means a portal can have actions on several different versions at once. Check the language dropdown in the action itself rather than relying on documentation.

Can I use pandas in a HubSpot custom code action?

Not in the native action. The runtime provides a fixed set of libraries with no way to install more, and heavy numerical libraries would in any case be difficult to use inside a 128 MB memory limit.

Is Python or JavaScript better for HubSpot custom code?

Neither has a capability advantage in the native runtime. Both hit the same 20-second timeout, the same 128 MB memory limit and the same fixed library set. Choose whichever your team maintains more comfortably. Most HubSpot code examples are JavaScript, so Python users find fewer copy-paste starting points.

Does the Python action get the same event object?

Yes, with Python conventions. The enrollment payload arrives as a dictionary, so you use event["inputFields"]["email"] where JavaScript uses event.inputFields.email. The structure is otherwise the same.

Start with one workflow step

Connect a portal, write one action, and put it in a workflow. About ten minutes, and the free tier doesn't ask for a card.