Why is jq output a string rather than array?
jq writes it output to stdout, a file handle. File handles accept/provide sequences of bytes. This is neither a string nor an array; these concepts are not applicable here.
Backticks substitute the output of the program into the command to execute. Again, this is neither a string nor an array; these concepts are not applicable here.
As for why category_ids isn't an array, the man page for bash says the following:
An indexed array is created automatically if any variable is assigned to using the syntax name[sub‐script]=value. The subscript is treated as an arithmetic expression that must evaluate to a number. To explicitly declare an indexed array, use declare -a name (see SHELL BUILTIN COMMANDS below). declare -a name[subscript] is also accepted; the subscript is ignored.
None of these things were done, so no array was created.
I have to turn it into an array to iterate properly.
Nah, just use a while read loop that reads a line at a time.
while IFS= read -r key; do
printf 'key: %s\n' "$key"
done <<<"$category_ids"
category_idsis not an array; it's a regular parameter that contains whatever string is written to standard output byjq. Second,jqcan't write anything but a series of bytes to standard output; it certainly cannot interface directly with the shell to "inject" a non-string value into the shell's address space. (Not that a shell array is even a proper data structure; it's more like a handful of syntactic tricks to simulate an array value.)keysproduces a JSON array, but the shell has no idea that's what is it, it just sees a stream of bytes (and interprets that as a string, because that's the only data type the shell knows about).