MATLAB Engine API for Python
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
Written against MATLAB R2026b. MATLAB has two interfaces to Python, and they point in opposite directions. In the first the MATLAB program is in charge: it writes py.numpy.array(...) and the Python interpreter is a guest, which is the subject of Calling Python from MATLAB. In the second, the subject of this page, the Python program is in charge: it imports a package called matlab.engine, starts or finds a MATLAB process, and calls MATLAB functions as if they were methods of a Python object. The engine API has been part of MATLAB since R2014b; connecting to a shared session arrived in R2015b, the background keyword for asynchronous starts and connections in R2019a, and a copy of the engine package preinstalled inside MATLAB in R2026a. The release numbers come from the "Introduced in" and "Since" notes of the R2026b reference pages.
GNU Octave 11.3.0 is not MATLAB here, and it cannot run any of the Python-side code on this page. The engine needs a MATLAB installation and a licence, so every [source,python] and [source,bash] block below is labelled "not run here" and was checked against the R2026b documentation; the Python snippets were also checked for syntax with python3 -I -m py_compile, which proves only that they parse. The one piece that can run is the MATLAB function that the heatflow example calls; it was run directly in Octave 11.3.0 (output labelled as such), which shows what the engine hands back to Python. No output is invented: where a result is shown for a Python call, it is the value the documentation states for the same call.
The two directions at a glance
| Question | Python calls MATLAB (this page) | MATLAB calls Python |
|---|---|---|
Who is the host process? |
Python; MATLAB is started or found as a separate process |
MATLAB; Python runs in-process or out-of-process ( |
How is a call written? |
|
|
Where are the conversions defined? |
"Pass Data Between MATLAB and Python from Python" |
"Pass Data Between MATLAB and Python from MATLAB" |
How do you install it? |
|
Nothing to install in MATLAB; point |
Which machines? |
Same machine only: the engine cannot reach MATLAB on a remote machine |
Same process or same machine |
The Python version rules are shared: the same table of supported Python versions applies to the MATLAB interface and to the engine. Use the engine when the application is a Python program, such as a web service, a notebook or a test harness, that needs MATLAB for part of the computation. Use the other direction when the application is a MATLAB program that needs one Python library. The rust section shows the same idea in another language pair: Python interop with PyO3 embeds native code into Python in the same spirit.
Installing matlabengine
The engine is a Python package. It needs a supported 64-bit CPython and an installed MATLAB; MATLAB Runtime alone is not enough ("you cannot run the MATLAB engine on a machine that only has MATLAB Runtime"). Python 2.x has not been supported since R2023a. The supported versions per release are:
| MATLAB release | Python versions supported by the engine |
|---|---|
R2026b |
3.10, 3.11, 3.12, 3.13, 3.14 |
R2026a |
3.9, 3.10, 3.11, 3.12, 3.13 |
R2025b, R2025a, R2024b |
3.9, 3.10, 3.11, 3.12 |
R2024a, R2023b |
3.9, 3.10, 3.11 |
R2023a |
3.8, 3.9, 3.10 |
So Python 3.14 became available with R2026b, and Python 3.9 stopped being supported there. The table is the page "Versions of Python Compatible with MATLAB Products by Release", which also lists older releases; check it before you choose an interpreter, because a mismatch is a common reason for an import or start-up failure. The bitness check from the documentation is a two-line Python script:
import sys
print(sys.maxsize > 2**32) # True on a 64-bit Python
Not run here. Source: MATLAB documentation: Install MATLAB Engine API for Python
There are four ways to get the package, all documented on that page:
| Route | What it does |
|---|---|
From MATLAB |
Run |
From the operating system prompt |
The same |
From PyPI |
|
Preinstalled (R2026a) |
MATLAB ships an engine in |
The pinned form is the one to put in a requirements.txt for a project that must run on the MATLAB release installed on its machines:
# a virtual environment per project keeps the engine out of the system Python
python3 -m venv .venv
source .venv/bin/activate
# newest engine, for the newest MATLAB release
python -m pip install matlabengine
# or an engine that matches an older MATLAB release (n.n.n from the PyPI release history)
python -m pip install matlabengine==n.n.n
# or install from your own MATLAB installation (replace the path printed by matlabroot)
cd "/path/to/matlabroot/extern/engines/python"
python -m pip install .
Not run here. n.n.n and the path are placeholders. The documentation states that you must activate a virtual environment before running the installation commands and points to a MATLAB Answers article for details; the general Python side of that is on Virtual Environments and Packaging. Source: MATLAB documentation: Install MATLAB Engine API for Python
Starting an engine
After installation, the package is imported as matlab.engine. start_matlab launches a new MATLAB process and returns a MatlabEngine object:
import matlab.engine
eng = matlab.engine.start_matlab() # a new MATLAB process, no desktop by default
print(eng.isprime(37)) # True, as the documentation shows
eng.quit() # stops the MATLAB process
Not run here. Source: MATLAB documentation: Start and Stop MATLAB Engine for Python
The signatures, from the reference page for matlab.engine.start_matlab:
eng = matlab.engine.start_matlab()
eng = matlab.engine.start_matlab(option)
future = matlab.engine.start_matlab(background=True)
-
Each call starts a new MATLAB process, so two calls give two independent engines with separate workspaces. If MATLAB cannot be started the call raises
EngineError. -
Startup options are one string. The default is
-nodesktop.start_matlab("-desktop")shows the desktop, andstart_matlab("-desktop -r 'format short'")combines two options. A desktop can also be opened later witheng.desktop(nargout=0). The engine accepts every MATLAB startup option except-h,-help,-?,-n,-eand-logfile. -
background=Truereturns aFutureResultat once and lets Python go on while MATLAB starts;future.result()waits and returns the engine. The old keywordasyncis an alias that is not recommended and cannot be used with Python 3.7 or later, whereasyncis a reserved word. -
Stopping:
eng.quit()oreng.exit(). If Python exits while an engine is running, Python stops the engine and its MATLAB process. -
A specific MATLAB version: the package that Python imports decides which MATLAB starts. Put the install folder on
sys.path(orPYTHONPATH) before the import, and printmatlab.__file__to see which package was imported.
Starting MATLAB takes seconds, and a script that starts an engine per request pays it each time. Start one engine and reuse it, or use the shared sessions below.
Shared sessions: shareEngine, find_matlab, connect_matlab
By default MATLAB sessions are not shared. A session you started yourself, with its desktop, variables and open figures, becomes reachable from Python after one command in MATLAB (R2015b):
matlab.engine.shareEngine % default name MATLAB_<process ID>
matlab.engine.shareEngine("heatflow_session") % or a name of your choice
matlab.engine.engineName % the name of the current shared session
matlab.engine.isEngineShared % true once the session is shared
Not run here (Octave has no matlab.engine package). The default name is MATLAB_ followed by the process ID, so it changes each time MATLAB starts. A chosen name must be a valid MATLAB variable name; if a shared session with that name already exists on the machine, the session gets the default name instead. A session can also be started shared from the operating system prompt:
matlab -r "matlab.engine.shareEngine"
matlab -r "matlab.engine.shareEngine('heatflow_session')"
Not run here. From Python, find_matlab lists the shared sessions of the local machine as a tuple of names, and connect_matlab attaches to one:
import matlab.engine
names = matlab.engine.find_matlab() # a tuple such as ('MATLAB_13232',)
eng = matlab.engine.connect_matlab() # the first session created
eng2 = matlab.engine.connect_matlab("heatflow_session") # a named session
future = matlab.engine.connect_matlab(background=True) # connect asynchronously
eng3 = future.result()
Not run here. Rules from the reference pages:
-
With a name that no shared session has,
connect_matlabraisesEngineError. -
Without a name and with no shared session at all, it starts a new shared session; with several, it connects to the first one created.
-
You cannot connect the engine several times to the same shared session simultaneously.
-
A Python program can connect to several shared sessions, one engine object per session.
-
shareEngineis not supported for deployed applications. -
The shared-session registry lives in the temporary folder. If MATLAB code overrides the
TMPorTEMPenvironment variables,find_matlab()returns an empty tuple, and the documented workaround is to set the same two variables in Python (os.environ) to MATLAB’s temporary folder.
The practical difference from start_matlab: with a shared session you see in the MATLAB desktop everything Python does, you can inspect variables the Python code created, and the session outlives the Python script. The price is that two clients (you at the keyboard and a Python script) share one workspace and one command queue.
Calling MATLAB functions
Any function on the MATLAB path is a method of the engine object. Arguments are positional, outputs are returned, and the number of outputs is the part with no Python counterpart:
| Call | Meaning |
|---|---|
|
One output by default: the result |
|
Three outputs, returned as a Python tuple |
|
A function that returns nothing needs |
|
Change MATLAB’s current folder, to call a function that is not on the path. |
|
Add a folder to the MATLAB path; combine with |
|
Operators are called by their function names ( |
The nargout rule is the engine’s counterpart of the MATLAB rule that the number of outputs is decided by the caller (see Functions): Python has no [a,b,c] = f(x) syntax that tells the engine how many outputs to produce, so the engine assumes one, and the caller says otherwise with nargout=. Two consequences produce most first-day errors: asking for several outputs without nargout returns only the first, and calling a function with no outputs, or a statement such as eng.eval("x = 4;"), without nargout=0 raises a SyntaxError with a MATLAB message about the left of the equals sign not being a valid assignment target.
Further rules:
-
Keyword arguments are not passed. The limitations page states that Python keyword arguments cannot be input arguments to MATLAB functions; the engine passes positional arguments only. The name-value arguments of MATLAB functions are written as positional
"Name", valuepairs, as ineng.plot(x, y, "LineWidth", 2.0). The keywords the engine does recognise (nargout,background,stdout,stderr) are its own. -
Stop a long call with Ctrl+C; control returns to Python.
-
Documentation of a MATLAB function is available from Python (
eng.doc("plot",nargout=0)opens the reference page in the system web browser, and the MATLABhelpfunction gives a brief description at the Python prompt; see Get Help for MATLAB Functions from Python). -
Scripts and your own functions are called the same way once they are on the path. The next example calls a function file from the heatflow project.
The default type trap: Python int is MATLAB int64
MATLAB and Python disagree about what the literal 4 is. In MATLAB it is a double; in Python it is an int, which the engine converts to int64. Many MATLAB functions then reject it. The documentation’s example:
import matlab.engine
eng = matlab.engine.start_matlab()
print(eng.sqrt(4)) # raises matlab.engine.MatlabExecutionError
print(eng.sqrt(4.0)) # 2.0
Not run here. The first call raises matlab.engine.MatlabExecutionError with the MATLAB message "Undefined function 'sqrt' for input arguments of type 'int64'." (the documentation shows that text; the exact wording can vary between releases). The cure is to pass Python float values, which become double, whenever MATLAB expects double. Source: MATLAB documentation: Troubleshoot MATLAB Errors in Python
Arrays: matlab.double and its relatives
Python has no built-in matrix, so the matlab package supplies array classes, one per MATLAB numeric type: matlab.double, matlab.single, matlab.int8 to matlab.int64, matlab.uint8 to matlab.uint64 and matlab.logical. They are created from nested Python sequences:
import matlab
a = matlab.double([[1, 2, 3], [4, 5, 6]]) # a 2-by-3 double matrix
b = matlab.double(vector=[11, 22, 33]) # a 1-by-3 row vector
c = matlab.double(initializer=[[10, 20], [30, 40]], size=[2, 2])
z = matlab.double([[1.1+2.4j, 3+4j], [5.3, 6.7]], is_complex=True)
print(a.size) # (2, 3)
Not run here. The constructor parameters are initializer (the first positional argument, a list, tuple or range, possibly nested), vector (a 1-by-N input, more efficient than initializer and exclusive with it), size and is_complex; matlab.logical has no is_complex. A one-dimensional sequence with a multidimensional size is read in column-major order, which is how MATLAB stores arrays (see Matrices and Arrays). Objects that support the Python buffer protocol, such as a NumPy ndarray, can initialise these arrays too. The properties and methods include size, itemsize, clone(), real(), imag(), noncomplex(), reshape(...) and, for one-dimensional rows, toarray(). Source: MATLAB documentation: MATLAB Arrays as Python Variables
Plain Python scalars and containers are converted too, in both directions. The most useful rows of the conversion tables on "Pass Data Between MATLAB and Python from Python":
| Python to MATLAB | Result in MATLAB | MATLAB to Python |
|---|---|---|
|
|
|
|
|
any integer type becomes |
|
|
|
|
|
|
|
cell array |
a 1-by-N or N-by-1 cell array becomes a |
|
scalar structure (keys must be valid MATLAB names) |
a scalar |
|
numeric array |
a numeric array becomes a |
|
|
|
|
|
|
Values with no Python equivalent, such as a handle object (containers.Map) or a categorical array, come back as an opaque matlab.object that can be passed back to MATLAB but not unpacked in Python. These are not converted at all: an M-by-N char, string or cell array, sparse arrays, struct arrays, function handles and Java objects. The other way round, None, Python classes and enum types have no MATLAB counterpart. The size of data passed between the processes is limited to 2 GB, including supporting information. Source: MATLAB documentation: Pass Data Between MATLAB and Python from Python
Example: the heatflow temperature field
The heatflow plate keeps its temperature in a matrix. A Python program that has a field (here a small one, with a hot interior on a plate held at 20 degrees) can send it to a MATLAB function and read three results back. First the MATLAB side, a function file plate_summary.m on the MATLAB path (or in the current folder of the engine):
function [tmean, tmax, where] = plate_summary(T)
%PLATE_SUMMARY Mean, maximum and location of the maximum of a temperature field.
% [tmean, tmax, where] = plate_summary(T) returns the mean of T, its
% maximum, and the [row column] subscripts of the first maximum.
tmean = mean(T(:));
[tmax, k] = max(T(:));
[r, c] = ind2sub(size(T), k);
where = [r c];
end
Called directly in Octave 11.3.0 (not through the engine), the function gives:
>> T = [20 20 20 20; 20 35 40 20; 20 30 35 20; 20 20 20 20];
>> [tmean, tmax, where] = plate_summary(T)
tmean = 23.750
tmax = 40
where =
2 3
Octave 11.3.0 output; MATLAB R2026b prints tmean as 23.7500. The function uses only base MATLAB.
Now the Python side, which creates the field as a matlab.double, asks for three outputs and passes only positional arguments:
import matlab
import matlab.engine
eng = matlab.engine.start_matlab()
eng.addpath("heatflow", nargout=0) # folder that holds plate_summary.m
T = matlab.double([
[20.0, 20.0, 20.0, 20.0],
[20.0, 35.0, 40.0, 20.0],
[20.0, 30.0, 35.0, 20.0],
[20.0, 20.0, 20.0, 20.0],
])
tmean, tmax, where = eng.plate_summary(T, nargout=3)
# tmean and tmax are Python floats (23.75 and 40.0);
# where is a 1-by-2 matlab.double holding the subscripts [2, 3].
print(tmean, tmax, list(where[0]))
eng.quit()
Not run here. The values in the comments follow from the Octave run above, because the engine calls the same MATLAB function; the exact printed form on the Python side is not shown. Two details matter. T is built from float values, but matlab.double converts integers as well, so matlab.double([[20, 20], [20, 20]]) is also a double matrix: the integer trap above applies to bare scalars, not to the elements of a matlab.double. And where arrives as a 2-D array of size (1, 2), not as a flat list, which is why the example indexes where[0]. Source: MATLAB documentation: Call User Scripts and Functions from Python
NumPy arrays are the usual source of fields in a Python program. An ndarray supports the buffer protocol, so matlab.double(T_numpy.tolist()) is the safe conversion for a 2-D array, and passing the ndarray itself is converted to a numeric array by the engine according to the table above. Check the shape that arrives in MATLAB (size(T)) when one of the dimensions is 1, because a 1-D NumPy array has no row or column orientation to preserve.
The engine workspace and eval
Each engine has a workspace, exposed as the dictionary eng.workspace. Its keys are MATLAB variable names, so assigning to it creates a MATLAB variable, and reading from it copies a variable back:
import matlab.engine
eng = matlab.engine.start_matlab()
eng.workspace["y"] = 4.0
a = eng.eval("sqrt(y)") # 2.0
eng.eval("b = pi;", nargout=0) # a statement with no output needs nargout=0
mpi = eng.workspace["b"] # 3.14159265359
Not run here. The values are those the documentation shows for the same statements. Keys must be valid MATLAB identifiers, so a number is not allowed as a key. eval is the escape hatch for a statement, not a function call; the same cautions as in MATLAB apply (see eval and its alternatives); prefer a function call with arguments, because the engine converts arguments and results for you and nothing is parsed as text. A recursive Python data structure cannot be placed in the workspace. Source: MATLAB documentation: Use MATLAB Engine Workspace in Python
Asynchronous calls
Calls are synchronous: control returns to Python when MATLAB has finished. To overlap MATLAB work with Python work, add background=True. The call returns a FutureResult at once and MATLAB works in the background:
import matlab.engine
eng = matlab.engine.start_matlab()
future = eng.sqrt(4.0, background=True) # returns immediately
# ... other Python work here ...
ret = future.result() # waits, then returns 2.0
print(ret)
print(future.done()) # True once the call has finished
# future.cancel() # would stop the call before it finishes
Not run here. Source: MATLAB documentation: Call MATLAB Functions Asynchronously from Python
A FutureResult has result(), done() and cancel() and can raise these exceptions when you ask for the result: SyntaxError and TypeError (Python), and matlab.engine.CancelledError, InterruptedError, MatlabExecutionError, RejectedExecutionError (the engine was terminated) and TimeoutError (the result could not be returned within the timeout period). The same background=True keyword applies to start_matlab and connect_matlab, so start-up and the first calls can overlap with Python work.
One engine is one MATLAB process with one command queue. Asynchronous calls let Python continue; they do not make MATLAB run two functions at once on that engine. For real parallelism start several engines, each with its own process, or use MATLAB’s own parallel features on the MATLAB side (see Background and Asynchronous Execution).
Source: matlab.engine.FutureResult
Errors and output
When a MATLAB function raises an error, the engine stops the function, copies the message into a new Python exception and raises it. The exception types listed on the MatlabEngine reference page are MatlabExecutionError (the function failed to execute), RejectedExecutionError (the engine terminated), SyntaxError (a syntax error in the call, which includes the missing nargout=0) and TypeError (an argument or result type that is not supported). Handle them like any Python exceptions:
import io
import matlab.engine
eng = matlab.engine.start_matlab()
try:
eng.sqrt(4) # int64 argument: MATLAB error
except matlab.engine.MatlabExecutionError as exc:
print("MATLAB said:", exc)
out = io.StringIO()
err = io.StringIO()
eng.dec2base(2**60, 16, stdout=out, stderr=err) # error text goes to err, not the screen
print(err.getvalue())
Not run here. The stdout and stderr keywords redirect MATLAB’s Command Window output and error text into Python StringIO objects; the documentation shows err holding "First argument must be an array of integers, 0 ⇐ D ⇐ 2^52." for this call. The MATLAB identifier (for example MATLAB:UndefinedFunction) is not part of the Python exception type, and the documented behaviour is that the message text is copied; if your code needs the identifier, test the message or catch the error inside a small MATLAB wrapper function that returns a status. Sources: MATLAB documentation: Troubleshoot MATLAB Errors in Python, Redirect Standard Output and Error to Python
Limitations and pitfalls
| Symptom | Cause | Remedy |
|---|---|---|
|
A Python |
Pass |
|
A statement or a function with no output was called without |
Add |
Only the first output arrives |
|
Use |
A keyword argument has no effect or raises an error |
The engine passes positional arguments only |
Write name-value pairs as positional |
|
MATLAB overrode |
Set the same variables in Python before calling it. |
|
No shared session of that name |
Call |
Import fails or MATLAB does not start after an upgrade |
The |
Reinstall the matching version; check the compatibility table. |
Remote machine |
The engine cannot start or connect to MATLAB on another machine |
Run the Python script on the MATLAB machine; for remote use consider MATLAB Production Server or MATLAB Compiler SDK instead. |
Very large arrays |
Data passed between the processes is limited to 2 GB |
Write the data to a file ( |
Python is not the only host language. The same idea exists for C++ and Java: the MATLAB Engine API for C++ (page "Call MATLAB from C++") starts MATLAB, connects to a shared session, calls functions with C++ arguments and evaluates statements, and the MATLAB Engine API for Java (page "Call MATLAB from Java") does the same for JDK programs, synchronously or asynchronously; both use the same shared sessions made by matlab.engine.shareEngine. Calling C++ code from MATLAB is the topic of C and C++ Libraries and MEX, and the C++ toolchain side is on C++ Build and Tooling; the Java side is on Java Build and Tooling. Sources: Call MATLAB from C++, Call MATLAB from Java
Where older books differ
The two books consulted for this section predate the engine’s current form: MATLAB Primer was written for R2010a, four years before the Python engine existed (R2014b), and neither book covers Python interoperability at all. Older web tutorials show import matlab.engine with Python 2.7, the async=True keyword (a syntax error from Python 3.7 on, where background=True has been the form since R2019a), and installs by running python setup.py install from the MATLAB folder instead of pip; the pip routes (including PyPI’s matlabengine) are the ones in the R2026b documentation, and Python 2.x is unsupported since R2023a. Tutorials from before R2015b know only start_matlab, not shared sessions, and tutorials from before R2026a do not mention the preinstalled engine package.