Python SDK — Quickstart

Go from zero to downloaded audio in one script. Clone a template, give it a script and a voice, run it, and fetch the result.

Clone a gallery template into a workflow, give it a script and a voice, run it, and download the audio, all in code. No existing workflow needed.

Prefer the CLI? See the Quickstart for the same flow in the terminal.

1. Install & authenticate

$pip install onepin # Python 3.10+
$export ONEPIN_API_KEY=op_live_... # create a key at app.onepin.ai

The key needs the workflows:write scope (it clones and edits a workflow), plus templates:read, workflows:read, and workflows:run.

2. Pick a template

This Quickstart uses the starter template, the simplest graph: one script, one voice, one export. List the gallery to see what’s available:

1import os
2from onepin import OnePinClient
3
4client = OnePinClient(token=os.environ["ONEPIN_API_KEY"]) # the SDK does not auto-read env vars
5
6# list() returns an envelope; the items are in .data
7for t in client.templates.list(limit=10).data:
8 print(t.id, t.name, "" if t.is_starter else "")

The script in step 3 selects the is_starter template automatically, so there’s nothing to copy here.

3. Configure it, run it, download it

Gallery templates ship with an empty script and no voice, so you fill those two nodes in the workflow’s definition before running (otherwise runs.start() returns a 422). The script below does the whole flow (clone, configure, run, download) for a starter-shaped template. Just run it:

1import os, time, httpx
2from onepin import OnePinClient
3
4client = OnePinClient(token=os.environ["ONEPIN_API_KEY"])
5# use the starter template (one script, one voice); swap for any single-voice template's id
6template_id = next(t.id for t in client.templates.list(limit=50).data if t.is_starter)
7locale = "en-us" # language to generate
8provider, model = "elevenlabs", "eleven_multilingual_v2"
9# if runs.start() below returns 403, this model isn't on your plan;
10# pick another from client.providers.list_catalog_provider_models(provider).data
11
12# 1. Clone the template into a workflow (single responses carry the object at .data)
13workflow_id = client.templates.clone(template_id, name="My first workflow").data.id
14
15# 2. Pick a voice available for this language
16voice = next(v for v in client.providers
17 .list_catalog_provider_model_voices(provider, model, limit=50).data
18 if locale in (v.supported_languages or []))
19
20# 3. Fill the empty script and voice nodes, then save the definition
21definition = client.workflows.get(workflow_id=workflow_id).data.model_dump()["definition"]
22for node in definition["graph"]["nodes"]:
23 if node["type"] == "source_script":
24 node["config"] = {"input_type": "text", "source_language": locale,
25 "text": "Hello from the Onepin Python SDK quickstart."}
26 elif node["type"] == "processing_generator":
27 # a voice is assigned by provider_voice_id (not the Onepin voice id), provider, and model
28 node["config"] = {"voice_map": {locale: [
29 {"voice_id": voice.provider_voice_id, "provider": provider, "model": model}]}}
30client.workflows.patch_workflow(workflow_id, definition=definition)
31
32# 4. Run it and wait
33run_id = client.workflows.runs.start(workflow_id).data.id
34while True:
35 status = client.workflows.runs.status(workflow_id, run_id).data
36 print(status.status, f"{status.finished_steps}/{status.total_steps}")
37 if status.status in ("completed", "failed", "cancelled"):
38 break
39 time.sleep(5)
40if status.status != "completed":
41 raise RuntimeError(f"run {status.status}: {status.error}")
42
43# 5. Download the result (download_run returns a temporary URL; fetch the file from it)
44download = client.workflows.download_run(workflow_id, run_id).data
45resp = httpx.get(download.url)
46resp.raise_for_status()
47with open(download.filename, "wb") as f:
48 f.write(resp.content)
49print("saved:", download.filename)

Expected output:

pending 0/0
completed 3/3
saved: My-first-workflow_2026-07-01.zip

The saved file lands in your working directory: a .zip of the generated MP3s plus a manifest.csv listing them.

This config assumes a starter-shaped graph: one generator, one locale. Templates that translate or use multiple voices have more generator nodes, and each needs its own voice_map. See Usage to configure them individually.

Next steps

  • Usage: every resource, pagination, async, and the export layout
  • Upload a script: use a .txt/.docx file instead of inline text
  • Errors: exception handling and retries
  • Authentication: scopes, credentials, key rotation