---
name: reddit
description: "Read subreddit listings, run searches or fetch one post with its comments: title, author, text/body, link, created date and permalink. No login, no browser. Use when the user wants posts or comments from Reddit: the latest or top posts of a subreddit, a Reddit search for a topic or brand, or one post with its comments. Calls the Pullwire API with the key in PULLWIRE_API_KEY; costs 1 credit per post or comment."
metadata:
  openclaw:
    requires:
      env:
        - PULLWIRE_API_KEY
      bins:
        - curl
    primaryEnv: PULLWIRE_API_KEY
    homepage: https://pullwire.dev/tools/reddit
---

# Reddit Posts, Comments & Search

Read subreddit listings, run searches or fetch one post with its comments: title, author, text/body, link, created date and permalink. No login, no browser. The data comes from the Pullwire API: you send the input as JSON and get structured JSON back. Only public data.

## Setup (once)

The API key must be in the environment variable `PULLWIRE_API_KEY`. Keys start with `af_live_`. Check it without printing it:

```bash
test -n "$PULLWIRE_API_KEY" && echo "key is set" || echo "PULLWIRE_API_KEY is missing"
```

If it is missing, stop and tell the user: sign up at https://pullwire.dev/login (new accounts get 2,000 free credits), create a key at https://pullwire.dev/app/keys, then run `export PULLWIRE_API_KEY="af_live_..."` in the shell the agent uses. Never print the key, write it into a file or repeat it in the chat.

## Run it

```bash
curl -sS -X POST "https://pullwire.dev/api/v1/tools/reddit/runs" \
  -H "x-api-key: $PULLWIRE_API_KEY" \
  -H "content-type: application/json" \
  -d '{"requests":[{"kind":"subreddit","subreddit":"programming","sort":"hot","limit":10}],"maxResults":3}'
```

The request waits up to 25 seconds and answers with `{"run": {...}, "results": [...]}`. Check `run.status` (`succeeded` or `failed`) and `run.error`: when it is set, the run stopped early and `results` is partial (everything in it was charged). `run.creditsUsed` is what the run cost. For big answers add `-o result.json` and read only the fields you need.

Good to know:

- Each entry in `requests` has a `kind`: `subreddit` needs `subreddit`, `search` needs `query` (add `subreddit` to search inside one), `post` needs `postUrl`. Put them all into one run.
- Results carry no score, upvote count, comment count or flair: do not rank or describe posts by popularity.
- Comments for `subreddit` and `search` results only come with `includeComments: true` (one extra request per post); `post` always includes them.
- A request with no matching posts returns nothing and costs nothing.
- This site blocks cloud servers, so runs go through residential network access and are switched on only while our automatic checks pass. When a run answers `503 TOOL_UNAVAILABLE`, tell the user this source is paused for now (nothing was charged) and check https://pullwire.dev/status.

## Input

The JSON body is the tool input. Fields:

| Field | Type | Required | Default | What it does |
| --- | --- | --- | --- | --- |
| `requests` | object[] | yes |  | Requests: One entry per subreddit listing, search or single post. |
| `requests[].kind` | "subreddit" \| "search" \| "post" | yes |  | Kind: subreddit: list a subreddit’s posts. search: reddit-wide or subreddit-scoped post search. post: one post plus its comments. |
| `requests[].subreddit` | string | no |  | Subreddit: Name without r/, e.g. "programming". Required for kind=subreddit; restricts kind=search to this subreddit when set. |
| `requests[].query` | string | no |  | Search query: Required for kind=search. |
| `requests[].postUrl` | string | no |  | Post URL or id: Required for kind=post: a reddit.com post URL (e.g. https://www.reddit.com/r/programming/comments/abc123/title/), a redd.it shortlink, a fullname (t3_abc123) or a bare post id. |
| `requests[].sort` | "hot" \| "new" \| "top" \| "rising" \| "controversial" \| "relevance" \| "comments" | no |  | Sort: kind=subreddit: hot/new/top/rising/controversial (default hot). kind=search: relevance/hot/top/new/comments (default relevance). Ignored for kind=post. |
| `requests[].time` | "hour" \| "day" \| "week" \| "month" \| "year" \| "all" | no | `"all"` | Time window: Used only with sort=top/controversial (subreddit) and for every search. |
| `requests[].limit` | integer | no | `25` | Max posts for this request: kind=subreddit/search only. Reddit returns at most 100 per page; more are fetched by following its pagination cursor up to this count. |
| `requests[].commentSort` | "confidence" \| "top" \| "new" \| "controversial" \| "old" \| "qa" | no | `"top"` | Comment sort |
| `requests[].maxComments` | integer | no | `20` | Max comments: kind=post: comments to return (all reply levels in thread order, as one page of reddit’s comment feed returns them; collapsed "load more" threads are not followed). kind=subreddit/search: used only when the run-level "Include comments" flag is on. |
| `includeComments` | boolean | no | `false` | Include comments for subreddit/search posts: Fetch each found post’s top comments too (one extra request per post, capped by that request’s "Max comments"). kind=post always includes comments regardless of this flag. |
| `maxResults` | integer | no |  | Max results: Stop after this many posts+comments in total across all requests. Empty: no extra cap. |

Without `maxResults` a run returns at most 20 results; the maximum is 200 per run. Credits for `maxResults` results are reserved before the run starts and the unused part is given back, so ask for what you need.

## Output

Each item in `results` has these fields:

| Field | Type | Always there |
| --- | --- | --- |
| `type` | "post" \| "comment" | yes |
| `id` | string | yes |
| `fullname` | string | yes |
| `subreddit` | string | yes |
| `permalink` | string | yes |
| `author` | string | no |
| `createdUtc` | number | no |
| `title` | string | no |
| `selftext` | string | no |
| `selftextHtml` | string | no |
| `url` | string | no |
| `isSelf` | boolean | no |
| `domain` | string | no |
| `thumbnail` | string | no |
| `postId` | string | no |
| `body` | string | no |
| `bodyHtml` | string | no |
| `isSubmitter` | boolean | no |
| `searchQuery` | string | no |
| `scrapedAt` | string | yes |

## Cost

1 credit per post or comment. One post or comment returned. Requests with zero matches are not charged. Check the balance with:

```bash
curl -sS "https://pullwire.dev/api/v1/me" -H "x-api-key: $PULLWIRE_API_KEY"
```

The answer has `balance` in credits. Tell the user what a run cost when it was more than a few credits.

## Bigger jobs

Add `?async=1` to the run URL to start a background run (up to 50 seconds). It answers `202` with `run.id` right away. Poll every few seconds until `status` is `succeeded` or `failed`, then fetch the results:

```bash
curl -sS "https://pullwire.dev/api/v1/runs/$RUN_ID" -H "x-api-key: $PULLWIRE_API_KEY"
curl -sS "https://pullwire.dev/api/v1/runs/$RUN_ID/results" -H "x-api-key: $PULLWIRE_API_KEY"
```

Add `?format=csv` to the results URL when the user wants a spreadsheet.

## Errors

Errors answer `{"error": {"code": "...", "message": "..."}}`.

| Status | Code | What to do |
| --- | --- | --- |
| 400 | `INVALID_INPUT` | The message names the field. Fix the input and retry; nothing was charged. |
| 401 | `UNAUTHORIZED` | The key is missing, wrong or revoked. Ask the user to set a valid key in `PULLWIRE_API_KEY`. |
| 402 | `INSUFFICIENT_CREDITS` | The balance cannot cover the reservation. Lower `maxResults` or tell the user the balance is too low. |
| 404 | `NOT_FOUND` | Unknown tool or run ID. |
| 429 | `RATE_LIMITED` | Used up its burst of 120 requests and its 60 per minute. Wait, then retry. |
| 500 | `INTERNAL_ERROR` | Retry later. If the body has `runId`, fetch the partial results with `GET /runs/{runId}/results`. |
| 503 | `TOOL_UNAVAILABLE` | Our latest automatic checks against this site failed, so its runs are paused. Nothing was reserved or charged. Tell the user and try again later. |
| 503 | `SERVICE_UNAVAILABLE` | Retry later. |

Docs: https://pullwire.dev/docs · Tool page: https://pullwire.dev/tools/reddit
