---
name: reddit-automations
description: How to automate Reddit operations using Chrome tools: scraping posts, extracting data with CSS selectors, and building Reddit automation workflows.
---

# Reddit Automations

A collection of step-by-step guides for automating Reddit operations using Chrome browser tools (`page_snapshot`, `inspect_element`, `extract_list`, `list_focusable`, `input`, `click`).

---

## 1. Get a List of Posts from the Reddit Homepage

### Overview
Scrape all posts from the Reddit homepage including title, URL, upvote count, and post timestamp.

### Steps

1. **Navigate to Reddit**
   ```
   navigate(url="https://www.reddit.com")
   ```

2. **Take a page snapshot** (optional, to verify the page loaded and inspect refs)
   ```
   page_snapshot()
   ```

2.5. Alternative way is to use `list_focusable` to know what is on the page. 

3. **Inspect a single post element** to understand structure (do this once; skip if reusing known selectors)
   ```
   inspect_element(ref="<article_ref_from_snapshot>")
   ```
   Reddit uses custom web components: `shreddit-post` holds post metadata as attributes, `faceplate-timeago` holds timestamps.

4. **Extract the full post list** using `extract_list`:
   ```
   extract_list(
     container="article",
     fields=[
       "title=a[slot='title']",
       "url=a[slot='full-post-link']@href",
       "upvotes=shreddit-post@score",
       "time_posted=faceplate-timeago@ts"
     ]
   )
   ```

### CSS Selectors Reference

| Field        | Selector                          | Notes                                      |
|--------------|-----------------------------------|--------------------------------------------|
| `title`      | `a[slot='title']`                 | Text content of the post title link        |
| `url`        | `a[slot='full-post-link']@href`   | Full absolute URL to the post              |
| `upvotes`    | `shreddit-post@score`             | Raw integer score from custom element attr |
| `time_posted`| `faceplate-timeago@ts`            | ISO 8601 timestamp string (UTC)            |

### Notes
- Reddit uses **custom web components** (`shreddit-post`, `faceplate-timeago`) — standard selectors like `.score` won't work.
- The `container="article"` selector returns ~48 items; ~13 are ad/promo placeholders with empty fields — filter those out.
- The `@score` attribute is a raw number (e.g. `948`). Posts with 0 votes also return `0`.
- The `@ts` timestamp is ISO format, e.g. `2026-06-26T12:37:00+00:00`.
- **If the extracted list is empty or returns no valid posts**, the page may not be in compact view. Re-navigate with `?feedViewType=compactView` appended: `https://www.reddit.com/?feedViewType=compactView` and retry the extraction.

---

## 2. Fill a Post Submission Form on a Subreddit

### Overview
Navigate to a subreddit's submit page, fill in the title, body text, and select a flair. Do NOT submit the post unless explicitly instructed.

### Step-by-Step

#### 2a. Open the Submit Page

```
navigate(url="https://www.reddit.com/r/<subreddit>/submit/")
```

Wait for the page to load, then use `list_focusable` to confirm the form is present. You should see ~26 focusable elements including the key form controls.

#### 2b. Fill the Title

The title field is a custom element `faceplate-textarea-input`. Use its selector directly:

```
input(
  selector="faceplate-textarea-input[name=\"title\"] >> #innerTextArea",
  text="Your post title here"
)
```

**Key detail:** Always use this exact selector — `faceplate-textarea-input[name="title"] >> #innerTextArea`. The `list_focusable` output will show it as element #16 (textbox "*").

#### 2c. Fill the Body (with Paragraphs)

The body is a `contenteditable` rich text editor inside `shreddit-composer`. Use the `div[name="body"]` selector and the `input` tool:

```
input(
  selector="div[name=\"body\"]",
  text="First paragraph.\n\nSecond paragraph.\n\nThird paragraph."
)
```

**IMPORTANT — Paragraph formatting:**
- Use `\n\n` (double newline / empty line) between paragraphs.
- The text is injected into a `contenteditable` div with Lexical editor, and `\n\n` gets converted to proper `<p>` tags — each paragraph becomes a separate `<p>` block.
- Do **NOT** click the Markdown toggle button (inside `#post-composer_bodytext`) — this causes the page to lose the form content and go blank. The button's `aria-label` is localized (e.g. "Перемкнутися на Markdown" in Ukrainian, "Switch to Markdown" in English), so never target it by label.
- Do **NOT** use Markdown formatting characters (`**bold**`, `# Heading`, etc.) — input **plain text only** with blank lines for paragraph separation.
- The `input` tool fills the contenteditable div correctly when using plain text with `\n\n` paragraph breaks. Verified working: empty lines between paragraphs result in separate `<p>` blocks with `<br>` elements.

#### 2d. Select a Flair

1. Click the flair button:
   ```
   click(selector="#post-flair-modal >> #reddit-post-flair-button")
   ```

2. After the dialog opens, use `list_focusable` to see available flairs and controls. The dialog will show focusable elements like:
   - `#post-flair-modal >> #flair-search` — search textbox
   - `#post-flair-modal >> #post-flair-radio-input-no-flair` — no flair
   - `#post-flair-modal >> #post-flair-radio-input-0` — first flair
   - `#post-flair-modal >> #post-flair-radio-input-1` — second flair
   - `#post-flair-modal >> #post-flair-radio-input-2` — third flair, etc.
   - `#post-flair-modal >> #post-flair-modal-cancel-button`
   - `#post-flair-modal >> #post-flair-modal-apply-button`

3. Click the desired flair radio, e.g. for "Discussion" (index 2):
   ```
   click(selector="#post-flair-modal >> #post-flair-radio-input-2")
   ```

4. Click Apply to close the dialog:
   ```
   click(selector="#post-flair-modal >> #post-flair-modal-apply-button")
   ```

#### 2e. Submit Button

The submit button lives inside the custom element `r-post-form-submit-button`. Use the shadow-piercing selector from `list_focusable`:

```
click(selector="#submit-post-button >> #inner-post-submit-button")
```

Verified 2026-06-30. The outer ID `#submit-post-button` combined with `>> #inner-post-submit-button` to pierce the shadow DOM reaches the clickable inner button reliably. The button sits below the viewport; the `click` tool handles scrolling automatically.

#### 2f. Detect Whether the Subreddit Allows Link Posts

Not all subreddits allow link posts. Some are text-only and reject `type=LINK`. Reddit's behavior makes this easy to detect.

**Reddit's behavior:** Navigating to `/r/{subreddit}/submit/?type=LINK` on a text-only subreddit triggers a server-side redirect to `type=TEXT`. On a link-allowed subreddit, the URL stays as `type=LINK`.

**Detection methods (prefer A — one navigation tells you everything):**

**Method A — URL after navigation (simplest, one step):**
```
navigate(url="https://www.reddit.com/r/<subreddit>/submit/?type=LINK")
```
→ Check the final URL:
- Contains `type=TEXT` → link posts NOT allowed (redirected)
- Contains `type=LINK` → link posts allowed

**Method B — Check for URL input field (most direct, post-navigation):**
```
list_focusable()
```
Look for `faceplate-textarea-input[name="link"]` in the output. If present, link posting is allowed.

**Method C — Check the post-type tabs:**
In the tab bar (`r-post-type-select[name="type"]`), the Link tab occupies `nth-of-type(3)` when present. In text-only subreddits, it's absent entirely (gaps in the nth-of-type numbering).

**Link form fields (when allowed):**
| Field | Selector |
|-------|----------|
| Title | `faceplate-textarea-input[name="title"] >> #innerTextArea` |
| URL | `faceplate-textarea-input[name="link"] >> #innerTextArea` |
| Body (optional) | `div[aria-label*="основного тексту"]` or `div[name="body"]` |

**Tested subreddits:**
| Subreddit | Link posts | Behavior |
|-----------|------------|----------|
| r/AI_Agents | ❌ No | `type=LINK` → redirects to `type=TEXT`; Link tab missing |
| r/modelcontextprotocol | ✅ Yes | `type=LINK` stays; Link tab present (nth-of-type(3)) |

---

### Form Elements Reference (from list_focusable)

| # | Role | Label | Selector |
|---|------|-------|----------|
| 16 | textbox | Title (*) | `faceplate-textarea-input[name="title"] >> #innerTextArea` |
| 17 | button | Add flair & tags (*) | `#post-flair-modal >> #reddit-post-flair-button` |
| 18 | textbox | Body text | `div[name="body"]` |
| — | button | Toggle Markdown | (avoid — breaks the form) |
| 22 | button | Submit | `#submit-post-button >> #inner-post-submit-button` |

### Known Gotchas

- **Selectors are language-independent; labels are not** — Reddit UI labels, button text, and `aria-label` values are localized (e.g. "Публікувати" vs "Post", "Перемкнутися на Markdown" vs "Switch to Markdown"). Always use the selector from `list_focusable` — these are structural (based on IDs, `name` attributes, shadow-piercing paths) and work regardless of locale. Never build a selector that includes a localized label, title, or `aria-label` value.
- **Do NOT click the Markdown toggle button** — it breaks the form; the page goes blank and `list_focusable` drops to 2 elements. Stay in rich text mode. The button is inside `#post-composer_bodytext` but its `aria-label` is localized, so never target it.
- **Title selector must include `>> #innerTextArea`** — the `faceplate-textarea-input` alone won't work for input.
- **Body uses `div[name="body"]` selector** — not a textarea. Plain text with `\n\n` works correctly.
- **Flair dialog is a modal** — elements are scoped under `#post-flair-modal >>`. Always prepend that prefix.
- **After submitting the page may stay on submit form** — Reddit can redirect to the post or show an error banner. Check `page_snapshot` after submit to confirm outcome.

---

## Fallback Strategy: When Something Goes Wrong

Reddit's UI is complex (custom web components, dynamic rendering, focus traps). If a `click`, `input`, or navigation doesn't work as expected:

### 1. Call `list_focusable` First

```
list_focusable()
```

This is the **preferred diagnostic tool** — it shows exactly which elements are interactive on the current page, their labels, and CSS selectors. Use it to:
- Confirm the form/page loaded correctly (26 elements = submit form OK; 2 elements = broken/blank page).
- Find the correct selector for an element that moved or changed.
- Verify a dialog opened (flair modal shows 12 elements with `#post-flair-modal >>` prefix).
- Discover new interactive elements you didn't know about.

### 2. Then Decide

Based on `list_focusable` output:
- **Page blank / few elements?** → `navigate` back to the submit URL fresh.
- **Element not found with known selector?** → use the selector from `list_focusable` output instead.
- **Dialog didn't open?** → the click target may have changed; inspect the button selector in `list_focusable` and try again.
- **Element present but click fails?** → try `page_snapshot` for a ref-based click, or `inspect_element` to check if the element is disabled/hidden.

---

## Future Sections (planned)
- Get posts from a specific subreddit
- Extract post comments
- Monitor a subreddit for new posts (event-based automation)
- Filter posts by keyword or upvote threshold
- Submit post and verify result