I can create a PointCloud object in the blender python API via
pc_data = bpy.data.pointclouds.new(name)
pc_obj = bpy.data.objects.new(name, pc_data)
bpy.context.collection.objects.link(pc_obj)
Problem is, that does not give me any points to work with. There is stuff like pc_data.points.foreach_set("vector", my_np_array.ravel()) and pc_data.attributes.new(name='position', type='FLOAT_VECTOR', domain='POINT').foreach_set("vector", my_np_array.ravel()), which all seems like a working API but fails because the PointCloud is empty and I somehow need to resize it to the size that I need it. But how?
I have also looked into hacks, for example using the operators/OPS/UI API. The irony is that I haven't even found a way to spawn points in the UI. The only thing I can do is to select some points and then duplicate or delete them.
Problem: A deep dive into the python bindings in C/C++ revealed that there is currently (as of blender 4.5.1 LTS) simply no straight forward option. The C/C++-function to allocate points in the pointcloud is not used/exposed in the blender python binding C-files.
Solution: Use a hack to allocate points via a geometry nodes modifier which is added, loaded with a previously/manually created node group (here called "[spawn]" in the picture), adjusted and applied automatically, all in blender python API.
import bpy
def add_pointcloud_points_to_obj(obj, count):
obj.modifiers.new("spawn_dummy_geonodes", "NODES")
obj.modifiers['spawn_dummy_geonodes'].node_group = bpy.data.node_groups['[spawn]']
# This is the "howmany" socket, but the name is not the key.
# I don't know how to change the socket key
obj.modifiers["spawn_dummy_geonodes"]["Socket_2"] = count
# this applies the modifier (without bpy.ops)
depsgraph = bpy.context.evaluated_depsgraph_get()
obj_evaluated = obj.evaluated_get(depsgraph)
obj.data = obj_evaluated.data.copy()
obj.modifiers.remove(obj.modifiers['spawn_dummy_geonodes'])