Python: Locate specific islands of geometry within a mesh object
Search for the 12  tri-fan poles.
Finding all the loose parts is going to be a rate determining step
How to detect disconnected parts within a selection?
How to find the number of loose parts with Blender&#39;s Python API?
but  for quoted case

The example object in this screenshot has two circles I want to match.
Each circle has 13 verts, 12 faces (which are all tris), and a total
area of approximately 3. So if an island matches those criteria, we
can consider it to qualify as the kind of circle we are after.

would not bother grinding away to find loose parts,instead search for potential pole verts, ie those that have exactly 12 linked edges and 12 triangles linked.
From there make sure that all the opposite edges of the tris, ie the edge not containing the pole is a boundary. Each vert of the boundary should be connected to exactly 3 edges.
Test script, prints the index of the poles the match the above criterea.
import bpy
import bmesh

context = bpy.context
ob = context.edit_object
me = ob.data

bm = bmesh.from_edit_mesh(me)

poles = [
        v for v in bm.verts 
        if len(v.link_faces) == len(v.link_edges) == 12
        and all(len(f.edges) == 3 for f in v.link_faces)
        ]
print(&quot;Potential Poles&quot;, [p.index for p in poles])
for p in poles:
    def check(f):
        return (
            bool(
                e for e in f.edges 
                if p not in e.verts 
                and e.is_boundary
                )
            and
                all(
                len(e.other_vert(p).link_edges) == 3
                for e in p.link_edges
                )
            )
    if all(check(f) for f in p.link_faces):
        print(p.index, &quot;is a 12 tri fan pole&quot;)

once we have p and know it is a disconnected tri fan all the 13 verts would be for example
[p] + [e.other_vert(p) for e in p.link_edges]

the area
sum(f.calc_area() for f in p.link_faces) 

Putting it all together
(Added by @Mentalist based on @batFINGER's code)
Here the script will check for triangle fan islands of geometry that match the specified face count and total area, find their centers, and take some action after finding those centers (in this example it places an Empty at each).
import bpy
import bmesh
import statistics

context = bpy.context
obj = context.view_layer.objects.active

def place_empty(coordinates): # create an Empty at the specified location
    bpy.ops.object.empty_add(type='PLAIN_AXES', radius=0.1, align='WORLD', location=coordinates, scale=(1, 1, 1))

def check_geom(ob, facecount, match_area, decplaces=3):
    if ob.type == 'MESH': # make sure it's a mesh
        bpy.ops.object.mode_set(mode='EDIT')
        me = ob.data
        bm = bmesh.from_edit_mesh(me).copy() # bmesh as a COPY
        bm.transform(ob.matrix_world) # that copy gets converted to world coords

        poles = [
            v for v in bm.verts 
            if len(v.link_faces) == len(v.link_edges) == facecount
            and all(len(f.edges) == 3 for f in v.link_faces)
            ]
        print(&quot;Potential Poles&quot;, [p.index for p in poles])
        for p in poles:
            def check(f):
                return (
                    bool(
                        e for e in f.edges 
                        if p not in e.verts 
                        and e.is_boundary
                        )
                    and
                        all(
                        len(e.other_vert(p).link_edges) == 3
                        for e in p.link_edges
                        )
                    )
            area = sum(f.calc_area() for f in p.link_faces)
            if all(check(f) for f in p.link_faces):
                print('\n' + str(p.index) + &quot; is a &quot; + str(facecount) + &quot; tri fan pole, with an area of &quot; + str(area) )
            if round(area, decplaces) == round(match_area, decplaces): # things to do if the area matches
                bpy.ops.object.mode_set(mode='OBJECT')
                v_x = []
                v_y = []
                v_z = []
                
                linked_verts = [p] + [e.other_vert(p) for e in p.link_edges] # verts of this island
                
                for v in linked_verts:
                    # populate lists with coordinates for each axis
                    v_x.append(v.co.x)
                    v_y.append(v.co.y)
                    v_z.append(v.co.z)
                    
                # average each list's values to find the center
                c_x = statistics.mean(v_x)
                c_y = statistics.mean(v_y)
                c_z = statistics.mean(v_z)
                center = [c_x, c_y, c_z] # mean of island vertices
                print('Center location: ' + str(center) )
                
                place_empty(center) # the action to take upon finding the circle centers
    else:
        print('Cannot proceed. Active object is not a mesh.')
                
check_geom(obj, 48, 0.045) # params: the object, the face count, the total face area (object must be a triangle fan mesh)

 (684KB)