NAME
Net::Async::WebSearch::Provider::Reddit::OAuth - Reddit search provider using the OAuth2 endpoint
VERSION
version 0.002
SYNOPSIS
# App-only OAuth (read-only, no user account involved) — simplest path:
my $r = Net::Async::WebSearch::Provider::Reddit::OAuth->new(
client_id => $ENV{REDDIT_CLIENT_ID},
client_secret => $ENV{REDDIT_CLIENT_SECRET},
user_agent => 'my-search-bot/1.0 by /u/myredditname',
);
# Authorization-code flow — drive the consent dance from a host app
# (MCP server, web app, CLI…) where a human can visit a URL.
my $r = Net::Async::WebSearch::Provider::Reddit::OAuth->new(
client_id => ...,
client_secret => ...,
grant_type => 'authorization_code',
user_agent => 'my-app/1.0',
on_token_refresh => sub {
my ( $provider, $raw ) = @_;
# persist $provider->token_state to your session store
},
);
$loop->add($ws_with_r);
# 1. Send the human off to authorize:
my $url = $r->authorize_url(
redirect_uri => 'https://my.app/oauth/reddit/callback',
scope => [qw( read identity )],
duration => 'permanent', # get a refresh_token
state => 'abc-123', # CSRF guard you verify on callback
);
# 2. Human clicks Approve, callback receives ?code=...&state=...
# Exchange it:
my $tokens = $r->complete_authorization(
http => $ws->http,
code => $code_from_callback,
redirect_uri => 'https://my.app/oauth/reddit/callback',
)->get;
# 3. Later sessions: re-hydrate from a stored refresh_token.
my $r = Net::Async::WebSearch::Provider::Reddit::OAuth->new(
client_id => ..., client_secret => ...,
grant_type => 'authorization_code',
refresh_token => $stored_refresh_token,
);
# Script app tied to a specific account (slightly higher rate limits):
my $r = Net::Async::WebSearch::Provider::Reddit::OAuth->new(
client_id => ...,
client_secret => ...,
grant_type => 'password',
username => 'myaccount',
password => 'hunter2',
user_agent => 'my-search-bot/1.0 by /u/myaccount',
);
# Installed-app grant (no server-side secret — still set client_secret to ''):
my $r = Net::Async::WebSearch::Provider::Reddit::OAuth->new(
client_id => ...,
client_secret => '',
grant_type => 'installed',
device_id => 'a-random-20-to-30-char-string',
user_agent => 'my-search-bot/1.0',
);
DESCRIPTION
Drop-in replacement for Net::Async::WebSearch::Provider::Reddit that hits the oauth.reddit.com endpoint with a bearer token. Higher rate limits, proper ToS compliance, same result shape.
client_id
Required. The app's public client id.
client_secret
Required (but may be the empty string for installed apps).
grant_type
client_credentials (default, app-only), password (script apps), installed (device-id apps), or authorization_code (user consent flow, typically driven by a host app — MCP server, web app, CLI — via "authorize_url" and "complete_authorization").
username
password
Reddit account credentials. Only used when grant_type=password.
device_id
20-30 character identifier for grant_type=installed. Should be stable per device but opaque. Defaults to DO_NOT_TRACK_THIS_DEVICE (a Reddit convention that opts out of fingerprinting).
user_agent
Override the User-Agent string. Strongly recommended — see "SETUP".
token_url
Override the token endpoint. Default https://www.reddit.com/api/v1/access_token.
token_margin
Seconds before token expiry to refresh preemptively. Default 60.
endpoint
Override the search endpoint. Default https://oauth.reddit.com.
All attributes inherited from Net::Async::WebSearch::Provider::Reddit (subreddit, sort, time) apply as well.
access_token
refresh_token
token_expires_at
Optional — pre-seed the auth state on new() (e.g. rehydrating a session from a host app's persistent store). When a refresh_token is present, the provider will use it to get fresh access tokens automatically instead of re-doing client_credentials.
on_token_refresh
Coderef called as $cb->($provider, $raw_response_hash) every time a new access token is minted (initial fetch or refresh). Use it to persist $provider->token_state to your session store so a later process can re-hydrate without re-prompting the user.
authorize_url(%args)
Builds — without a network round-trip — the Reddit URL the human should visit to grant your app access. Returns a plain URL string.
Required: redirect_uri. Optional: scope (arrayref or space-separated string, default 'read'), state (auto-generated if omitted — strongly recommended to supply your own for CSRF defence), duration (permanent (default) to receive a refresh_token, temporary for a one-hour token only).
This is the primitive a host app (MCP server, web app) uses to kick off the consent dance. The host shows the URL to the user — via a tool result, a browser redirect, or whatever — and waits for the ?code=... to come back on the callback URL.
complete_authorization(%args)
Exchanges an authorization code for an access/refresh token pair. Required: http (a Net::Async::HTTP — typically $ws->http), code (the value from the redirect), and redirect_uri (must match the one used in "authorize_url"). Returns a Future of the access token; also populates $self->access_token, $self->refresh_token, and $self->token_expires_at, and fires on_token_refresh if set.
token_state
Snapshots the currently-held auth state as a hashref:
{ access_token, refresh_token, token_expires_at }
Feed this hash back to a future new() call (or drop its keys in as access_token = ..., refresh_token => ..., token_expires_at => ...>) to continue the same authenticated session. Intended for host-app session persistence — this library does not persist anything on its own.
search
Same contract as the parent provider — plus a transparent token fetch/refresh round-trip before the first search (and again when the cached token is about to expire). For authorization_code grants without a seeded refresh_token, the caller must drive "authorize_url" / "complete_authorization" first; otherwise the first search will fail.
SETUP
Reddit apps are created at https://www.reddit.com/prefs/apps. The flow:
Log in to Reddit with the account that should own the app. For app-only / installed grants this can be any account; for
grant_type=passwordit must be the account whose credentials you're going to use.Scroll to the bottom of https://www.reddit.com/prefs/apps and click "create app" (or "create another app").
Pick the app type:
script — when you plan to use
grant_type=passwordor the defaultclient_credentials. This is the right choice for personal/CLI tools and backend services where you own the account. Gets the "full" rate limit.web app — for server-side web apps using the authorization-code flow (not covered by this provider; use it only with
client_credentialshere).installed app — for mobile/desktop clients without a server-held secret. Use with
grant_type=installedand supply a per-devicedevice_id.
Fill in
name,description, andabout url(anything reasonable). Forredirect uriusehttp://localhost:8000(not used by this provider but the form demands a value).Submit. Reddit shows you two strings:
The
client_id: the short string shown right under the app name (looks likeAbCdEfGhIjKlMn).The
secret: next to the label "secret". Copy it now — it won't be shown again in full.
For installed apps there is no secret — pass
client_secret => ''.Choose a real User-Agent. Reddit's API wiki explicitly asks for the form
<platform>:<app-id>:<version> (by /u/<your-reddit-name>), e.g.my-search-bot/1.0 by /u/myaccount. Generic UAs (Python-requests, curl, LWP::UserAgent) get throttled into oblivion. Pass it viauser_agent => ...onnew.Respect the rate limits. Authenticated OAuth clients get ~100 QPM (queries per minute) per OAuth-ID. Script apps pin to the account, app-only / installed pin to the client_id.
After setup, the provider handles the token dance for you: it POSTs to https://www.reddit.com/api/v1/access_token, caches the bearer token until token_margin seconds before expiry, and attaches it to every search request against https://oauth.reddit.com.
SEE ALSO
Net::Async::WebSearch::Provider::Reddit, https://www.reddit.com/prefs/apps, https://github.com/reddit-archive/reddit/wiki/OAuth2, https://support.reddithelp.com/hc/en-us/articles/16160319875092-Reddit-Data-API-Wiki
SUPPORT
Issues
Please report bugs and feature requests on GitHub at https://github.com/Getty/p5-net-async-websearch/issues.
CONTRIBUTING
Contributions are welcome! Please fork the repository and submit a pull request.
AUTHOR
Torsten Raudssus <torsten@raudssus.de> https://raudss.us/
COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Torsten Raudssus.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.