Same Material with different key frames
Object Info Node

As mentioned by @Gorgeous can use an object info node.
Control object emission strength (not material)
Using Object.color

Object color,
same as Color in the 
Properties Editor ‣ Object ‣ Viewport Display


For the example below am creating two new nodes, an object info node, separating the RGB via a separate RGB node and feeding this into the mix node factor.
Unfortunately I am not aware of how to use the Alpha channel, so am keyframing and using the Red channel.
This way the action is created on and belongs to the object using the material.
Minor adjustment to your code to add only one material with  the new nodes and links, and keyframe the object's red color in the loop.
import bpy

fade_objects = bpy.context.selected_objects

frame_dif = 10
frame_length = 40
frame_start = 0

fade_frame_start = frame_start
fade_frame_end = fade_frame_start + frame_length



# Create Material Object and Apply to character curve
mat = bpy.data.materials.new('Fade')  

mat.use_nodes = True
mat.blend_method = 'BLEND'

nodes = mat.node_tree.nodes
links = mat.node_tree.links

#Creating New Nodes
node_transparent = nodes.new('ShaderNodeBsdfTransparent')
node_mixshader = mat.node_tree.nodes.new('ShaderNodeMixShader')
node_bsdf = nodes['Principled BSDF']
node_matout = nodes['Material Output']


objinput = nodes.new(&quot;ShaderNodeObjectInfo&quot;)
seprgb = nodes.new(&quot;ShaderNodeSeparateRGB&quot;)


#Linking Nodes
links.new(node_transparent.outputs['BSDF'], node_mixshader.inputs[1])
links.new(node_bsdf.outputs['BSDF'], node_mixshader.inputs[2])
links.new(node_mixshader.outputs['Shader'], node_matout.inputs['Surface'])
links.new(objinput.outputs['Color'], seprgb.inputs['Image'])
links.new(seprgb.outputs[0], node_mixshader.inputs['Fac'])

for fade_object in fade_objects:
    fade_object.animation_data_create()
    fade_object.animation_data_clear()
    fade_object.data.materials.clear()
    fade_object.data.materials.append(mat)
    fade_object.color[0] = 0
    fade_object.keyframe_insert(&quot;color&quot;, index=0, frame=fade_frame_start)
    fade_object.color[0] = 1
    fade_object.keyframe_insert(&quot;color&quot;, index=0, frame=fade_frame_end)    
    #Add frame difference for next character for sliding effect
    fade_frame_start += frame_dif
    fade_frame_end += frame_dif

Other things to consider would be using drivers on the color field based on the current frame, or by using a single action that is attached to each object and offset via the NLA