API: Is there any faster way to add/link material nodes?
Make 1 material and copy it.
In as much as I agree with the comment of
@MarkusvonBroady in that investigating making a single material and passing an argument re which image to use, especially if texture image is the only  difference per material. eg akin to Is possible play video in reverse mode into Texture Image node? with a static input like Object.pass_index.
Can get some improvement in script above by making the material once and copying in N - 1 times.  (Note tested with 500, since life's too short for 5000 on my crap machine ATM)
import time
import bpy

group = bpy.data.node_groups.new('dummy group', 'ShaderNodeTree')
for i in range(5):
    group.inputs.new('NodeSocketFloat', str(i))

start = time.time()

mat = bpy.data.materials.new(name=&quot;0&quot;)
mat.use_nodes = True
tree = mat.node_tree
nodeGroup = tree.nodes.new('ShaderNodeGroup')
nodeGroup.node_tree = group
for i in range(5):
    nodeTex = tree.nodes.new('ShaderNodeTexImage')
    tree.links.new(nodeTex.outputs[0],nodeGroup.inputs[i])

def cp(i):
    m = mat.copy()
    m.name = f&quot;{i}&quot;
    return m
# even quicker without renaming
# mats = [mat.copy() for i in range(1, 500)]
# or consider using generator.
mats = [cp(i) for i in range(1, 500)]        
print((time.time() - start))

after running a few times this was 47x quicker than using question code. (with 500)