Skip to main content
Glama

upload_ad_image

Upload images to Meta Ads for use in ad creatives. Supports direct file uploads or fetching from URLs, returning image details including hash for creative creation.

Instructions

Upload an image to use in Meta Ads creatives.

Args:
    account_id: Meta Ads account ID (format: act_XXXXXXXXX)
    access_token: Meta API access token (optional - will use cached token if not provided)
    file: Data URL or raw base64 string of the image (e.g., "data:image/png;base64,iVBORw0KG...")
    image_url: Direct URL to an image to fetch and upload
    name: Optional name for the image (default: filename)

Returns:
    JSON response with image details including hash for creative creation

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
account_idYes
access_tokenNo
fileNo
image_urlNo
nameNo

Implementation Reference

  • Main handler function for upload_ad_image tool. Uploads image (from base64 data URL or remote URL) to Meta Ads adimages endpoint, returns structured JSON with image_hash for use in creatives.
    @mcp_server.tool()
    @meta_api_tool
    async def upload_ad_image(
        account_id: str,
        access_token: Optional[str] = None,
        file: Optional[str] = None,
        image_url: Optional[str] = None,
        name: Optional[str] = None
    ) -> str:
        """
        Upload an image to use in Meta Ads creatives.
        
        Args:
            account_id: Meta Ads account ID (format: act_XXXXXXXXX)
            access_token: Meta API access token (optional - will use cached token if not provided)
            file: Data URL or raw base64 string of the image (e.g., "data:image/png;base64,iVBORw0KG...")
            image_url: Direct URL to an image to fetch and upload
            name: Optional name for the image (default: filename)
        
        Returns:
            JSON response with image details including hash for creative creation
        """
        # Check required parameters
        if not account_id:
            return json.dumps({"error": "No account ID provided"}, indent=2)
        
        # Ensure we have image data
        if not file and not image_url:
            return json.dumps({"error": "Provide either 'file' (data URL or base64) or 'image_url'"}, indent=2)
        
        # Ensure account_id has the 'act_' prefix for API compatibility
        if not account_id.startswith("act_"):
            account_id = f"act_{account_id}"
        
        try:
            # Determine encoded_image (base64 string without data URL prefix) and a sensible name
            encoded_image: str = ""
            inferred_name: str = name or ""
    
            if file:
                # Support data URL (e.g., data:image/png;base64,...) and raw base64
                data_url_prefix = "data:"
                base64_marker = "base64,"
                if file.startswith(data_url_prefix) and base64_marker in file:
                    header, base64_payload = file.split(base64_marker, 1)
                    encoded_image = base64_payload.strip()
    
                    # Infer file extension from MIME type if name not provided
                    if not inferred_name:
                        # Example header: data:image/png;...
                        mime_type = header[len(data_url_prefix):].split(";")[0].strip()
                        extension_map = {
                            "image/png": ".png",
                            "image/jpeg": ".jpg",
                            "image/jpg": ".jpg",
                            "image/webp": ".webp",
                            "image/gif": ".gif",
                            "image/bmp": ".bmp",
                            "image/tiff": ".tiff",
                        }
                        ext = extension_map.get(mime_type, ".png")
                        inferred_name = f"upload{ext}"
                else:
                    # Assume it's already raw base64
                    encoded_image = file.strip()
                    if not inferred_name:
                        inferred_name = "upload.png"
            else:
                # Download image from URL
                try:
                    image_bytes = await try_multiple_download_methods(image_url)
                except Exception as download_error:
                    return json.dumps({
                        "error": "We couldn’t download the image from the link provided.",
                        "reason": "The server returned an error while trying to fetch the image.",
                        "image_url": image_url,
                        "details": str(download_error),
                        "suggestions": [
                            "Make sure the link is publicly reachable (no login, VPN, or IP restrictions).",
                            "If the image is hosted on a private app or server, move it to a public URL or a CDN and try again.",
                            "Verify the URL is correct and serves the actual image file."
                        ]
                    }, indent=2)
    
                if not image_bytes:
                    return json.dumps({
                        "error": "We couldn’t access the image at the link you provided.",
                        "reason": "The image link doesn’t appear to be publicly accessible or didn’t return any data.",
                        "image_url": image_url,
                        "suggestions": [
                            "Double‑check that the link is public and does not require login, VPN, or IP allow‑listing.",
                            "If the image is stored in a private app (for example, a self‑hosted gallery), upload it to a public URL or a CDN and try again.",
                            "Confirm the URL is correct and points directly to an image file (e.g., .jpg, .png)."
                        ]
                    }, indent=2)
    
                import base64  # Local import
                encoded_image = base64.b64encode(image_bytes).decode("utf-8")
    
                # Infer name from URL if not provided
                if not inferred_name:
                    try:
                        path_no_query = image_url.split("?")[0]
                        filename_from_url = os.path.basename(path_no_query)
                        inferred_name = filename_from_url if filename_from_url else "upload.jpg"
                    except Exception:
                        inferred_name = "upload.jpg"
    
            # Final name resolution
            final_name = name or inferred_name or "upload.png"
    
            # Prepare the API endpoint for uploading images
            endpoint = f"{account_id}/adimages"
    
            # Prepare POST parameters expected by Meta API
            params = {
                "bytes": encoded_image,
                "name": final_name,
            }
    
            # Make API request to upload the image
            print(f"Uploading image to Facebook Ad Account {account_id}")
            data = await make_api_request(endpoint, access_token, params, method="POST")
    
            # Normalize/structure the response for callers (e.g., to easily grab image_hash)
            # Typical Graph API response shape:
            # { "images": { "<hash>": { "hash": "<hash>", "url": "...", "width": ..., "height": ..., "name": "...", "status": 1 } } }
            if isinstance(data, dict) and "images" in data and isinstance(data["images"], dict) and data["images"]:
                images_dict = data["images"]
                images_list = []
                for hash_key, info in images_dict.items():
                    # Some responses may omit the nested hash, so ensure it's present
                    normalized = {
                        "hash": (info.get("hash") or hash_key),
                        "url": info.get("url"),
                        "width": info.get("width"),
                        "height": info.get("height"),
                        "name": info.get("name"),
                    }
                    # Drop null/None values
                    normalized = {k: v for k, v in normalized.items() if v is not None}
                    images_list.append(normalized)
    
                # Sort deterministically by hash
                images_list.sort(key=lambda i: i.get("hash", ""))
                primary_hash = images_list[0].get("hash") if images_list else None
    
                result = {
                    "success": True,
                    "account_id": account_id,
                    "name": final_name,
                    "image_hash": primary_hash,
                    "images_count": len(images_list),
                    "images": images_list
                }
                return json.dumps(result, indent=2)
    
            # If the API returned an error-like structure, surface it consistently
            if isinstance(data, dict) and "error" in data:
                return json.dumps({
                    "error": "Failed to upload image",
                    "details": data.get("error"),
                    "account_id": account_id,
                    "name": final_name
                }, indent=2)
    
            # Fallback: return a wrapped raw response to avoid breaking callers
            return json.dumps({
                "success": True,
                "account_id": account_id,
                "name": final_name,
                "raw_response": data
            }, indent=2)
    
        except Exception as e:
            return json.dumps({
                "error": "Failed to upload image",
                "details": str(e)
            }, indent=2)
  • Function signature and docstring defining input parameters and return type for the upload_ad_image tool.
    async def upload_ad_image(
        account_id: str,
        access_token: Optional[str] = None,
        file: Optional[str] = None,
        image_url: Optional[str] = None,
        name: Optional[str] = None
    ) -> str:
        """
        Upload an image to use in Meta Ads creatives.
        
        Args:
            account_id: Meta Ads account ID (format: act_XXXXXXXXX)
            access_token: Meta API access token (optional - will use cached token if not provided)
            file: Data URL or raw base64 string of the image (e.g., "data:image/png;base64,iVBORw0KG...")
            image_url: Direct URL to an image to fetch and upload
            name: Optional name for the image (default: filename)
        
        Returns:
            JSON response with image details including hash for creative creation
        """
  • Decorators registering the upload_ad_image function as an MCP tool and Meta API tool.
    @mcp_server.tool()
    @meta_api_tool

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