Data 360 Code Extension: writing Python for transforms and search-index chunking when clicks run out
The visual transform builder and the declarative chunking config cover most of a Data 360 build — until they don't. Code Extension lets you drop into Python for a Batch Data Transform step or a custom search-index chunking strategy, authored and tested in a notebook. Here's what it can do, where the Python actually runs, how it differs from the Python Connector, and the limits worth knowing before you reach for code.
Most of a Data 360 build is deliberately click-first, and that’s the right default. You shape data in the visual Data Transform builder, you configure a search index to chunk and vectorize your documents, and you never write a line of code. For the great majority of pipelines, that’s not a limitation — it’s the feature. Declarative is faster to build, easier to hand off, and doesn’t rot when the person who wrote it leaves.
Then you hit the case the builder can’t express. A transform that needs a fuzzy-match library, a probabilistic dedupe, a bit of NumPy the formula editor doesn’t have. A document set where the default chunking strategy — split every N tokens — shreds a table across three chunks and destroys the very grounding you built the index for. In the old Data Cloud, that was where you exported the data to an external job, did the work somewhere else, and pushed it back — losing the governance and the zero-copy promise on the way out. Code Extension is Salesforce’s answer to that gap: it lets you drop into Python at two specific points in the Data 360 pipeline — a Batch Data Transform step and a search-index chunking strategy — without leaving the platform. This post is what it actually does, where the Python runs, how it’s different from the Python Connector people confuse it with, and the limits worth knowing before you reach for it.
Two places code plugs in, and only two
The first thing to be clear about — because it’s the thing that stops you building the wrong mental model — is that Code Extension is not a general “run arbitrary Python against Data 360” surface. It’s a pair of extension points inside features you already use declaratively, and it arrived in Beta in the Spring ‘26 timeframe, so treat everything here as a capability to verify against current release notes rather than a settled GA surface. Salesforce introduced it under the banner “extend Data 360 with the power of code,” Python first with other languages flagged as possible later, and the two things it currently lets you write are named distinctly:
- Scripts — for Batch Data Transforms. Inside a batch transform — the visual node graph that joins, aggregates, filters, and reshapes data at rest — a Code Extension Script runs custom Python that reads from and writes back to Data 360 objects. This is for the shaping logic the node palette can’t express: real string parsing, XML you have to walk, a decryption step, a calculation that genuinely wants pandas. Salesforce’s own launch example is a Script that read 139M+ rows, applied custom decryption, and produced 65M processed records in about 17 minutes — a vendor figure, but it tells you the intended scale.
- Functions — for Search Index Chunking. When you build a search index to ground an agent on unstructured content, the index has to chunk the source — break each document into passages — before it vectorizes them. The built-in strategies are passage-extraction rules (semantic boundaries from a document’s HTML structure, or window-based blocks), and they’re blunt for anything that isn’t clean prose. A Code Extension Function lets you supply a custom Python chunking strategy, which you then select as the chunking option under a search index’s Advanced Setup — so you decide where a document splits based on its actual structure.
That’s the whole surface today. It matters because it tells you what Code Extension is for: it’s an escape hatch inside the declarative tools, reached only when the declarative tool genuinely can’t do the job — not a replacement for them. The discipline is the same one we apply to the Flow-versus-Apex-versus-Agentforce decision: stay declarative until declarative stops paying, then drop exactly one level, not all the way to a bespoke external pipeline.
Where the Python actually runs
Here’s the part that separates Code Extension from every “connect Python to Salesforce” story that came before it, and it’s the whole reason the feature exists: the code runs inside Data 360’s own processing, over data that never leaves the platform.
When a Script runs inside a batch transform, it executes as part of the transform run, in the pipeline, on the lakehouse data — the same governed, zero-copy estate the rest of the transform operates on. You’re not exporting rows to a laptop, running a script, and re-ingesting the result. The data stays in Data 360; the code comes to the data. That inversion is the point. It’s what keeps the governance, the lineage, and the credit accounting intact — an external Python job breaks all three the moment the first row crosses the boundary.
Authoring, though, is local — and this is the part teams get backwards. You develop and test on your own machine using the Salesforce CLI, a Code Extension plugin, and a Python SDK, working in whatever you like: an IDE such as VS Code, or a Jupyter notebook. Against a connected sandbox you can read real schema and a sample of up to 1,000 records to validate your logic quickly — but writes back are deliberately blocked from the local environment (they surface in the console for debugging) so a work-in-progress script can’t corrupt production. When the logic is right, you deploy the Script or Function to Data 360, where it runs governed as a pipeline step — on a schedule or via Run Now for a transform. The mental model that keeps you out of trouble: your laptop is the workbench, the deployed run inside Data 360 is the factory, and there are deliberate security gates between them.
The data contract at the step boundary is the one any Python data engineer expects. Rows arrive as a pandas DataFrame, your code does its work in pandas/Python, and you return a DataFrame that becomes the step’s output — flowing to the next node, or out to the target Data Lake Object. If you’ve written a pandas transform before, you already know the shape of the job; what’s new is only that it runs governed and in place.
# Illustrative shape of a Code Extension transform step.
# Input rows arrive as a DataFrame; return a DataFrame of the shaped output.
# (Exact entrypoint signature and available libraries follow the current
# Salesforce docs — confirm them against your org's release before building.)
import pandas as pd
def transform(df: pd.DataFrame) -> pd.DataFrame:
# Normalize a messy phone column the formula node can't handle cleanly:
# strip non-digits, drop empties, tag a type, build a stable composite key.
out = df.copy()
out["phone_digits"] = (
out["raw_phone"].astype("string").str.replace(r"\D", "", regex=True)
)
out = out[out["phone_digits"].str.len() >= 10]
out["phone_type"] = "mobile"
out["phone_id"] = out["customer_id"].astype("string") + "_" + out["phone_digits"]
return out[["phone_id", "customer_id", "phone_digits", "phone_type"]]
Notice what this replaces. That normalization is doable in a streaming transform’s SQL with enough CASE WHEN and string functions, as we showed in the Data Transforms walkthrough — but the moment the rule gets genuinely irregular (real phone parsing, locale-aware casing, a fuzzy join key), SQL and the formula node turn into a wall of nested expressions nobody can maintain. Ten lines of pandas is the honest tool. That’s the whole value proposition: not “code is better,” but “code is the right tool for the shaping the declarative editor makes tortured.”
The chunking case is the one that quietly saves your RAG
The transform use case is the obvious one. The chunking one is the one that actually moves an agent’s answer quality, and it’s underappreciated.
Recall how grounding on unstructured content works. To make a PDF, a knowledge article, or a pile of case replies retrievable, Data 360 builds a search index: it chunks each document into passages, vectorizes each chunk into an embedding, and at query time retrieves the chunks most similar to the question so the agent grounds on them. The retrieval is only ever as good as the chunks. If a chunk splits a table header away from its rows, or cuts a procedure in half, the embedding represents a fragment that means nothing on its own — and the agent grounds on a fragment, which is how you get a confident answer assembled from half a policy.
The built-in strategies — passage extraction from a document’s HTML structure, or fixed-size windows — are fine for prose and blunt for structure. A rate table, a nested spec, a Q&A doc, a contract with numbered clauses: these have natural boundaries a generic strategy ignores. A Code Extension Function lets you write the split logic in Python. Data 360 reads the source content from your selected Data Lake Objects, parses it into document elements, and hands those to your function; you return the chunks:
# Illustrative custom chunking Function. Data 360 passes parsed document
# content in; you return a list of self-contained passages. (The exact
# request/response object shapes are defined in the Code Extension docs —
# treat the entrypoint below as conceptual, not a verbatim signature.)
import re
def chunk(document_text: str) -> list[str]:
# Break on markdown-ish headings or numbered clauses, keep each heading
# with the body it introduces, and drop trivially short fragments so
# every chunk vectorizes into something that means one thing.
parts = re.split(r"\n(?=#{1,3}\s|\d+\.\s)", document_text)
return [p.strip() for p in parts if len(p.strip()) > 40]
This is the difference between an index that retrieves “the paragraph that happened to contain the keyword” and one that retrieves “the whole clause that answers the question.” When teams tell us their grounded agent is “sometimes right, sometimes weirdly partial,” bad chunking is the first thing we look at, and it’s usually the cheapest thing to fix. Code Extension is what turns “we can’t control the chunking” into “we chunk on the document’s real structure.” If you want the broader picture of getting unstructured data agent-ready before you even reach for custom chunks, Intelligent Context is the declarative layer that sits above this — try it first; drop to a Python chunker when the shape of your documents defeats it.
Code Extension is not the Python Connector — don’t conflate them
There are three ways to “use Python with Data 360,” they solve different problems, and mixing them up leads to building in the wrong place. Keep them straight:
- Code Extension (this post). Python runs inside the Data 360 pipeline, on the data in place, as a transform step or a chunking strategy. The data doesn’t move. This is for shaping and indexing logic that lives in your pipeline.
- The Data 360 Python Connector. A package you
pip installfrom PyPI (the long-standingsalesforce-cdp-connector, with a newer connector in beta succeeding it — check which is current) that lets an external Python process — your notebook, a data-science box, an ML training job — connect to Data 360 over a connected app, run Data 360 SQL, and pull results back as pandas DataFrames for analysis or model training outside the platform. Here the data does come to your code, and the connector is read-oriented. This is for exploration and downstream modeling, not for pipeline logic. - Data 360 SQL and the Query API. The ANSI-SQL surface for querying the unified lakehouse — DMOs and DLOs — from wherever, covered in the Query API post. It’s the query language both the connector and external tools speak; it is not SOQL, and it reaches the analytical store, not the CRM transaction objects.
The rule of thumb: if the logic belongs in the pipeline and the data should stay put, that’s Code Extension. If you’re pulling data out to analyze or train a model, that’s the Python Connector over Data 360 SQL. And if the model you train that way needs to score records back inside the platform, that’s a handoff to predictive AI and bring-your-own-model — the connector gets the training data out; BYOM gets the scoring back in. Three tools, three jobs, one estate.
The limits and the bill
Code Extension is powerful precisely because it runs your code on governed data, and that same fact is where the constraints come from. A few worth internalizing before you design around it.
It runs on the platform’s terms, not your laptop’s. Because the code executes inside Data 360’s processing rather than on a box you control, the available libraries, the resource envelope, and the execution model are Salesforce’s to define, and they move between releases. Do not assume an arbitrary PyPI package is importable, and do not assume unbounded memory for a step that has to process a large DataFrame. Check the current documentation for the supported runtime, library set, and any row or resource limits before you commit to an approach — and design the step to stream or batch large inputs rather than assuming the whole object fits in memory at once.
It’s compute, and compute is metered. A Python transform step is still a Data Transform, and transforms consume Data 360 credits against the volume of data they process, charged every time the transform runs — not once at setup. Custom Python doesn’t get a discount for being clever; if anything, a heavy per-row Python step over a large object is exactly the kind of workload that runs up a bill quietly. The same discipline from the credit optimization playbook applies: schedule the transform no more often than the source actually changes, filter to the rows that need the custom logic before the Python step rather than running the whole object through it, and don’t reach for a standing process when a scheduled one will do. (Exact per-row credit rates live on Salesforce’s current rate card and shift between releases — price it against the live sheet, not a number from a blog, including this one.)
Availability and scope are worth verifying, not assuming. Code Extension and its supported operations have been rolling out across recent releases, and the precise set of operations that accept custom Python — plus the notebook and IDE authoring experience — is exactly the kind of surface that expands release to release. Before you architect a pipeline around a Python step, confirm against current release notes that the specific operation you need supports it in your org, rather than inferring it from an announcement.
Governance still applies, and that’s a feature. A Python step runs within Data 360’s access model, so it sees what the pipeline is permitted to see — which is the whole reason to run it here instead of exporting data to an ungoverned script. But it also means the step is subject to the same governance and access rules as everything else in the estate. Treat the code as production code: version it outside the notebook, review it, and test it against a representative slice before it runs against the full object.
The takeaway
Code Extension closes a specific, real gap: the point where the visual transform builder or the default chunking config can’t express the logic you need, and your only old option was to leave the platform and lose the governance and zero-copy that made Data 360 worth adopting. Now the code comes to the data — Python runs as a transform step or a custom chunking strategy, in place, governed, and metered like any other Data 360 workload.
Use it the way you’d use Apex in a clicks-first org: as the deliberate exception, not the default. Stay in the visual transform builder and the declarative index config for everything they can do — which is most things — and drop into Python only for the shaping the node palette makes tortured or the chunking your document structure demands. When you do, keep the three Python surfaces straight (Code Extension in the pipeline, the Python Connector for pulling data out, BYOM for scoring back in), price the compute against the live rate card, and treat the code as production. Do that and Code Extension is what it should be: the escape hatch that keeps you inside the governed estate on the rare day the declarative tools run out — not a reason to abandon it. Getting that data foundation right, so it shapes cleanly and grounds an agent without bill shock, is exactly the work our integration and Data Cloud practice does before a single agent goes live.
Understanding the basics
What is Code Extension in Data 360?
Code Extension is a Data 360 capability that lets you run custom Python at specific points in the pipeline — currently a Batch Data Transform step and a search-index chunking strategy — with the code executing inside Data 360’s own processing over data that stays in the platform. You author and test the logic in a notebook (a Jupyter-style Python workflow), and developers can work against the same surfaces from an IDE like VS Code, but the code runs as a governed pipeline step, not as an external script. It exists to handle the shaping and indexing logic the visual transform builder and the default chunking config can’t express, without exporting data to an ungoverned external job.
How is Code Extension different from the Data 360 Python Connector?
They move data in opposite directions. Code Extension runs Python inside the pipeline, on data in place, as a transform or chunking step — the data never leaves Data 360. The Python Connector is a PyPI package that lets an external Python process connect to Data 360, run Data 360 SQL, and pull results back as pandas DataFrames for analysis or model training outside the platform — here the data comes to your code. Use Code Extension for pipeline logic that should stay governed and in place; use the Connector for exploration and downstream modeling. If a model trained that way needs to score records back inside the platform, that’s a separate handoff to bring-your-own-model.
When should I use custom Python chunking instead of the default?
Use it when your documents have structure a fixed token window destroys — tables, numbered clauses, nested specs, Q&A formats — because retrieval quality is only ever as good as the chunks the index builds. Default token-count chunking is fine for prose but splits structured content across chunk boundaries, producing embeddings that represent meaningless fragments and grounding an agent on half an answer. A custom Python chunking strategy lets you split on the document’s real boundaries so each chunk is a self-contained passage. Try the declarative and Intelligent Context layers first; drop to a Python chunker when the shape of your documents defeats them.
Deciding whether a reshape belongs in the visual builder, a Python transform step, or an external job — and how the credit math lands either way? Talk to us. Getting the Data 360 foundation right so it shapes cleanly and grounds AI without surprises is exactly the work we do.
Keep reading
All insights
Showing Data 360 data on a Salesforce record page: related lists, copy fields, and the mapping that makes it work
Data 360 governance: making sure your agent only sees what the user is allowed to see
Right to be forgotten in Data 360: the Consent API, and why deleting a profile is harder than it looks