Skip to main content
Glama
Arindam200

Reddit MCP Server

get_submission_by_url

Retrieve structured Reddit post data by providing its URL, including optional comments and metadata for analysis.

Instructions

Get a Reddit submission by its URL.

Args:
    url: The URL of the Reddit submission to retrieve
    include_comments: If True, load and return the full comment forest for the post
    comment_replace_more_limit: Limit for replacing "MoreComments" objects (0 for none, None for all)

Returns:
    Dictionary containing structured submission information with the following structure:
    {
        'id': str,  # Submission ID (e.g., 'abc123')
        'title': str,  # Submission title
        'author': str,  # Author's username or '[deleted]' if deleted
        'subreddit': str,  # Subreddit name
        'score': int,  # Post score (upvotes - downvotes)
        'upvote_ratio': float,  # Ratio of upvotes to total votes
        'num_comments': int,  # Number of comments
        'created_utc': float,  # Post creation timestamp (UTC)
        'url': str,  # Full URL to the post
        'permalink': str,  # Relative URL to the post
        'is_self': bool,  # Whether it's a self (text) post
        'selftext': str,  # Content of self post (if any)
        'selftext_html': Optional[str],  # HTML formatted content
        'link_url': str,  # URL for link posts (if any)
        'domain': str,  # Domain of the linked content
        'over_18': bool,  # Whether marked as NSFW
        'spoiler': bool,  # Whether marked as spoiler
        'stickied': bool,  # Whether stickied in the subreddit
        'locked': bool,  # Whether comments are locked
        'archived': bool,  # Whether the post is archived
        'distinguished': Optional[str],  # Distinguishing type (e.g., 'moderator')
        'flair': Optional[Dict],  # Post flair information if any
        'media': Optional[Dict],  # Media information if any
        'preview': Optional[Dict],  # Preview information if available
        'awards': List[Dict],  # List of awards received
        'comments': Optional[List[Dict]],  # present if include_comments is True
        'metadata': {
            'fetched_at': float,  # Timestamp when data was fetched
            'subreddit_id': str,  # Subreddit full ID
            'author_id': str,  # Author's full ID if available
            'is_original_content': bool,  # Whether marked as OC
            'is_meta': bool,  # Whether marked as meta
            'is_crosspostable': bool,  # Whether can be crossposted
            'is_reddit_media_domain': bool,  # Whether media is hosted on Reddit
            'is_robot_indexable': bool,  # Whether search engines should index
            'is_created_from_ads_ui': bool,  # Whether created via ads UI
            'is_video': bool,  # Whether the post is a video
            'pinned': bool,  # Whether the post is pinned in the subreddit
            'gilded': int,  # Number of times gilded
            'total_awards_received': int,  # Total number of awards received
            'view_count': Optional[int],  # View count if available
            'visited': bool,  # Whether the current user has visited
        }
    }

Raises:
    ValueError: If URL is invalid or submission not found
    RuntimeError: For other errors during the operation

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
urlYes
include_commentsNo
comment_replace_more_limitNo

Implementation Reference

  • The primary handler function decorated with @mcp.tool() that implements the get_submission_by_url tool. It fetches a Reddit submission using PRAW client.submission(url=url), extracts and structures all relevant metadata, optionally loads and serializes the comment tree, and returns a comprehensive dictionary with post details, flair, awards, media, and metadata.
    def get_submission_by_url(url: str, include_comments: bool = False, comment_replace_more_limit: int = 0) -> Dict[str, Any]:
        """Get a Reddit submission by its URL.
    
        Args:
            url: The URL of the Reddit submission to retrieve
            include_comments: If True, load and return the full comment forest for the post
            comment_replace_more_limit: Limit for replacing "MoreComments" objects (0 for none, None for all)
    
        Returns:
            Dictionary containing structured submission information with the following structure:
            {
                'id': str,  # Submission ID (e.g., 'abc123')
                'title': str,  # Submission title
                'author': str,  # Author's username or '[deleted]' if deleted
                'subreddit': str,  # Subreddit name
                'score': int,  # Post score (upvotes - downvotes)
                'upvote_ratio': float,  # Ratio of upvotes to total votes
                'num_comments': int,  # Number of comments
                'created_utc': float,  # Post creation timestamp (UTC)
                'url': str,  # Full URL to the post
                'permalink': str,  # Relative URL to the post
                'is_self': bool,  # Whether it's a self (text) post
                'selftext': str,  # Content of self post (if any)
                'selftext_html': Optional[str],  # HTML formatted content
                'link_url': str,  # URL for link posts (if any)
                'domain': str,  # Domain of the linked content
                'over_18': bool,  # Whether marked as NSFW
                'spoiler': bool,  # Whether marked as spoiler
                'stickied': bool,  # Whether stickied in the subreddit
                'locked': bool,  # Whether comments are locked
                'archived': bool,  # Whether the post is archived
                'distinguished': Optional[str],  # Distinguishing type (e.g., 'moderator')
                'flair': Optional[Dict],  # Post flair information if any
                'media': Optional[Dict],  # Media information if any
                'preview': Optional[Dict],  # Preview information if available
                'awards': List[Dict],  # List of awards received
                'comments': Optional[List[Dict]],  # present if include_comments is True
                'metadata': {
                    'fetched_at': float,  # Timestamp when data was fetched
                    'subreddit_id': str,  # Subreddit full ID
                    'author_id': str,  # Author's full ID if available
                    'is_original_content': bool,  # Whether marked as OC
                    'is_meta': bool,  # Whether marked as meta
                    'is_crosspostable': bool,  # Whether can be crossposted
                    'is_reddit_media_domain': bool,  # Whether media is hosted on Reddit
                    'is_robot_indexable': bool,  # Whether search engines should index
                    'is_created_from_ads_ui': bool,  # Whether created via ads UI
                    'is_video': bool,  # Whether the post is a video
                    'pinned': bool,  # Whether the post is pinned in the subreddit
                    'gilded': int,  # Number of times gilded
                    'total_awards_received': int,  # Total number of awards received
                    'view_count': Optional[int],  # View count if available
                    'visited': bool,  # Whether the current user has visited
                }
            }
    
        Raises:
            ValueError: If URL is invalid or submission not found
            RuntimeError: For other errors during the operation
        """
        manager = RedditClientManager()
        if not manager.client:
            raise RuntimeError("Reddit client not initialized")
    
        if not url or not isinstance(url, str):
            raise ValueError("URL is required")
        if not url.startswith(("http://", "https://")):
            raise ValueError("URL must start with http:// or https://")
    
        try:
            logger.info(f"Getting submission from URL: {url} (include_comments={include_comments})")
    
            # Create submission from URL
            submission = manager.client.submission(url=url)
    
            # Force fetch submission data to verify it exists and get all attributes
            submission.title  # This will raise if submission doesn't exist
    
            # Get basic submission data with error handling
            submission_data = {
                "id": submission.id,
                "title": submission.title,
                "author": str(submission.author)
                if hasattr(submission, "author") and submission.author
                else "[deleted]",
                "subreddit": submission.subreddit.display_name
                if hasattr(submission, "subreddit")
                else "unknown",
                "score": getattr(submission, "score", 0),
                "upvote_ratio": getattr(submission, "upvote_ratio", 0.0),
                "num_comments": getattr(submission, "num_comments", 0),
                "created_utc": submission.created_utc,
                "url": f"https://www.reddit.com{submission.permalink}"
                if hasattr(submission, "permalink")
                else url,
                "permalink": getattr(submission, "permalink", f"/comments/{submission.id}"),
                "is_self": getattr(submission, "is_self", False),
                "selftext": getattr(submission, "selftext", ""),
                "selftext_html": getattr(submission, "selftext_html", None),
                "link_url": getattr(submission, "url", ""),
                "domain": getattr(submission, "domain", ""),
                "over_18": getattr(submission, "over_18", False),
                "spoiler": getattr(submission, "spoiler", False),
                "stickied": getattr(submission, "stickied", False),
                "locked": getattr(submission, "locked", False),
                "archived": getattr(submission, "archived", False),
                "distinguished": getattr(submission, "distinguished", None),
                "flair": None,
                "media": getattr(submission, "media", None),
                "preview": getattr(submission, "preview", None),
                "awards": [],
            }
    
            # Add flair information if available
            if hasattr(submission, "link_flair_text") and submission.link_flair_text:
                submission_data["flair"] = {
                    "text": submission.link_flair_text,
                    "css_class": getattr(submission, "link_flair_css_class", ""),
                    "template_id": getattr(submission, "link_flair_template_id", None),
                    "text_color": getattr(submission, "link_flair_text_color", None),
                    "background_color": getattr(
                        submission, "link_flair_background_color", None
                    ),
                }
    
            # Add awards information if available
            if hasattr(submission, "all_awardings"):
                submission_data["awards"] = [
                    {
                        "id": award.get("id"),
                        "name": award.get("name"),
                        "description": award.get("description"),
                        "coin_price": award.get("coin_price", 0),
                        "coin_reward": award.get("coin_reward", 0),
                        "icon_url": award.get("icon_url"),
                        "count": award.get("count", 1),
                    }
                    for award in submission.all_awardings
                ]
    
            # Add comments if requested
            if include_comments:
                try:
                    # Resolve all MoreComments to get the complete tree
                    submission.comments.replace_more(limit=comment_replace_more_limit)
    
                    top_level_comments = [
                        c
                        for c in submission.comments
                        if isinstance(c, praw.models.Comment)
                    ]
    
                    submission_data["comments"] = [
                        _serialize_comment_tree(c) for c in top_level_comments
                    ]
                except Exception as comments_error:
                    logger.exception(f"Error loading comments for submission {submission.id}")
                    submission_data["comments"] = []
    
            # Add metadata
            submission_data["metadata"] = {
                "fetched_at": time.time(),
                "subreddit_id": getattr(submission.subreddit, "id", "")
                if hasattr(submission, "subreddit")
                else "",
                "author_id": f"t2_{submission.author.id}"
                if hasattr(submission, "author")
                and submission.author
                and hasattr(submission.author, "id")
                else None,
                "is_original_content": getattr(submission, "is_original_content", False),
                "is_meta": getattr(submission, "is_meta", False),
                "is_crosspostable": getattr(submission, "is_crosspostable", False),
                "is_reddit_media_domain": getattr(
                    submission, "is_reddit_media_domain", False
                ),
                "is_robot_indexable": getattr(submission, "is_robot_indexable", True),
                "is_created_from_ads_ui": getattr(
                    submission, "is_created_from_ads_ui", False
                ),
                "is_video": getattr(submission, "is_video", False),
                "pinned": getattr(submission, "pinned", False),
                "gilded": getattr(submission, "gilded", 0),
                "total_awards_received": getattr(submission, "total_awards_received", 0),
                "view_count": getattr(submission, "view_count", None),
                "visited": getattr(submission, "visited", False),
            }
    
            return submission_data
    
        except Exception as e:
            logger.error(f"Error in get_submission_by_url: {e}")
            if "404" in str(e) or "not found" in str(e).lower():
                raise ValueError(f"Submission not found at URL: {url}") from e
            if "403" in str(e) or "forbidden" in str(e).lower():
                raise ValueError(
                    f"Not authorized to access submission at URL: {url}"
                ) from e
            if isinstance(e, (ValueError, RuntimeError)):
                raise
            raise RuntimeError(f"Failed to get submission by URL: {e}") from e
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden and does well by documenting error conditions (ValueError for invalid URLs, RuntimeError for other errors) and the detailed return structure. It explains what data is fetched and when comments are included, though it doesn't mention rate limits, authentication needs, or performance characteristics.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately front-loaded with the core purpose, but the extremely detailed return structure (40+ fields) could be streamlined. While the return documentation is valuable, it occupies significant space that might be better in an output schema. The Args/Returns/Raises structure is clear but verbose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no annotations, 0% schema coverage, and no output schema, the description provides complete context: clear purpose, detailed parameter explanations, comprehensive return structure documentation, and error conditions. For a 3-parameter tool with rich return data, this description leaves minimal gaps for agent understanding.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate fully. It provides excellent parameter semantics: explains what 'url' represents, clarifies that 'include_comments' loads the full comment forest, and defines 'comment_replace_more_limit' with specific values (0 for none, None for all). This adds substantial meaning beyond the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the specific action ('Get a Reddit submission') and resource ('by its URL'), distinguishing it from sibling tools like get_submission_by_id (which uses ID instead of URL) and get_top_posts (which retrieves multiple posts). The verb 'retrieve' is precise and the scope is well-defined.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage context through the parameter explanations (e.g., include_comments for loading comments), but doesn't explicitly state when to use this tool versus alternatives like get_submission_by_id or search_posts. It provides clear parameter guidance but lacks explicit sibling differentiation in the main description text.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/Arindam200/reddit-mcp'

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