There are three kinds of curve structures, although you only mention Bezier curves in the tag there are also NURBS/poly curves. This technique works for both, although you have to code NURBS/poly slightly differently.
You have to select the object containing the curve. The
data field of this object will be of type
bpy.types.Curve. The curve will have zero or more Splines. Each spline will contain either
Each spline may be cyclic, meaning it has no endpoint. If a spline has only one point, it is degenerate. Otherwise, the endpoints are always the first and last points in the spline.
That said, ignoring NURBS curves, this code will give you all of the endpoints of the active object, if it is a curve.
import bpy def is_cyclic(spline): return spline.use_cyclic_u or spline.use_cyclic_v def find_endpoints(spline): if spline.type == 'BEZIER': return (spline.bezier_points, spline.bezier_points[-1]) elif spline.type == 'NURBS': return (spline.points, spline.points) else: return None def all_endpoints(object): """ Returns a list consisting of None, indicating that the spline is cyclic tuple (EP 0, EP 1), containing the endpoints of the spline If EP 0 and EP 1 are the same then the spline has only one point """ if object.type != 'CURVE': return None all_endpoints =  for spline in object.data.splines: if is_cyclic(spline): all_endpoints.append(None) else: all_endpoints.append(find_endpoints(spline)) return all_endpoints endpoint_list = all_endpoints(bpy.context.active_object)