3636 "platform_position" ,
3737})
3838
39+ # Breakdowns that collide with the default action_breakdowns=[action_type] but
40+ # are real Meta fields (so we explicitly clear action_breakdowns instead of
41+ # dropping the action-typed response fields). For non-DCO ads this returns
42+ # empty rows; for DCO ads it returns the breakdown dimension populated.
43+ _BREAKDOWNS_REQUIRING_EMPTY_ACTION_BREAKDOWNS = frozenset ({
44+ "media_type" ,
45+ })
46+
3947_ACTION_TYPED_FIELDS = frozenset ({
4048 "actions" ,
4149 "action_values" ,
@@ -66,6 +74,7 @@ async def get_insights(object_id: str = "", access_token: Optional[str] = None,
6674 time_range : Union [str , Dict [str , str ]] = "maximum" , breakdown : str = "" ,
6775 level : str = "ad" , limit : int = 25 , after : str = "" ,
6876 action_attribution_windows : Optional [List [str ]] = None ,
77+ action_breakdowns : Optional [List [str ]] = None ,
6978 compact : bool = False ,
7079 account_id : str = "" , campaign_id : str = "" ,
7180 adset_id : str = "" , ad_id : str = "" ) -> str :
@@ -98,6 +107,10 @@ async def get_insights(object_id: str = "", access_token: Optional[str] = None,
98107 gen_ai_asset_type
99108 NOTE: Asset breakdowns (image_asset, video_asset, etc.) only return data for ads
100109 running with Dynamic Creative; for non-DCO ads, expect empty rows.
110+ NOTE: media_type collides with the default action_breakdowns=[action_type], so
111+ this tool auto-overrides action_breakdowns to [] when you pass media_type.
112+ Action-typed metrics (actions, action_values, conversions) are still returned
113+ but are no longer sliced by action_type alongside media_type.
101114 media_asset_url, media_creator, media_destination_url, media_format,
102115 media_origin_url, and media_text_content are NOT supported by Meta's Insights API
103116 (Meta returns "(#100) Tried accessing nonexisting field"). Use the asset breakdowns
@@ -122,6 +135,10 @@ async def get_insights(object_id: str = "", access_token: Optional[str] = None,
122135 after: Pagination cursor to get the next set of results. Use the 'after' cursor from previous response's paging.next field.
123136 action_attribution_windows: Optional list of attribution windows (e.g., ["1d_click", "7d_click", "1d_view"]).
124137 When specified, actions include additional fields for each window. The 'value' field always shows 7d_click.
138+ action_breakdowns: Optional list of action_breakdowns to apply to action-typed metrics. Pass [] to disable
139+ the default action_type slicing (required when combining action data with breakdowns that collide
140+ with action_type, e.g. media_type — auto-applied for media_type when not set).
141+ Meta supports values like action_type, action_target_id, action_destination, etc.
125142 compact: When True, strips redundant action-type duplicates from the response
126143 (omni_*, onsite_web_*, offsite_conversion.fb_pixel_*, etc.) to reduce
127144 payload size by ~60%. The canonical action types (purchase, add_to_cart,
@@ -162,6 +179,12 @@ async def get_insights(object_id: str = "", access_token: Optional[str] = None,
162179 breakdown_set .add ("publisher_platform" )
163180 if breakdown_set & _BREAKDOWNS_INCOMPATIBLE_WITH_ACTION_TYPE :
164181 fields = [f for f in fields if f not in _ACTION_TYPED_FIELDS ]
182+ # media_type collides with action_breakdowns=[action_type] but is a real
183+ # field — override action_breakdowns to empty so the request succeeds with
184+ # the action-typed metrics intact.
185+ override_action_breakdowns_empty = bool (
186+ breakdown_set & _BREAKDOWNS_REQUIRING_EMPTY_ACTION_BREAKDOWNS
187+ )
165188
166189 params = {
167190 "fields" : "," .join (fields ),
@@ -182,6 +205,13 @@ async def get_insights(object_id: str = "", access_token: Optional[str] = None,
182205
183206 if breakdown_values :
184207 params ["breakdowns" ] = "," .join (breakdown_values )
208+ # Caller-supplied action_breakdowns wins; otherwise auto-empty for media_type.
209+ if action_breakdowns is not None :
210+ params ["action_breakdowns" ] = (
211+ "[" + "," .join (action_breakdowns ) + "]" if action_breakdowns else "[]"
212+ )
213+ elif override_action_breakdowns_empty :
214+ params ["action_breakdowns" ] = "[]"
185215
186216 if after :
187217 params ["after" ] = after
0 commit comments