Skip to main content
Glama

create_ad_creative

Create Meta ad creatives using uploaded images, specifying copy, headlines, CTAs, and dynamic creative options for Facebook and Instagram campaigns.

Instructions

Create a new ad creative using an uploaded image hash.

Args:
    account_id: Meta Ads account ID (format: act_XXXXXXXXX)
    image_hash: Hash of the uploaded image
    access_token: Meta API access token (optional - will use cached token if not provided)
    name: Creative name
    page_id: Facebook Page ID to be used for the ad
    link_url: Destination URL for the ad
    message: Ad copy/text
    headline: Single headline for simple ads (cannot be used with headlines)
    headlines: List of headlines for dynamic creative testing (cannot be used with headline)
    description: Single description for simple ads (cannot be used with descriptions)
    descriptions: List of descriptions for dynamic creative testing (cannot be used with description)
    dynamic_creative_spec: Dynamic creative optimization settings
    call_to_action_type: Call to action button type (e.g., 'LEARN_MORE', 'SIGN_UP', 'SHOP_NOW')
    lead_gen_form_id: Lead generation form ID for lead generation campaigns. Required when using
                     lead generation CTAs like 'SIGN_UP', 'GET_OFFER', 'SUBSCRIBE', etc.
    instagram_actor_id: Optional Instagram account ID for Instagram placements

Returns:
    JSON response with created creative details

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
account_idYes
image_hashYes
access_tokenNo
nameNo
page_idNo
link_urlNo
messageNo
headlineNo
headlinesNo
descriptionNo
descriptionsNo
dynamic_creative_specNo
call_to_action_typeNo
lead_gen_form_idNo
instagram_actor_idNo

Implementation Reference

  • Primary handler implementation for the create_ad_creative MCP tool. Decorated with @mcp_server.tool() for registration and @meta_api_tool for API handling. Supports both simple creatives (object_story_spec) and dynamic creatives (asset_feed_spec with multiple headlines/descriptions). Handles page discovery, validation, and API calls to create the creative.
    @mcp_server.tool()
    @meta_api_tool
    async def create_ad_creative(
        account_id: str,
        image_hash: str,
        access_token: Optional[str] = None,
        name: Optional[str] = None,
        page_id: Optional[str] = None,
        link_url: Optional[str] = None,
        message: Optional[str] = None,
        headline: Optional[str] = None,
        headlines: Optional[List[str]] = None,
        description: Optional[str] = None,
        descriptions: Optional[List[str]] = None,
        dynamic_creative_spec: Optional[Dict[str, Any]] = None,
        call_to_action_type: Optional[str] = None,
        lead_gen_form_id: Optional[str] = None,
        instagram_actor_id: Optional[str] = None
    ) -> str:
        """
        Create a new ad creative using an uploaded image hash.
        
        Args:
            account_id: Meta Ads account ID (format: act_XXXXXXXXX)
            image_hash: Hash of the uploaded image
            access_token: Meta API access token (optional - will use cached token if not provided)
            name: Creative name
            page_id: Facebook Page ID to be used for the ad
            link_url: Destination URL for the ad
            message: Ad copy/text
            headline: Single headline for simple ads (cannot be used with headlines)
            headlines: List of headlines for dynamic creative testing (cannot be used with headline)
            description: Single description for simple ads (cannot be used with descriptions)
            descriptions: List of descriptions for dynamic creative testing (cannot be used with description)
            dynamic_creative_spec: Dynamic creative optimization settings
            call_to_action_type: Call to action button type (e.g., 'LEARN_MORE', 'SIGN_UP', 'SHOP_NOW')
            lead_gen_form_id: Lead generation form ID for lead generation campaigns. Required when using
                             lead generation CTAs like 'SIGN_UP', 'GET_OFFER', 'SUBSCRIBE', etc.
            instagram_actor_id: Optional Instagram account ID for Instagram placements
        
        Returns:
            JSON response with created creative details
        """
        # Check required parameters
        if not account_id:
            return json.dumps({"error": "No account ID provided"}, indent=2)
        
        if not image_hash:
            return json.dumps({"error": "No image hash provided"}, indent=2)
        
        if not name:
            name = f"Creative {int(time.time())}"
        
        # Ensure account_id has the 'act_' prefix
        if not account_id.startswith("act_"):
            account_id = f"act_{account_id}"
        
        # Enhanced page discovery: If no page ID is provided, use robust discovery methods
        if not page_id:
            try:
                # Use the comprehensive page discovery logic from get_account_pages
                page_discovery_result = await _discover_pages_for_account(account_id, access_token)
                
                if page_discovery_result.get("success"):
                    page_id = page_discovery_result["page_id"]
                    page_name = page_discovery_result.get("page_name", "Unknown")
                    print(f"Auto-discovered page ID: {page_id} ({page_name})")
                else:
                    return json.dumps({
                        "error": "No page ID provided and no suitable pages found for this account",
                        "details": page_discovery_result.get("message", "Page discovery failed"),
                        "suggestions": [
                            "Use get_account_pages to see available pages",
                            "Use search_pages_by_name to find specific pages",
                            "Provide a page_id parameter manually"
                        ]
                    }, indent=2)
            except Exception as e:
                return json.dumps({
                    "error": "Error during page discovery",
                    "details": str(e),
                    "suggestion": "Please provide a page_id parameter or use get_account_pages to find available pages"
                }, indent=2)
        
        # Validate headline/description parameters - cannot mix simple and complex
        if headline and headlines:
            return json.dumps({"error": "Cannot specify both 'headline' and 'headlines'. Use 'headline' for single headline or 'headlines' for multiple."}, indent=2)
        
        if description and descriptions:
            return json.dumps({"error": "Cannot specify both 'description' and 'descriptions'. Use 'description' for single description or 'descriptions' for multiple."}, indent=2)
        
        # Validate dynamic creative parameters (plural forms only)
        if headlines:
            if len(headlines) > 5:
                return json.dumps({"error": "Maximum 5 headlines allowed for dynamic creatives"}, indent=2)
            for i, h in enumerate(headlines):
                if len(h) > 40:
                    return json.dumps({"error": f"Headline {i+1} exceeds 40 character limit"}, indent=2)
        
        if descriptions:
            if len(descriptions) > 5:
                return json.dumps({"error": "Maximum 5 descriptions allowed for dynamic creatives"}, indent=2)
            for i, d in enumerate(descriptions):
                if len(d) > 125:
                    return json.dumps({"error": f"Description {i+1} exceeds 125 character limit"}, indent=2)
        
        # Prepare the creative data
        creative_data = {
            "name": name
        }
        
        # Choose between asset_feed_spec (dynamic creative) or object_story_spec (traditional)
        # ONLY use asset_feed_spec when user explicitly provides plural parameters (headlines/descriptions)
        if headlines or descriptions:
            # Use asset_feed_spec for dynamic creatives with multiple variants
            # Structure based on Meta API working example from documentation
            # ad_formats must be specified with exactly one format
            asset_feed_spec = {
                "images": [{"hash": image_hash}],
                "link_urls": [{"website_url": link_url if link_url else "https://facebook.com"}],
                "ad_formats": ["SINGLE_IMAGE"]
            }
            
            # Handle headlines - Meta API uses "titles" not "headlines" in asset_feed_spec
            if headlines:
                asset_feed_spec["titles"] = [{"text": headline_text} for headline_text in headlines]
                
            # Handle descriptions  
            if descriptions:
                asset_feed_spec["descriptions"] = [{"text": description_text} for description_text in descriptions]
            
            # Add message as bodies - Meta API uses "bodies" not "primary_texts" in asset_feed_spec
            if message:
                asset_feed_spec["bodies"] = [{"text": message}]
            
            # Add call_to_action_types if provided
            if call_to_action_type:
                asset_feed_spec["call_to_action_types"] = [call_to_action_type]
            
            creative_data["asset_feed_spec"] = asset_feed_spec
            
            # For dynamic creatives with asset_feed_spec, object_story_spec only needs page_id
            # Link information is already in asset_feed_spec.link_urls
            creative_data["object_story_spec"] = {
                "page_id": page_id
            }
        else:
            # Use traditional object_story_spec with link_data for simple creatives
            creative_data["object_story_spec"] = {
                "page_id": page_id,
                "link_data": {
                    "image_hash": image_hash,
                    "link": link_url if link_url else "https://facebook.com"
                }
            }
            
            # Add optional parameters if provided
            if message:
                creative_data["object_story_spec"]["link_data"]["message"] = message
            
            # Add headline (singular) to link_data
            if headline:
                creative_data["object_story_spec"]["link_data"]["name"] = headline
            
            # Add description (singular) to link_data
            if description:
                creative_data["object_story_spec"]["link_data"]["description"] = description
            
            # Add call_to_action to link_data for simple creatives
            if call_to_action_type:
                cta_data = {"type": call_to_action_type}
                
                # Add lead form ID to value object if provided (required for lead generation campaigns)
                if lead_gen_form_id:
                    cta_data["value"] = {"lead_gen_form_id": lead_gen_form_id}
                
                creative_data["object_story_spec"]["link_data"]["call_to_action"] = cta_data
        
        # Add dynamic creative spec if provided
        if dynamic_creative_spec:
            creative_data["dynamic_creative_spec"] = dynamic_creative_spec
        
        if instagram_actor_id:
            creative_data["instagram_actor_id"] = instagram_actor_id
        
        # Prepare the API endpoint for creating a creative
        endpoint = f"{account_id}/adcreatives"
        
        try:
            # Make API request to create the creative
            data = await make_api_request(endpoint, access_token, creative_data, method="POST")
            
            # If successful, get more details about the created creative
            if "id" in data:
                creative_id = data["id"]
                creative_endpoint = f"{creative_id}"
                creative_params = {
                    "fields": "id,name,status,thumbnail_url,image_url,image_hash,object_story_spec,asset_feed_spec,url_tags,link_url"
                }
                
                creative_details = await make_api_request(creative_endpoint, access_token, creative_params)
                return json.dumps({
                    "success": True,
                    "creative_id": creative_id,
                    "details": creative_details
                }, indent=2)
            
            return json.dumps(data, indent=2)
        
        except Exception as e:
            return json.dumps({
                "error": "Failed to create ad creative",
                "details": str(e),
                "creative_data_sent": creative_data
            }, indent=2)
  • MCP tool registration decorator applied to the create_ad_creative function.
    @mcp_server.tool()
  • Input schema and documentation defined in the function docstring, describing all parameters, types, and usage for the MCP tool.
    Create a new ad creative using an uploaded image hash.
    
    Args:
        account_id: Meta Ads account ID (format: act_XXXXXXXXX)
        image_hash: Hash of the uploaded image
        access_token: Meta API access token (optional - will use cached token if not provided)
        name: Creative name
        page_id: Facebook Page ID to be used for the ad
        link_url: Destination URL for the ad
        message: Ad copy/text
        headline: Single headline for simple ads (cannot be used with headlines)
        headlines: List of headlines for dynamic creative testing (cannot be used with headline)
        description: Single description for simple ads (cannot be used with descriptions)
        descriptions: List of descriptions for dynamic creative testing (cannot be used with description)
        dynamic_creative_spec: Dynamic creative optimization settings
        call_to_action_type: Call to action button type (e.g., 'LEARN_MORE', 'SIGN_UP', 'SHOP_NOW')
        lead_gen_form_id: Lead generation form ID for lead generation campaigns. Required when using
                         lead generation CTAs like 'SIGN_UP', 'GET_OFFER', 'SUBSCRIBE', etc.
        instagram_actor_id: Optional Instagram account ID for Instagram placements
    
    Returns:
        JSON response with created creative details
    """

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pipeboard-co/meta-ads-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server