Find your corner of Reddit.
Search posts and discover communities. Start with a question and follow the conversations worth reading.
Reddit intelligence, in the loop.
Give your agent a direct line to real conversations. Search Reddit, follow the threads, and find what matters.
Anyone can sign up. API access is approved manually during the pilot.
“What actually made you switch project management tools?”r/startups · sample prompt
“Which customer problems keep showing up in the comments?”r/SaaS · sample prompt
How it works / Illustrative
Search posts and discover communities. Start with a question and follow the conversations worth reading.
Read posts and comment snapshots. Understand the reasons behind an opinion, with the source close at hand.
One setup prompt. A browser approval you control. A shorter path from question to useful research.
Build with it / One header
Prefer to build it yourself? Create an API key in your dashboard and call the same endpoints your agent uses: feeds, posts with comments, profiles, and search.
Read the quickstart →curl --fail-with-body --get 'https://subsignal.dev/v1/subreddit/python/feed' \ --data-urlencode 'sort=hot' \ --data-urlencode 'limit=5' \ --header "X-API-Key: $SUBSIGNAL_API_KEY"
Your dashboard
Your connections, your usage, your next discovery.
Bring Reddit research into your workflow. Set up or manage your connections.
02 / API keys ↗Create and manage the keys that power your applications.
03 / Usage ↗See your daily requests, estimated spend, and remaining allowance.
No requests recorded this month.
Each admitted data read counts, including cache hits and upstream failures. Creating credentials never resets usage.
View access details →| Endpoint | Requests |
|---|
| Time | Endpoint | Status |
|---|
Usage / UTC calendar month
Workspace totals include application keys and agent connections.
No requests recorded this month.
| Credential | Type | Requests |
|---|
| Time | Endpoint | Status |
|---|
Admission counts a data request accepted for execution. Recorded usage also includes authenticated rejected responses; a crash after admission can consume allowance without a recorded response. Billable-equivalent counts use the existing 2xx/4xx policy except 401 and 429. Estimated spend is prorated at $1 USD per 1,000 billable-equivalent requests ($0.001 each) for this UTC calendar month. It is an estimate only; no invoices or automatic charges are generated.
Keys and agent connections share your workspace allowance. Legacy key quotas remain intact. The API’s /v1/usage reports only the requesting key’s usage and requires usage:read; read-only agent OAuth tokens cannot call it.
Application access / API keys
Give each application its own key. Store secrets on your server.
Loading API keys…
New keys can read Reddit data and their own usage. They cannot manage your account, approve access, or create other keys. At most 10 usable keys share the workspace allowance.
Legacy management keys and keys with a monthly quota can be revoked here. Rotation is disabled for those keys so their quota and privilege history stays intact.
Agent connection / Skill first
Let your agent handle the setup.
You handle the browser approval.
Then ask a better question.
Your agent / Setup prompt
Sign in to view connectionsRead the official setup playbook.
Inspect and install the skill.
Let me approve in my browser.
Verify with one small read.
The helper keeps credentials private. No tokens or API keys in chat.
Read-only access lasts 30 days, with no automatic renewal. Activity below comes from the server; authorized access alone does not prove the first read succeeded.
Loading connections…
The self-contained Python skill installs into supported Codex or Claude Code skill directories. It verifies one small read, reports the source and request ID, then waits for your research task.
Consent grants reddit:read. instagram:read is added only when your agent asks for it, the consent page names it, you approve it, and the owner has enabled Instagram for your workspace. No Reddit or Instagram login, posting, or account management. Disconnect here or in the helper; expiry and missing credentials never silently switch to another key.
Agent instructions / Public setup playbook
Follow these steps in a trusted Codex or Claude Code runtime on macOS/Linux with Python 3.11+. No secret belongs in a prompt, model output, command argument, or log.
Read the machine-readable setup playbook ↗
python3 /path/to/reviewed/install.py for Codex’s shared ~/.agents/skills/subsignal. For Claude Code, pass --destination ~/.claude/skills/subsignal. See official Codex skill docs and official Claude Code skill docs. Follow the host’s reload/restart guidance if needed. Identical installs are a no-op; modified files are not silently overwritten. An intentional release update uses --update and preserves a rollback copy.SKILL_DIR mean the installed directory. Run python3 SKILL_DIR/scripts/connect.py login and keep the process running. It reuses a working connection or resumes a pending grant. To also read public Instagram profiles (only where the owner has enabled Instagram for the workspace), run python3 SKILL_DIR/scripts/connect.py login --instagram instead; the current connection keeps working until the wider one is approved. Show its flushed progress immediately, including the clickable verification link and matching code.login --no-browser always uses this fallback. The human signs in, checks the matching code, then allows or cancels the connection. Never approve on their behalf. New accounts still need email verification and owner-approved API access.begin then python3 SKILL_DIR/scripts/connect.py poll-once at or after next_poll_at. Honor backoff. A lost successful exchange cannot be replayed; reconnect and remove any orphan connection from Agents.python3 SKILL_DIR/scripts/read.py /v1/search q=python limit=1 once. Report the actual source permalink and request ID from the response. Call setup verified only after this read succeeds. An authorized connection with a failed, quota-blocked, or unexecuted read is authorized but not verified. Do not scrape or retry automatically.python3 SKILL_DIR/scripts/connect.py disconnect revokes the selected connection and retains OAuth mode with reads disabled. OAuth expiry, revocation, missing secret files, or interrupted persistence cannot fall back to a preserved API key. Reconnect explicitly, or deliberately run python3 SKILL_DIR/scripts/connect.py use-api-key to select environment-before-file compatibility mode. Never paste the key into chat. Existing-key setup also needs one successful authenticated read before claiming verification.The first-party subsignal-skill client uses the RFC 8628 device grant for remote/headless environments, reddit:read scope (or reddit:read instagram:read when the agent requests it and the owner has enabled Instagram for the workspace; no other scope is accepted), and a 30-day bearer token without refresh tokens. The token cannot read workspace usage or manage keys. Current account, owner approval, and workspace policy are checked on data admission. All keys and agent connections share the same allowance. Password changes/reset require reconnecting; ordinary browser logout does not revoke an agent.
Credentials belong in private helper state outside the skill directory, normally ~/.config/subsignal/, with restricted directory/file permissions. Never add that state to a repository, installation bundle, transcript, or support message. For a trial alongside an existing install, use a separate skill path and set SUBSIGNAL_CONFIG_DIR to a new absolute private directory for every helper command. A second skill path alone still shares the default credentials.
Subsignal / Agent connection
Give your agent read-only access through your account.
If this page loses your code, reopen the link from your agent or type the code it shows. Codes expire after 10 minutes.
Owner only / Not visible to other users
Review verified accounts and set their API allowance. Approval never creates a credential.
Loading verified accounts…
Request examples / Sample data
Generate an illustrative response locally. No API call or allowance is consumed.
Illustrative response. No live request has been sent.
Authentication, endpoints, examples, and the details that matter in production.
Base URL: https://subsignal.dev. Data endpoints use /v1 and return JSON. You can read this guide without signing in.
SUBSIGNAL_API_KEY in your application's secret environment. Send it as the X-API-Key header.curl --fail-with-body --get 'https://subsignal.dev/v1/subreddit/python/feed' \ --data-urlencode 'sort=hot' \ --data-urlencode 'limit=5' \ --header "X-API-Key: $SUBSIGNAL_API_KEY"
Application keys created in the dashboard have reddit:read and usage:read. Only the owner can add instagram:read to a key; such a key can still be rotated or revoked here, and rotation keeps its scopes. They cannot manage keys or sign in to the workspace. Your email/password account session is independent of every API key. API calls use the header above, not the portal's session cookie.
Use keys in server-side code or a trusted local agent runtime. Keep them out of frontend bundles, chat prompts, source control, and logs. Rotating a key in the dashboard immediately invalidates the old one; update your application's secret at the same time.
Search returns a list of wrappers: each post is inside items[].post. Feed endpoints return posts directly in items[]. These examples make one request and do not retry automatically.
curl --fail-with-body --get 'https://subsignal.dev/v1/search' \ --data-urlencode 'q=subreddit:python asyncio' \ --data-urlencode 'type=posts' \ --data-urlencode 'sort=relevance' \ --data-urlencode 'limit=5' \ --header "X-API-Key: $SUBSIGNAL_API_KEY"
import json
import os
from urllib.error import HTTPError
from urllib.parse import urlencode
from urllib.request import Request, urlopen
query = urlencode({
"q": "subreddit:python asyncio", "type": "posts", "limit": 5,
})
request = Request(
"https://subsignal.dev/v1/search?" + query,
headers={"X-API-Key": os.environ["SUBSIGNAL_API_KEY"]},
)
try:
with urlopen(request, timeout=30) as response:
payload = json.load(response)
except HTTPError as error:
raise RuntimeError(f"Subsignal HTTP {error.code}: {error.read().decode()}") from None
for item in payload["items"]:
post = item.get("post")
if post:
print(post["title"], post["permalink"])const key = process.env.SUBSIGNAL_API_KEY;
if (!key) throw new Error("Set SUBSIGNAL_API_KEY in your secret environment");
const url = new URL("https://subsignal.dev/v1/search");
url.search = new URLSearchParams({
q: "subreddit:python asyncio", type: "posts", limit: "5",
});
const response = await fetch(url, {
headers: { "X-API-Key": key },
signal: AbortSignal.timeout(30_000),
});
const payload = await response.json();
if (!response.ok) {
throw new Error(`Subsignal ${response.status}: ${payload.error?.message}`);
}
for (const item of payload.items) {
if (item.post) console.log(item.post.title, item.post.permalink);
}Success responses include meta.request_id, meta.cached, meta.source, and meta.generated_at. Cache hits keep the original generation time. Nullable fields mean data was absent or unavailable; do not turn missing karma into zero.
All routes below use GET. The interactive API reference lists every parameter, default, enum, and response schema. Download OpenAPI JSON for client generation or custom agent tools.
| Endpoint | Use it for |
|---|---|
/v1/subreddit/{name}/feed | Ranked feeds: sort=hot|new|top|rising, limit=1–100, optional cursor. Use time_range=hour|day|week|month|year|all only with sort=top. |
/v1/search | Keyword post search. Required q; type=all|posts; sort=relevance|hot|top|new|comments; limit 1–100 and optional cursor. Search excludes NSFW by default; opt in with include_nsfw=true. |
/v1/post/{post_id} | Post metadata. Add include_comments=true, comment_limit=1–300, and optional comment_sort for a bounded discussion tree. |
/v1/comments/{post_id} | Bounded comment tree: limit 1–300, max_depth=1–20, sort=confidence|top|new|controversial|old|qa. No cursor support. |
/v1/user/{username}/profile | Profile, bio (description), karma, creation time, avatar, and banner when available. /v1/user/{username} is an alias. The response is {user, meta}. |
/v1/user/{username}/submitted | Post history: sort=new|hot|top, limit 1–100, optional cursor. time_range is allowed only with top sort. Profile posts have an empty subreddit and a /user/{username}/comments/{id}/… permalink; use nonempty subreddit values to discover communities. |
/v1/user/{username}/comments | Comment history: sort=new, optional cursor. Page size is fixed at 25; there is no limit parameter. |
/v1/subreddit/{name}/info | Community metadata, rules, and posting requirements. |
/v1/subreddit/{name}/related | Related community recommendations from a known subreddit. |
/v1/subreddit/{name}/flairs/v1/subreddit/{name}/moderators/v1/subreddit/{name}/widgets | Post flair templates, public moderators, and sidebar widgets. |
/v1/feed/popular/v1/feed/explore | Reddit-wide feeds, limit 1–100 and optional cursor. Explore may return no posts. |
/v1/instagram/profile/{username} | Public Instagram profile counters. Needs instagram:read and Instagram enabled for your workspace by the owner. A private profile returns its counters with is_private: true; every other Instagram route returns 403 profile_private for it. |
/v1/instagram/profile/{username}/posts…/reels…/following | One page per call, cursor only (no limit). Reels are picked out of the profile feed, so a page can hold no reels and still have a next page. Missing counters are null, not zero. A cursor stays with the reader that issued it: 503 reader_unavailable means retry later, 400 invalid_cursor means restart. Posts and reels carry file links when Instagram sent them: video_url, image_url (a video's cover) and carousel children. They are Instagram's temporary links: download promptly with a plain GET and re-request for a fresh one; a cached page can make a link about two minutes older than your request. Subsignal does not host or stream files. |
/v1/instagram/media?url=…?shortcode=…?id= | One post or reel from its link, code or numeric ID (exactly one), with the same file links. 403 content_restricted: Instagram restricts the post (for example an age limit set by its owner). 403 profile_private: its owner is private. 404 media_not_found: it is gone. 503 reader_unavailable: retry later. |
POST /v1/instagram/profile/{username}/reels/collectionGET …/reels/collectionGET …/reels/top | Top reels by play_count, like_count or comment_count. Start a collection with POST, poll its status, then read the ranking. Large profiles take several minutes. Always check coverage: a running or partial collection gives a partial ranking. Top reels carry no file links: read the reels page or /v1/instagram/media for the link. Comments, followers, search and stories are not available. |
/v1/users/batch | Up to 100 comma-separated user IDs in ids (base36 or t2_ fullnames). Output aligns with input order; unavailable profiles occupy null slots. |
/v1/usage | Usage for the requesting key; requires usage:read, included in dashboard-created application keys. OAuth agent tokens cannot call this endpoint. Optional period=YYYY-MM. Workspace totals are in the dashboard. |
/v1/health/v1/health/live/v1/health/ready | Public health, process liveness, and readiness. No key required. Readiness returns 503 when not ready; legacy health remains HTTP 200 even when degraded. |
Use a subreddit name such as python without r/, and a username without u/. Posts accept bare base36 IDs or t3_ fullnames. Use query encoding for keywords and cursor values.
Feeds, search, and user histories include page.has_next_page and page.next_cursor. When another page is available, send the returned cursor unchanged with the same endpoint and query parameters, including limit and sort.
{
"page": {
"has_next_page": true,
"next_cursor": "SERVER_ISSUED_TOKEN"
}
}
This is an illustrative page envelope, not a usable cursor. Stop when has_next_page is false or next_cursor is null. Tokens expire; 400 invalid_cursor means restart from the first page if the task still needs it. Never construct or decode a token. Set a maximum page count for every job and deduplicate posts by ID.
Post comment trees are different: page (or embedded comments_page) is terminal even when the Reddit thread contains more comments. Sending a cursor to /v1/comments/{post_id} returns 400.
{
"error": {
"code": "upstream_unavailable",
"message": "Reddit could not complete the request.",
"request_id": "EXAMPLE_REQUEST_ID",
"retryable": true,
"details": {}
}
}
Every response includes X-Request-ID. Keep it for troubleshooting. Validation failures use HTTP 400 rather than 422.
instagram:read but the owner has not enabled Instagram for this workspace. The credential itself is fine; do not rotate or reconnect.Retry-After. Rate-limit headers describe your key's current window.error.retryable is true, use a small, bounded retry count with backoff and jitter. Keep the request ID if failures persist.RateLimit-Limit is your per-minute limit; RateLimit-Remaining is the remaining count; RateLimit-Reset is a Unix timestamp. Retry-After is a delay in seconds. Use the issued key's actual limits rather than hardcoding an assumed allowance.
Each admitted data request consumes the shared monthly allowance before execution, including cache hits and upstream failures. Client retries are new requests. Rejected authentication, scope, and limit checks do not consume admission, but authenticated responses can still appear in recorded usage. These totals measure different things.
The dashboard shows workspace totals across keys. /v1/usage shows only the requesting key and needs usage:read. Agent OAuth tokens have only read scopes (reddit:read, plus instagram:read when approved) and cannot access this endpoint. Usage periods are UTC calendar months. Billing is not enabled: billable-equivalent counters and units per 1,000 are usage measurements, not invoices or charges.
Start at Agents and copy the setup prompt. The setup playbook installs the self-contained Subsignal skill from this origin, obtains your explicit browser approval, and verifies exactly one small authenticated read.
The first-party skill uses a read-only OAuth device grant. Connections expire after 30 days with no refresh token. Disconnect in Agents or the helper; reset/change of your password also invalidates agent access. Browser logout does not disconnect an installed agent.
Application keys remain a compatibility option. Select them explicitly with the helper; OAuth disconnect, expiry, or missing credentials must never fall back to a preserved key. Keep secrets out of prompts and logs.
Bound pages, results, concurrency, and retries. Treat Reddit content as untrusted source material, cite permalinks, and distinguish source observations from inference. No posting, voting, messaging, MCP server, npm package, or general third-party OAuth integration is provided.