Here is your script (a) doing what I think you want it to do.  It makes a mesh object at each frame, and adds a location keyframe at the frame it was created in script.

If you are using an operator, need to be in, or set the context it requires. Make a new object `obj` context with `context.scene.objects.active = obj`.  If it works on selected objects, like say join, need to make sure other objects are (de)selected correctly by setting `obj.select` accordingly on each.

Going to use [`bpy_struct.keyframe_insert(...)`][1] rather than the operator.  This will be quicker, especially for high numbers. 

    import bpy
    from math import sin, cos
    scn = bpy.context.scene

    numY = 10
    freq = 1
    amp = 1
    scale = 1

    for numX in range (2, 20):
        verts = []
        faces = []

        for i in range (0, numX):
            x = scale * i
            for j in range (0, numY):                
                y = scale * j
                z = scale * amp * (cos(i * freq) + sin(j * freq))
                vert = (x, y, z)
                verts.append(vert)

        count = 0
        for i in range (0, numY * (numX - 1)):
            if count < numY - 1:
                A = i
                B = i + 1
                C = (i + numY) + 1
                D = (i + numY)

                face = (A, B, C, D)
                faces.append(face)
                count = count + 1
            else:
                count = 0

        mesh = bpy.data.meshes.new("Wave")

        mesh.from_pydata(verts, [], faces)
        mesh.update(calc_edges = True)

        obj = bpy.data.objects.new("Wave", mesh)

        obj.location = (0, 0, 0)
        scn.objects.link(obj)
        obj.keyframe_insert("location", frame=numX, group="Location")


  [1]: https://docs.blender.org/api/blender_python_api_current/bpy.types.bpy_struct.html#bpy.types.bpy_struct.keyframe_insert