Ableton Live REST API
Producer Pal includes a REST API for building custom scripts, automation, and integrations with Ableton Live using plain HTTP requests — no MCP SDK needed. It also works as an alternative interface for coding agents: download this page as Markdown (button at the top) and give it to your agent for a complete reference.
The REST API runs on the same server as the MCP endpoint (default port 3350) and is available whenever the Producer Pal Max for Live device is running.
This is for developers
Most users don't need the REST API. The normal way to use Producer Pal is through an AI chat client like Claude Desktop — see the Installation guide to get started.
Endpoints
List Tools
GET http://localhost:3350/api/toolsReturns all enabled tools with their JSON Schema input definitions:
{
"tools": [
{
"name": "ppal-read-live-set",
"title": "Read Live Set",
"description": "Read an overview of the Live Set...",
"annotations": { "readOnlyHint": true, "destructiveHint": false },
"inputSchema": { "type": "object", "properties": { ... } }
}
]
}Call a Tool
POST http://localhost:3350/api/tools/{toolName}
Content-Type: application/json
{ "trackIndex": 0, "include": ["session-clips"] }Returns:
{ "result": "...", "isError": false }- 200 with
isError: false— tool ran successfully - 200 with
isError: true— tool ran but reported an error (e.g. invalid path, execution error) - 404 — unknown or disabled tool
- 400 — invalid input (includes validation details)
Warnings from the Live API appear inline in the result text, prefixed with WARNING:. The ppal-update-* tools use this when updating multiple objects — if any individual operation fails or is inapplicable (e.g. setting quantize on an audio clip), it emits a warning and continues with the rest.
Response format: ?format=json
Default will change in v1.5.0
The default response format will change from compact to json in v1.5.0. We recommend explicitly using ?format=json for now, and removing the override once v1.5.0 ships. The compact JS-literal format is optimized for LLM context efficiency and is generally not the right fit for HTTP integrations — if you do specifically want it, keep ?format=compact explicit to stay forward-compatible.
When ?format is omitted, the server uses the global compact-output setting configured on the Setup tab of the Producer Pal Max for Live device. By default that setting is on, so result is a string in a compact JavaScript-literal syntax (unquoted keys, no whitespace) optimized for LLM token efficiency — the same format MCP clients receive.
For scripts that want structured data (no JSON.parse or jq | fromjson gymnastics), append ?format=json to the request — the server parses on your behalf and the wrapper shape changes:
# Compact JS-literal (default) — result is a string, warnings are inline
curl -X POST http://localhost:3350/api/tools/ppal-read-live-set \
-H 'Content-Type: application/json' -d '{}'
# → {"result":"{tempo:120,timeSignature:\"4/4\",...}","isError":false}
# JSON — result is the parsed value; warnings are a separate string array
curl -X POST 'http://localhost:3350/api/tools/ppal-read-live-set?format=json' \
-H 'Content-Type: application/json' -d '{}'
# → {"result":{"tempo":120,"timeSignature":"4/4",...},"isError":false}When ?format=json is set:
resultis the parsed value (object, array, number, string, etc.) — not a JSON-encoded string. Access fields directly:body.result.tempo.warningsis astring[](with theWARNING:prefix stripped), present only when the tool emitted any. In compact mode, warnings remain inline inresultfor backwards compatibility.- On error (
isError: true),resultis still a plain error string regardless of format — error messages are not JSON.
Pass ?format=compact or ?format=json to force a specific format regardless of the device-level setting. Other values return 400. Per-request format overrides apply to REST only and never affect MCP clients.
Per-request timeout: ?timeoutMs=N
Override the configured tool-call timeout for a single request. Useful for long-running operations (e.g. bulk clip generation) that need more headroom than the global timeout, or for short polling calls that should fail fast.
curl -X POST 'http://localhost:3350/api/tools/ppal-create-clip?timeoutMs=10000' \
-H 'Content-Type: application/json' \
-d '{"slot": "0/0", "length": "16:0", "notes": "..."}'timeoutMs must be a positive integer up to 60000 (60 seconds). Other values return 400. Combinable with ?format=:
POST /api/tools/{name}?format=json&timeoutMs=10000Quick Start with curl
# Read the Live Set overview
curl -X POST http://localhost:3350/api/tools/ppal-read-live-set \
-H 'Content-Type: application/json' -d '{}'
# Read track 0 with all clips
curl -X POST http://localhost:3350/api/tools/ppal-read-track \
-H 'Content-Type: application/json' \
-d '{"trackIndex": 0, "include": ["session-clips", "arrangement-clips"]}'
# List available tools
curl http://localhost:3350/api/toolsSample Scripts
Zero-dependency client examples — they use only built-in HTTP libraries. Copy and modify them for your own integrations.
Node.js
The Node.js client doubles as the Producer Pal Agent Skill script — see The bundled script for the full source and CLI reference. It works with Claude Code, Codex CLI, Gemini CLI, and any other agent runtime that reads the SKILL.md convention.
- Source:
examples/skills/producer-pal/ppal.mjs - Requires Node.js 18+ (for built-in
fetch)
Python
Works with Python 3.6+ (no dependencies).
#!/usr/bin/env python3
"""Producer Pal REST API client (Python, no dependencies).
Usage:
python ppal.py --list-tools [options]
python ppal.py <tool-name> [json-args] [options]
Options:
--url <baseUrl> override Producer Pal URL (default http://localhost:3350)
--timeout-ms <ms> per-request timeout (1-60000)
Examples:
python ppal.py --list-tools
python ppal.py ppal-read-live-set
python ppal.py ppal-read-track '{"trackIndex": 0}'
python ppal.py --list-tools | jq -r '.tools[].name'
python ppal.py ppal-read-live-set | jq .result.tempo
"""
import argparse
import json
import sys
import urllib.error
import urllib.parse
import urllib.request
def list_tools(base_url):
"""GET /api/tools — returns the full envelope `{"tools": [...]}` as a dict.
The tool list endpoint always returns JSON; it has no `?format` toggle.
"""
req = urllib.request.Request(f"{base_url}/api/tools")
with urllib.request.urlopen(req) as res:
return json.loads(res.read())
def call_tool(base_url, name, args, *, timeout_ms=None):
"""Call a Producer Pal tool by name with the given args.
Always uses `?format=json` so `result` is a parsed value (dict/list/etc.)
and warnings are surfaced as a separate `warnings` list.
"""
params = {"format": "json"}
if timeout_ms is not None:
params["timeoutMs"] = str(timeout_ms)
url = f"{base_url}/api/tools/{name}?{urllib.parse.urlencode(params)}"
data = json.dumps(args).encode()
req = urllib.request.Request(
url,
data=data,
headers={"Content-Type": "application/json"},
method="POST",
)
with urllib.request.urlopen(req) as res:
return json.loads(res.read())
def main():
parser = argparse.ArgumentParser(
description="Producer Pal REST API client",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=(
"Examples:\n"
" python ppal.py --list-tools\n"
" python ppal.py ppal-read-live-set\n"
' python ppal.py ppal-read-track \'{"trackIndex": 0}\''
),
)
parser.add_argument("tool", nargs="?", help="Tool name to call")
parser.add_argument(
"args",
nargs="?",
default="{}",
help="Tool arguments as a JSON object (default: {})",
)
parser.add_argument("--url", default="http://localhost:3350")
parser.add_argument("--timeout-ms", type=int, default=None, dest="timeout_ms")
parser.add_argument("--list-tools", action="store_true", dest="list_tools")
parsed = parser.parse_args()
if parsed.list_tools:
print(json.dumps(list_tools(parsed.url), indent=2))
return
if not parsed.tool:
parser.error(
"Missing tool name. Use --list-tools to discover tools, "
"or pass a tool name as the first argument."
)
try:
tool_args = json.loads(parsed.args)
except json.JSONDecodeError as e:
parser.error(f"Invalid JSON for tool args: {e}")
response = call_tool(
parsed.url,
parsed.tool,
tool_args,
timeout_ms=parsed.timeout_ms,
)
if response.get("isError"):
print(f"API error: {response['result']}", file=sys.stderr)
sys.exit(1)
print(json.dumps(response, indent=2))
if __name__ == "__main__":
try:
main()
except urllib.error.URLError as e:
if "Connection refused" in str(e.reason):
print(
"Could not connect to Producer Pal."
" Is Ableton Live running with the Producer Pal device?",
file=sys.stderr,
)
else:
raise
sys.exit(1)Tool Reference
Use the list tools endpoint to discover all available tools and their input schemas at runtime. You can also browse the full tool documentation on the Features page.
Raw Live API
The ppal-raw-live-api tool provides direct access to the Ableton Live Object Model for development, scripting, and debugging. It is always available via the REST API regardless of tool configuration.
WARNING
This is a development tool for scripting and debugging. It can read and modify any Live Set property — use it with care.
Request structure
The path parameter sets the initial Live Object Model object to operate on (e.g., "live_set", "live_set tracks 0", "live_set tracks 0 clip_slots 1 clip"). The operations array is then executed sequentially on that object. Use goto to navigate to a different object mid-sequence.
Available operation types:
| Type | Properties used | Description |
|---|---|---|
get_property / get | property | Read a property value |
set_property / set | property, value | Write a property value |
call_method / call | method, args (optional) | Call a method |
goto | value (path) | Navigate to a different object |
info | — | Get object info |
getProperty | property | Alias for get_property |
getChildIds | property (child type) | Get child object IDs |
exists | — | Check if the object exists |
getColor | — | Read object color |
setColor | value (hex string) | Write object color |
Examples
# Get the tempo
curl -X POST http://localhost:3350/api/tools/ppal-raw-live-api \
-H 'Content-Type: application/json' \
-d '{
"path": "live_set",
"operations": [{"type": "get_property", "property": "tempo"}]
}'
# Set the tempo to 140 BPM
curl -X POST http://localhost:3350/api/tools/ppal-raw-live-api \
-H 'Content-Type: application/json' \
-d '{
"path": "live_set",
"operations": [{"type": "set_property", "property": "tempo", "value": 140}]
}'
# Fire scene 0
curl -X POST http://localhost:3350/api/tools/ppal-raw-live-api \
-H 'Content-Type: application/json' \
-d '{
"path": "live_set",
"operations": [{"type": "call", "method": "fire_scene_at_index", "args": [0]}]
}'
# Chain multiple operations on one object
curl -X POST http://localhost:3350/api/tools/ppal-raw-live-api \
-H 'Content-Type: application/json' \
-d '{
"path": "live_set tracks 0",
"operations": [
{"type": "get", "property": "name"},
{"type": "get", "property": "color_index"},
{"type": "get", "property": "has_midi_input"}
]
}'INFO
This tool is always available via the REST API. It is only available via MCP when the ENABLE_RAW_LIVE_API environment variable is set at build time (npm run build:debug).
Tips
- The
inputSchemain the tool list response is standard JSON Schema, so you can use it for client-side validation or code generation. - The REST API shares the same tool configuration as MCP — tools enabled or disabled on the device apply to both interfaces.
- The REST API has no authentication (same as the MCP endpoint). It is designed for use on localhost or trusted networks only.