Nodes/FishAudioComfyUI/Fish Search Voices (Ace Data Cloud)
ComfyUI Node

Fish Search Voices (Ace Data Cloud)

The free node that finds you a voice before you spend anything

By AceDataCloud·Created 3 days ago·Updated 2 days ago· 0
Fish Search Voices (Ace Data Cloud)
    • status
    • task_id
    • media_urls_json
    • first_media_url
    • result_json
    • trace_id
    ◄parameters_json{}►
    ◄limit►
    ◄page_number►
    ◄query►
    ◄tag►
    ◄self►
    ◄author_id►
    ◄language►
    ◄title_language►
    ◄sort_by►

    The step everyone skips and then regrets

    Every other node in this pack needs a voice. Generate Audio takes a reference_id, Get Voice needs an id, Create Model wants a voice reference. This node is how you get one - it queries Fish Audio's public voice catalogue and hands you back a list to pick from.

    It's read-only, so it costs nothing, and that matters more than it sounds. In a paid pipeline the expensive mistake is the blind one: fire a generation with a voice ID you scraped from a forum post, get back a bored podcast host, spend credits learning that. Browsing first, shortlisting two or three candidates, auditioning them through Get Voice and Load Audio - that's the cheap path to a keeper.

    There's a tell in how people talk about Fish Audio: they rate its emotional delivery highly - ahead of ElevenLabs for realistic, dramatic reads - and then immediately ask how they're supposed to use it in a graph at all. Being the catalogue is the least glamorous job in this pack and the one that answers that question.

    How the request works

    The contract maps this tool to a GET on /fish/model, with everything passed as query parameters, and the pack labels it read-only - so it gets the retry treatment, three attempts with a short backoff, a 10-second connect timeout, a 60-second read timeout. No redirects. Your bearer token still goes in the header, so you need ACEDATACLOUD_API_KEY set even though the call is free.

    Two widgets get renamed on the way out, which is worth knowing when you read the service's API docs next to this node: limit is sent as page_size and query is sent as title. So the text you type in query is matched against the title of the voice, not its description or author.

    The inputs

    parameters_json is always there; the rest are the catalogue's query options, all optional, all strings, and an empty widget means "omit it":

    • limit - how many results per page. Sent to the API as page_size; the contract's default is 10.
    • page_number - which page. Defaults to 1. Bump it when a search has more hits than fit in one response.
    • query - the title search. This is your main tool: search for a language name, a word like news, or an accent descriptor, and you'll find more than the first ten results suggest.
    • tag - filter by a catalogue tag. Tags are how you get from "speech" to "audiobook narration" territory.
    • self - a boolean flag interpreted by the service rather than by the node, so treat it as "the API's own filter" and check its docs before relying on it.
    • author_id - everything published by one account, which is the fastest way to follow a creator whose voice you liked.
    • language - the language of the voice.
    • title_language - the language the title itself is written in, which is more useful than it looks when you're browsing a catalogue written in several scripts.
    • sort_by - the ordering of results.

    Because these are query parameters, the API's contract defines them loosely (a string, an integer, a boolean), which is the nice way of saying the service, not the node, decides what a valid value is. If sort_by or tag is wrong, you'll get a service error, not a helpful widget dropdown. Start broad, then narrow with combinations.

    Outputs

    status, task_id, media_urls_json, first_media_url, result_json and trace_id, same shape as the rest of the pack.

    For a catalogue search, result_json is the whole point - it's the list of voices with their IDs, titles, tags and sample URLs. Open it in a text-display node because reading a JSON blob through the pack's summary UI is not fun. media_urls_json will usually be empty for a catalogue query (a search result isn't media), and status reads succeeded on a clean call. task_id and trace_id are information for support rather than anything you'd wire.

    The loop to actually run:

    1. Search broadly, read result_json, shortlist two or three IDs.
    2. Paste an ID into Fish Audio Get Voice, take its first_media_url, feed it to Fish Audio Load Audio → SaveAudio, and listen.
    3. Put the winner in Generate Audio's reference_id.

    That's ten minutes of free browsing before your first paid render, and it will save you more credits than anything else on this page.

    Install

    Pack-wide, identical for all seven Fish Audio nodes. In ComfyUI Manager, search Fish Audio, confirm the publisher is acedatacloud and the package is Fish Audio by Ace Data Cloud (registry ID fish-audio), install, restart. Manually:

    cd ComfyUI/custom_nodes
    git clone https://github.com/AceDataCloud/FishAudioComfyUI
    cd FishAudioComfyUI && python -m pip install -r requirements.txt
    

    Then get the key into the ComfyUI process environment and restart the server:

    export ACEDATACLOUD_API_KEY=your_key_here
    

    The pack reads ACEDATACLOUD_API_KEY or ACEDATACLOUD_API_TOKEN from the environment only, so a key typed into a node field is ignored - and impossible to leak through a shared workflow. Missing key, and you get the pack's flat "Set ACEDATACLOUD_API_KEY in the ComfyUI process environment and restart."

    A note on trust you'd want anyway: this is a new, obscure pack from a commercial API vendor, and API-wrapper nodes hold a credential and call the network by design - exactly the shape that made one node pack in this ecosystem a credential stealer. The reassurance here is that the footprint is inspectable in a minute: two dependencies, no install script, no bundled binaries, and the code sits in nodes.py and client.py. Read it once before you paste a key.

    Gotchas

    • query only searches titles. If you get nothing back, that's often why - try a shorter, dumber term.
    • The default page size is 10. A search that "finds nothing" may just be one page in; try page_number 2.
    • self is not a search term. It's a flag the service interprets, so don't expect it to behave like a username field.
    • limit isn't limit server-side. If you're cross-referencing the API docs, look for page_size.
    • Nothing here is cached. Every Queue press is a fresh query. Free, but not instant - don't put it inside a loop you're also generating in.
    CategoryAce Data Cloud/Fish Audio

    Inputs (10)

    NameTypeDefaultDescription
    parameters_jsonSTRING{}—
    limitSTRING—
    page_numberSTRING—
    querySTRING—
    tagSTRING—
    selfSTRING—
    author_idSTRING—
    languageSTRING—
    title_languageSTRING—
    sort_bySTRING—

    Outputs (6)

    NameTypeDescription
    statusSTRING—
    task_idSTRING—
    media_urls_jsonSTRING—
    first_media_urlSTRING—
    result_jsonSTRING—
    trace_idSTRING—