get_analytics
Retrieve social media analytics data for a specific Metricool brand account. Specify the brand ID, date range, timezone, social network, and metrics to generate insights.
Instructions
Retrieve analytics data for a specific Metricool brand. If the user does not specify any metric you can use the get_metrics tool and let the user decide them.
Args: - blog_id (int): ID of the Metricool brand account. Required. - start (str): Start date of the data period (format: YYYY-MM-DD). Required. - end (str): End date of the data period (format: YYYY-MM-DD). Required. - timezone (str): Timezone from the brand(e.g., Europe%2FMadrid). Required. If you don't have the timezone you can obtain it from the get_brands tool - network (str): Social network to analyze (e.g., facebook, instagram, linkedin, youtube, tiktok, etc.), it must be connected to the brand. Required. - metric ([str]): List of metrics, default is empty. If blog_id is missing, ask the user to provide it. If network is missing, ask the user to specify one. If network is not connected to the brand, ask the user to specify one of the connected ones.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| blog_id | Yes | ||
| end | Yes | ||
| metric | Yes | ||
| network | Yes | ||
| start | Yes | ||
| timezone | Yes |
Implementation Reference
- src/mcp_metricool/tools/tools.py:707-815 (handler)The core handler function for the 'get_analytics' tool. Decorated with @mcp.tool() for automatic registration in the MCP server. Implements the logic to fetch analytics metrics from Metricool API for specified network, period, and metrics using dynamic URL construction based on network_subject_metrics.@mcp.tool() async def get_analytics(blog_id: int, start: str, end: str, timezone: str, network: str, metric: [str]) -> str | dict[str, Any]: """ Retrieve analytics data for a specific Metricool brand. If the user does not specify any metric you can use the get_metrics tool and let the user decide them. Args: - blog_id (int): ID of the Metricool brand account. Required. - start (str): Start date of the data period (format: YYYY-MM-DD). Required. - end (str): End date of the data period (format: YYYY-MM-DD). Required. - timezone (str): Timezone from the brand(e.g., Europe%2FMadrid). Required. If you don't have the timezone you can obtain it from the get_brands tool - network (str): Social network to analyze (e.g., facebook, instagram, linkedin, youtube, tiktok, etc.), it must be connected to the brand. Required. - metric ([str]): List of metrics, default is empty. If blog_id is missing, ask the user to provide it. If network is missing, ask the user to specify one. If network is not connected to the brand, ask the user to specify one of the connected ones. """ if network not in network_subject_metrics: return f"Incorrect network '{network}'. The available networks are: {', '.join(network_subject_metrics.keys())}" if not metric: return "Please provide a list of metrics. You can use the 'get_metrics' tool to explore available metrics." results = {} subjects = list(network_subject_metrics[network].keys()) start_formatted = format_datetime_with_timezone(start, "00:00:00", timezone) end_formatted = format_datetime_with_timezone(end, "23:59:59", timezone) start_aux = start.replace("-", "") end_aux = end.replace("-", "") for subj in subjects: metrics = metric if metric else network_subject_metrics[network][subj] for met in metrics: if met not in network_subject_metrics[network][subj]: continue if network == 'tiktok' and subj == 'videos': url = ( f"{METRICOOL_BASE_URL}/v2/analytics/timelines" f"?blogId={blog_id}&userId={METRICOOL_USER_ID}&integrationSource=MCP" f"&from={start_formatted}&to={end_formatted}" f"&timezone={timezone}&metric={met}&network={network}" ) elif network == 'youtube' and subj == 'videos': url = ( f"{METRICOOL_BASE_URL}/v2/analytics/timelines" f"?blogId={blog_id}&userId={METRICOOL_USER_ID}&integrationSource=MCP" f"&from={start_formatted}&to={end_formatted}" f"&timezone={timezone}&metric={met}&network={network}&postsType=publishedInRange" ) elif network == 'youtube' and subj == "account": url = ( f"https://app.metricool.com/api/stats/timeline/{met}?start={start_aux}&end={end_aux}&timezone={timezone}" f"&userId={METRICOOL_USER_ID}&blogId={blog_id}&integrationSource=MCP" ) elif network == "linkedin" and subj != "stories": url = ( f"{METRICOOL_BASE_URL}/v2/analytics/timelines" f"?blogId={blog_id}&userId={METRICOOL_USER_ID}&integrationSource=MCP" f"&from={start_formatted}&to={end_formatted}" f"&timezone={timezone}&metric={met}&metricType={subj}&network={network}" ) elif network == "linkedin" and subj == "stories": url = ( f"https://app.metricool.com/api/stats/timeline/{met}" f"?start={start_aux}&end={end_aux}&userId={METRICOOL_USER_ID}&blogId={blog_id}&integrationSource=MCP" ) elif network == "webpage": url = ( f"https://app.metricool.com/api/stats/timeline/{met}" f"?start={start_aux}&end={end_aux}&userId={METRICOOL_USER_ID}&blogId={blog_id}&timezone={timezone}&integrationSource=MCP" ) elif network == "twitter": url = ( f"https://app.metricool.com/api/stats/timeline/{met}" f"?start={start_aux}&end={end_aux}&userId={METRICOOL_USER_ID}&blogId={blog_id}&timezone={timezone}&integrationSource=MCP" ) elif network == "twitch": url = ( f"https://app.metricool.com/api/stats/timeline/twitch{met}" f"?start={start_aux}&end={end_aux}&userId={METRICOOL_USER_ID}&blogId={blog_id}&timezone={timezone}&integrationSource=MCP" ) else: url = ( f"{METRICOOL_BASE_URL}/v2/analytics/timelines" f"?blogId={blog_id}&userId={METRICOOL_USER_ID}&integrationSource=MCP" f"&from={start_formatted}&to={end_formatted}" f"&timezone={timezone}&metric={met}&subject={subj}&network={network}" ) try: result = await make_get_request(url) if result: results[f"{subj}:{met}"] = result else: results[f"{subj}:{met}"] = "No data" except Exception as e: results[f"{subj}:{met}"] = f"Error: {str(e)}" for key, value in results.items(): if isinstance(value, dict) and "data" in value: for item in value["data"]: if isinstance(item, dict) and "values" in item: for v in item["values"]: if "dateTime" in v: v["dateTime"] = convert_datetime_to_timezone(v["dateTime"], unquote(timezone)) return results if results else "No valid data."
- Defines the schema of available metrics organized by network and subject (e.g., posts, account, reels). Used in get_analytics to validate input metrics, iterate over subjects, and construct API endpoints.network_subject_metrics = { "tiktok": { "videos": [ "videos", "views", "comments", "shares", "interactions", "likes", "reach", "engagement", "impressionSources", "averageVideoViews" ], "account": [ "video_views", "profile_views", "followers_count", "followers_delta_count", "likes", "comments", "shares" ] }, "pinterest": { "pins": [ "impression", "save", "pin_click", "outbound_click", "video_mrc_view", "video_avg_watch_time", "video_v50_watch_time", "quartile_95_percent_view", "pins" ], "account": [ "followers", "following", "delta followers", "IMPRESSION", "ENGAGEMENT_RATE", "ENGAGEMENT", "PIN_CLICK", "OUTBOUND_CLICK", "SAVE" ], "posts": [ "PINS" ] }, "youtube": { "videos": [ "views", "interactions", "likes", "dislikes", "comments", "shares" ], "account": [ "yttotalSubscribers", "ytestimatedRevenue", "ytVideos", "ytsubscribersGained", "ytsubscribersLost" ] }, "facebook": { "stories": [ "storiesCount" ], "posts": [ "count", "interactions", "engagement", "impressionsunique", "impressions", "clicks", "comments", "shares", "reactions" ], "reels": [ "blue_reels_play_count", "post_impressions_unique", "post_video_likes_by_reaction_type", "post_video_social_actions", "engagement", "count" ], "account": [ "page_posts_impressions", "page_actions_post_reactions_total", "postsCount", "postsInteractions", "likes", "pageFollows", "pageImpressions", "pageImpressions.M", "pageImpressions.F", "pageImpressions.U", "pageImpressions.13-17", "pageImpressions.18-24", "pageImpressions.25-34", "pageImpressions.35-44", "pageImpressions.45-54", "pageImpressions.55-64", "pageImpressions.65+", "pageViews" ] }, "gmb": { "business": [ "business_impressions_maps", "business_impressions_search", "business_impressions_total", "business_direction_requests", "call_clicks", "website_clicks", "clicks_total", "business_conversations", "business_bookings", "business_food_orders", "business_actions_total" ] }, "instagram": { "account": [ "email_contacts", "get_directions_clicks", "phone_call_clicks", "text_message_clicks", "clicks_total", "postsCount", "postsInteractions", "followers", "friends" ], "posts": [ "count", "interactions", "engagement", "reach", "impressions", "likes", "comments", "saves", "shares" ], "reels": [ "count", "comments", "likes", "saved", "shares", "engagement", "impressions", "reach", "interactions", "videoviews" ] }, "linkedin": { "account": [ "followers", "paidFollowers", "companyImpressions", "deltaFollowers" ], "posts": [ "posts", "clicks", "likes", "comments", "shares", "engagement", "impressions", "interactions" ], "stories": [ "inStoriesEngagement", "inStoriesInteractions", "inStoriesImpressions", "inStoriesCliks", "inStories" ] }, "threads": { "posts": [ "count", "views", "likes", "replies", "reposts", "engagement", "quotes", "interactions" ], "account": [ "followers_count", "delta_followers" ] }, "bluesky": { "posts": [ "posts_count", "interactions", "likes", "replies", "reposts", "quotes" ], "account": [ "followers_count", "follows_count", "count", "follow_event", "unfollow_event" ] }, "webpage": { "account": [ "PageViews", "SessionsCount", "Visitors", "DailyPosts", "DailyComments" ] }, "twitter": { "account": [ "twitterFollowers", "twFriends", "twTweets", "follows", "unfollows", "twEngagement", "twImpressions", "twInteractions", "twFavorites", "twRetweets", "twReplies", "twQuotes", "twProfileClicks", "twLinkClicks" ] }, "twitch": { "account": [ "TotalFollowers", "TotalSubscribers", "TotalVideos", "DeltaFollowers", "TotalTier1", "TotalTier2", "TotalTier3", "TotalGifts", "TotalViews", "TotalDuration" ] } }
- Utility functions for formatting start/end datetimes with timezone and converting response datetimes to user timezone. Directly called within get_analytics handler.def format_datetime_with_timezone(date_str: str, hour: str, timezone_str: str) -> str: tz_clean = unquote(timezone_str) tz = timezone(tz_clean) dt = datetime.strptime(f"{date_str}T{hour}", "%Y-%m-%dT%H:%M:%S") final_dt = tz.localize(dt) return quote(final_dt.isoformat()) def convert_datetime_to_timezone(dt_str: str, target_tz_str: str) -> str: try: dt = datetime.strptime(dt_str, "%Y-%m-%dT%H:%M:%S%z") target_tz = timezone(target_tz_str) dt_converted = dt.astimezone(target_tz) return dt_converted.isoformat() except Exception as e: return dt_str
- Supporting tool 'get_metrics' that exposes the available metrics from network_subject_metrics for a given network, with instructions to use before calling get_analytics.@mcp.tool() async def get_metrics(network: str) -> str | dict[str, Any]: """ Retrieve the available metrics for a specific network. Args: network: Specific network to get the available metrics. """ if network not in network_subject_metrics: return f"Incorrect network '{network}'. The available networks are: {', '.join(network_subject_metrics.keys())}" else: metrics = {} subjects = list(network_subject_metrics[network].keys()) for subj in subjects: metrics[subj] = network_subject_metrics[network][subj] return {"metrics": metrics, "instructions": "Stop the chat, show the metrics and let the user choose the metrics they want to analyze before going again to get_analytics, the user must choose before you continue."}
- API request helper used extensively in get_analytics to fetch data from Metricool endpoints.async def make_get_request(url: str) -> dict[str, Any] | None: """Make a get request to the Metricool API with proper error handling.""" headers = { "X-Mc-Auth": METRICOOL_USER_TOKEN, } async with httpx.AsyncClient() as client: try: response = await client.get(url, headers=headers, timeout=30.0) response.raise_for_status() return response.json() except Exception: return None