Fish Audio Create Model (Ace Data Cloud)
Where you bundle voices into something with a name
- status
- task_id
- media_urls_json
- first_media_url
- result_json
- trace_id
What it does, in plain terms
Fish Audio's API has two layers, and people mix them up constantly. Create Voice uploads a sample and makes a voice. Create Model - this node - packages one or more voices into a named model you can browse, share and reuse under a title of your own. Think of a voice as "the timbre" and a model as "the release": the thing with a title, a description, a cover image, tags, and sample text attached.
Reach for it when you're publishing rather than just generating. If you're producing a voice for a personal project, you probably never need this node - grab the voice ID from Create Voice and feed it to Generate Audio. You need Create Model when you want a stable, named artefact: a cover image, tags, sample text, and a title you can look up again instead of hunting for an ID in an old note. And to be clear, "creating a model" here is not a training job - you're registering a configuration with the service, and it costs credits like the other writes.
How it works
The node builds a request body, checks it against the pack's vendored copy of the service's published contract with jsonschema, and only then sends POST /fish/model with your bearer token. A bad field fails locally and free.
The submission is asynchronous, so the node comes back almost immediately with status = pending and a task_id. That's normal, not a failure - the creation happens server-side. Note the asymmetry in how the pack talks to the network: read-only queries retry three times with backoff, but a paid write gets exactly one attempt. So if the response never arrives, hold on to the task_id and use Fish Audio Retrieve Task to check the outcome rather than blindly re-running this node and paying twice.
The inputs that matter
Two are required by the contract, and everything else is optional with an empty default meaning "omit it":
title- required. The name of the model. Pick something descriptive enough to survive being listed next to a hundred others.voices- required. Typed as a string by the contract, and it's the voice reference the model is built around. Get the ID from Create Voice'sresult_json, or from a search result; an empty string here fails validation before any request goes out.description- free text. Say what the voice is and what it's for.cover_image- a URL to a cover. Cosmetic, and only cosmetic.tags- a JSON array of strings in this widget, e.g.["narration", "en"]. Arrays and objects go in as JSON strings, per the pack's convention.texts- a JSON array of sample strings, again typed as an array of strings in the contract. The sample lines buyers/users see.enhance_audio_quality- a boolean, entered astrueorfalseand coerced for you. Worth trying on a noisy source sample.generate_sample- boolean. Whether the service should render a preview from yourtexts.parameters_json- the escape hatch for any API field without a matching widget,{}by default. A non-empty widget always wins over the same key in the JSON, and the pack's(from JSON/default)sentinel exists for the same reason on selector fields.
There's a small trap in the widget typing: tags and texts look like ordinary text boxes but the contract wants arrays. A bare string (narration) fails validation; ["narration"] works, and the contract's own type check is what enforces it.
Outputs and what to do with them
You get the same six strings as every tool node: status, task_id, media_urls_json, first_media_url, result_json, trace_id.
For this node, the payload you want lives in result_json - the created model's identifier, which is what you'll feed into other calls. media_urls_json will typically be empty unless you asked for a generated sample, in which case first_media_url points at it and you can wire that into Fish Audio Load Audio to actually hear the thing you just published. task_id is your receipt; if status says pending, hit it with Fish Audio Retrieve Task at wait_seconds 30–60 and check again. Lookups don't cost credits, re-creating does.
Install
The short version: ComfyUI Manager → search Fish Audio → confirm publisher acedatacloud, package Fish Audio by Ace Data Cloud (registry ID fish-audio) → install → restart. The manual version, identical for the whole pack:
cd ComfyUI/custom_nodes
git clone https://github.com/AceDataCloud/FishAudioComfyUI
cd FishAudioComfyUI && python -m pip install -r requirements.txt
Dependencies are requests and jsonschema, full stop. No model files, no GPU, no PyAV surprises unless you use the media loader. Then get your credential into the environment ComfyUI itself starts from:
export ACEDATACLOUD_API_KEY=your_key_here
Set it in the process environment and restart - that's the documented route, and the pack only ever reads ACEDATACLOUD_API_KEY or ACEDATACLOUD_API_TOKEN from there. Miss it and the node refuses with "Set ACEDATACLOUD_API_KEY in the ComfyUI process environment and restart."
Practical notes
- Validate before you spend. Contract validation happens before the request, so errors naming an offending field cost nothing. Read them.
- A malformed
parameters_jsonstops everything - "parameters_json must contain a JSON object."{}is a valid empty body, and it's the default. - This is an infrastructure node, not a creative one. Nothing here improves your audio. For a better voice you want a cleaner reference clip and Generate Audio's settings, not more model metadata.
- It writes to your account. Read the service's pricing before running it in a loop.
Inputs (9)
| Name | Type | Default | Description |
|---|---|---|---|
| parameters_json | STRING | {} | — |
| title | STRING | — | |
| voices | STRING | — | |
| description | STRING | — | |
| cover_image | STRING | — | |
| tags | STRING | — | |
| texts | STRING | — | |
| enhance_audio_quality | STRING | — | |
| generate_sample | STRING | — |
Outputs (6)
| Name | Type | Description |
|---|---|---|
| status | STRING | — |
| task_id | STRING | — |
| media_urls_json | STRING | — |
| first_media_url | STRING | — |
| result_json | STRING | — |
| trace_id | STRING | — |