Documentation
¶
Overview ¶
Package reddit is a dependency-free (stdlib-only, CGO=0) client for Reddit's public read-only JSON API.
It targets the anonymous endpoints that back every subreddit and post page — appending ".json" to any Reddit URL returns the same listing the site renders. No OAuth, no API key: the only requirement Reddit enforces on anonymous traffic is a descriptive, non-generic User-Agent (a missing or browser-mimicking UA earns a 429). Set one with WithUserAgent.
The client is deliberately small and allocation-light so it can be reused unchanged from a native binary, a wasm proxy, or a Ruby binding (github.com/go-ruby-reddit). It builds for every Go target, cgo disabled.
Index ¶
- Constants
- type APIError
- type Client
- func (c *Client) Comments(ctx context.Context, subreddit, id string, opts ListingOptions) (*PostWithComments, error)
- func (c *Client) Friends(ctx context.Context) ([]Friend, error)
- func (c *Client) Frontpage(ctx context.Context, sort Sort, opts ListingOptions) (*Page, error)
- func (c *Client) MySubreddits(ctx context.Context) ([]SubredditInfo, error)
- func (c *Client) SearchPosts(ctx context.Context, query, subreddit string, sort SearchSort, ...) (*Page, error)
- func (c *Client) SearchSubreddits(ctx context.Context, query string, opts ListingOptions) (*SubredditPage, error)
- func (c *Client) Subreddit(ctx context.Context, name string, sort Sort, opts ListingOptions) (*Page, error)
- func (c *Client) UserPosts(ctx context.Context, name string, sort Sort, opts ListingOptions) (*Page, error)
- type Comment
- type Friend
- type ListingOptions
- type Option
- func WithBaseURL(base string) Option
- func WithHTTPClient(h *http.Client) Option
- func WithOAuth(clientID, clientSecret string) Option
- func WithOAuthScript(clientID, clientSecret, username, password string) Option
- func WithRateLimit(perMinute int) Option
- func WithSessionCookie(cookie string) Option
- func WithUserAgent(ua string) Option
- type Page
- type Post
- type PostWithComments
- type SearchSort
- type Sort
- type SubredditInfo
- type SubredditPage
- type TimeRange
Constants ¶
const DefaultBaseURL = "https://www.reddit.com"
DefaultBaseURL is Reddit's canonical JSON host. The "www" host (rather than "old" or "api") returns the modern listing schema this package decodes.
const DefaultRequestsPerMinute = 60
DefaultRequestsPerMinute is the self-imposed request rate a fresh client uses until Reddit's own X-Ratelimit headers refine it. 60/min (one request per second) sits at Reddit's documented OAuth budget and well under what the anonymous JSON endpoints tolerate, so a first burst does not trip a 429.
const DefaultUserAgent = "go-reddit/reddit (+https://github.com/go-reddit/reddit)"
DefaultUserAgent identifies this library. Reddit rate-limits anonymous requests that omit a UA or impersonate a browser, so callers are strongly encouraged to override this with WithUserAgent using their own contact string (Reddit's guidance: "<platform>:<app-id>:<version> (by /u/<name>)").
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type APIError ¶
APIError is returned when Reddit responds with a non-2xx status. It carries the HTTP status code and a short snippet of the body for diagnosis (Reddit serves an HTML or JSON error page on 403/429/5xx).
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client fetches listings and comments from Reddit's public JSON API. The zero value is not usable; construct one with NewClient. A Client is safe for concurrent use as long as the underlying http.Client is.
func NewClient ¶
NewClient returns a Client with sensible defaults (a 30-second HTTP timeout, the canonical base URL, DefaultUserAgent, and a DefaultRequestsPerMinute rate limiter), then applies each Option in order.
func (*Client) Comments ¶
func (c *Client) Comments(ctx context.Context, subreddit, id string, opts ListingOptions) (*PostWithComments, error)
Comments fetches a post and its comment tree. The comments endpoint returns a two-element array: [ Listing(the post), Listing(the comments) ]. subreddit may be blank (Reddit resolves the post by id alone) but supplying it avoids a redirect. id is the base-36 post id ("abc123"), with or without the "t3_" prefix.
func (*Client) Friends ¶ added in v0.9.0
Friends fetches the redditors the authenticated account follows, via GET /api/v1/me/friends. Reddit returns a single "UserList" thing whose data.children each name one followed account, so a reader can import the people a logged-in account follows the same way it imports its subreddits. Authentication is required — a session cookie (see WithSessionCookie) or OAuth (the "mysubreddits" scope) — and an anonymous client is rejected before any request is made. The list is unpaginated: Reddit returns the account's full friends list in one response.
func (*Client) Frontpage ¶
Frontpage fetches the logged-out front page under the given sort (blank defaults to SortHot). This is r/all-ish aggregate content, not a personalised feed (anonymous access has no account).
func (*Client) MySubreddits ¶ added in v0.7.0
func (c *Client) MySubreddits(ctx context.Context) ([]SubredditInfo, error)
MySubreddits fetches every subreddit the authenticated user is subscribed to, paging through Reddit's /subreddits/mine/subscriber listing until it is exhausted, so a reader can import a logged-in account's subscriptions in one call. Authentication is required — a session cookie (see WithSessionCookie) or OAuth — and an anonymous client is rejected before any request is made. Results come back in Reddit's own order (roughly by subscriber count). At most [mySubredditsMaxPages] pages are fetched.
func (*Client) SearchPosts ¶ added in v0.5.0
func (c *Client) SearchPosts(ctx context.Context, query, subreddit string, sort SearchSort, opts ListingOptions) (*Page, error)
SearchPosts searches Reddit for posts (links and self posts) matching query, returning a page of posts (the same shape a listing returns) so a search can be shown or followed as a feed. When subreddit is non-empty the search is restricted to that subreddit; otherwise it is site-wide. sort selects the order (blank → relevance). A blank query is rejected. Limit (Reddit clamps to 100) and After paginate. Works anonymously or with a session cookie / OAuth.
func (*Client) SearchSubreddits ¶ added in v0.3.0
func (c *Client) SearchSubreddits(ctx context.Context, query string, opts ListingOptions) (*SubredditPage, error)
SearchSubreddits searches Reddit for subreddits whose name, title or description matches query, returning a page of results. Subreddit names cannot be enumerated, so this search endpoint is how a UI discovers them; a caller can further filter the returned names/descriptions locally (e.g. by a regular expression). A blank query is rejected. ListingOptions.Limit (Reddit clamps to 100) and After paginate. Works anonymously or with a configured session cookie / OAuth.
func (*Client) Subreddit ¶
func (c *Client) Subreddit(ctx context.Context, name string, sort Sort, opts ListingOptions) (*Page, error)
Subreddit fetches a page of posts from r/<name> under the given sort. A blank sort defaults to SortHot; a blank name is rejected. The special name "all" and "popular" resolve to Reddit's aggregate feeds.
func (*Client) UserPosts ¶ added in v0.6.0
func (c *Client) UserPosts(ctx context.Context, name string, sort Sort, opts ListingOptions) (*Page, error)
UserPosts fetches a page of a redditor's submissions (their "submitted" posts, not their comments), so a reader can follow a person the way it follows a subreddit. A leading "u/" or "/u/" on name is tolerated. sort is new / hot / top (blank → new, the natural order for a person's posts); Time scopes top. A blank name is rejected. Limit (Reddit clamps to 100) and After paginate. Works anonymously or with a session cookie / OAuth.
type Comment ¶
type Comment struct {
ID string `json:"id"`
Fullname string `json:"name"`
Author string `json:"author"`
Body string `json:"body"`
Score int `json:"score"`
CreatedUTC float64 `json:"created_utc"`
Stickied bool `json:"stickied"`
Replies []Comment `json:"-"`
}
Comment is a single "t1" thing in a comment tree. Replies is populated recursively; leaf comments (or the truncated "load more" frontier) have a nil slice.
type Friend ¶ added in v0.9.0
type Friend struct {
Name string // username (follow as u/<Name>)
ID string // account fullname, e.g. "t2_1w72"
Date float64 // friendship creation time, Unix seconds
RelID string // relationship id
}
Friend is one redditor the authenticated account follows (Reddit's "friends" list). Name is the username — subscribe to a person's submissions as u/<Name>. ID is their "t2_…" fullname, Date the Unix time (seconds) the friendship was created, and RelID the friendship relationship id.
type ListingOptions ¶
type ListingOptions struct {
// Limit caps the number of posts returned (Reddit clamps to 100).
Limit int
// After is the fullname ("t3_xxxxxx") to page from; use [Page.After].
After string
// Time scopes SortTop / SortControvers; ignored otherwise.
Time TimeRange
}
ListingOptions tunes a listing request. The zero value asks Reddit for its default page size starting at the top of the listing.
type Option ¶
type Option func(*Client)
Option customises a Client at construction time.
func WithBaseURL ¶
WithBaseURL overrides the API host. Primarily a test seam (point it at an httptest server); an empty string is ignored. Any trailing slash is trimmed so path joining stays predictable.
func WithHTTPClient ¶
WithHTTPClient supplies the http.Client used for every request. Use it to inject timeouts, proxies, or a test transport. A nil client is ignored.
func WithOAuth ¶
WithOAuth configures application-only ("client_credentials") OAuth using a registered script/web app's client id and secret. This grants read access to public listings without a user account and works from IPs where the anonymous ".json" endpoints are blocked. Empty credentials are ignored (the Client stays anonymous).
func WithOAuthScript ¶
WithOAuthScript configures the resource-owner-password ("script app") grant, authenticating as a specific Reddit user. Use it when you need per-user context; otherwise prefer WithOAuth. Empty credentials are ignored.
func WithRateLimit ¶ added in v0.8.0
WithRateLimit paces the client to at most perMinute requests, so aggregating many subscriptions (or paging an infinite scroll) glides under Reddit's budget instead of tripping 429 "Too Many Requests". The client further adapts to the X-Ratelimit headers Reddit returns and honours a 429's Retry-After. A perMinute <= 0 restores DefaultRequestsPerMinute.
func WithSessionCookie ¶ added in v0.2.0
WithSessionCookie authenticates reads with a logged-in browser's reddit_session cookie instead of OAuth. Reddit's self-serve OAuth registration is effectively closed to new personal projects, so supplying the cookie from an already-signed-in browser is the practical path for individual, read-only use.
The argument may be either the bare reddit_session value or a full "reddit_session=…; other=…" Cookie header string; both normalise to a Cookie header value (a bare value is wrapped as "reddit_session=<value>"; a string that already contains "name=value" pairs is used verbatim). Surrounding whitespace is trimmed and an empty (or whitespace-only) argument is ignored.
Unlike WithOAuth, this keeps the default www base URL — the cookie authenticates the same anonymous ".json" endpoints. When both a session cookie and OAuth are configured, the session cookie takes precedence: the request carries the Cookie header and no Bearer token.
func WithUserAgent ¶
WithUserAgent sets the User-Agent header. An empty string is ignored so the DefaultUserAgent always remains in effect. Reddit blocks generic agents, so production callers should pass a unique, descriptive value.
type Page ¶
Page is a decoded listing: the posts plus the paging cursors Reddit returns. After is the fullname to pass back via ListingOptions.After to fetch the next page ("" when the listing is exhausted).
type Post ¶
type Post struct {
ID string `json:"id"`
Fullname string `json:"name"` // e.g. "t3_abc123", used for paging/After
Title string `json:"title"`
Author string `json:"author"`
Subreddit string `json:"subreddit"`
Permalink string `json:"permalink"`
URL string `json:"url"`
Domain string `json:"domain"`
SelfText string `json:"selftext"`
Thumbnail string `json:"thumbnail"`
Score int `json:"score"`
Ups int `json:"ups"`
NumComments int `json:"num_comments"`
CreatedUTC float64 `json:"created_utc"`
IsSelf bool `json:"is_self"`
Over18 bool `json:"over_18"`
Stickied bool `json:"stickied"`
Flair string `json:"link_flair_text"`
// Media fields. Reddit exposes a post's image(s)/video through several shapes;
// the Images / VideoURL accessors (see media.go) resolve them uniformly.
PostHint string `json:"post_hint"` // "image","hosted:video","rich:video","link",…
IsVideo bool `json:"is_video"`
IsGallery bool `json:"is_gallery"`
Preview postPreview `json:"preview"`
GalleryData galleryData `json:"gallery_data"`
MediaMetadata map[string]mediaMetaItem `json:"media_metadata"`
Media postMedia `json:"media"`
}
Post is a single link or self-post ("t3" thing). Fields mirror the subset of Reddit's JSON that a reader UI needs; unmapped fields are dropped.
func (Post) FullPermalink ¶
FullPermalink joins the site-relative Permalink onto the canonical host so it is directly openable. Reddit's permalinks already begin with "/".
func (Post) HasThumbnail ¶
HasThumbnail reports whether Thumbnail is a real image URL rather than one of Reddit's sentinel strings ("self", "default", "nsfw", "spoiler", "").
func (Post) Images ¶ added in v0.4.0
Images returns the post's display image URLs in display order, best-effort and HTML-unescaped, de-duplicated: a gallery's items first (in order), else the resolved preview source image(s), else a direct-image URL. It is empty when the post carries no image the caller can show. A reddit-hosted video's poster comes through the preview, so a video post returns its poster here too (use Post.VideoURL for the playable stream).
func (Post) PreviewImage ¶ added in v0.4.0
PreviewImage returns the single best display image for the post (the first of Post.Images), or "" when there is none.
type PostWithComments ¶
PostWithComments bundles a resolved post with its (partial) comment tree, as returned by the /comments/<id> endpoint.
type SearchSort ¶ added in v0.5.0
type SearchSort string
SearchSort selects how post-search results are ordered.
const ( // SearchRelevance is Reddit's default search order. SearchRelevance SearchSort = "relevance" // SearchHot / SearchTop / SearchNew / SearchComments mirror the feed sorts. SearchHot SearchSort = "hot" SearchTop SearchSort = "top" SearchNew SearchSort = "new" SearchComments SearchSort = "comments" )
type SubredditInfo ¶ added in v0.3.0
type SubredditInfo struct {
Name string // display_name, e.g. "golang" (subscribe as r/<Name>)
Title string // human title
PublicDescription string // short public blurb
Subscribers int64 // subscriber count
Over18 bool // NSFW flag
URL string // path, e.g. "/r/golang/"
Type string // subreddit_type: public / restricted / private
}
SubredditInfo is a subreddit's metadata as returned by the search and listing endpoints (a Reddit "t5" thing). It carries what a discovery UI needs to show a result and let the user subscribe by Name.
type SubredditPage ¶ added in v0.3.0
type SubredditPage struct {
Subreddits []SubredditInfo
After string
Before string
}
SubredditPage is a page of subreddit search results. After is the fullname to pass back via ListingOptions.After for the next page ("" when exhausted).