Integrate Bolt.new with Reddit Ads using the Reddit Marketing API via Next.js API routes. Create a Reddit Ads developer app, implement OAuth 2.0 to obtain bearer tokens, then fetch campaign performance data including impressions, clicks, CPC, and conversion metrics. Build a community-targeted ad analytics dashboard. Reddit targets by subreddit and interest community — a fundamentally different model from demographic-based platforms. OAuth callback requires a deployed URL for production access.
Build a Custom Reddit Ads Analytics Dashboard in Bolt.new
Reddit Ads reaches over 100,000 active communities organized by topic and interest — a targeting model fundamentally different from Facebook's demographic approach or Google's keyword-based intent. When you advertise on Reddit, you choose subreddits (communities like r/technology, r/personalfinance, or r/gaming) and interest categories that Reddit users self-select by joining. This community-first targeting creates higher engagement among enthusiasts compared to mass-market demographic targeting, but it also means the standard campaign analytics metrics tell only part of the story — upvotes, comments, and community engagement signal quality in ways that CTR alone does not.
The Reddit Ads API follows standard REST conventions with OAuth 2.0 authentication. All API calls use HTTPS, which means outbound requests from Bolt's WebContainer preview work correctly during development. You can build and populate your campaign analytics dashboard in Bolt, see real numbers, and test most of the integration before deploying. The exception is OAuth 2.0 authorization flows where Reddit redirects back to your app — these callbacks require a stable HTTPS URL, which means testing the full auth flow requires deploying to Netlify or Bolt Cloud first.
For building a dashboard for your own ad account — an agency tool for one account, an internal analytics view, or a personal dashboard — you can use a long-lived refresh token obtained once through the authorization flow, then stored as an environment variable. This approach avoids building full OAuth infrastructure for single-account access and works well for most Bolt use cases. Multi-tenant applications where users connect their own Reddit Ads accounts need the full OAuth PKCE flow and a deployed callback URL.
Integration method
Bolt generates Next.js API routes that proxy requests to Reddit's Ads API, keeping your client credentials and bearer tokens server-side. Reddit's API is REST over HTTPS, so outbound API calls for reading campaign data work in Bolt's WebContainer during development. OAuth 2.0 authorization for multi-user apps requires a deployed URL as the redirect URI, but single-account access using a refresh token can be tested immediately.
Prerequisites
- A Bolt.new account with a Next.js project
- A Reddit account with an active Reddit Ads account (ads.reddit.com)
- At least $5 in ad spend history on the Reddit Ads account (required for API access)
- A Reddit developer app created at reddit.com/prefs/apps with 'web app' type
- A Reddit Ads API access request submitted at business.reddithelp.com (API access is not automatic)
Step-by-step guide
Create a Reddit Developer App and Request API Access
Create a Reddit Developer App and Request API Access
Reddit Ads API access requires two separate steps: creating an OAuth app at the Reddit developer portal, and separately requesting Ads API access through Reddit's business help center. This two-step requirement trips up most developers who assume creating a developer app is sufficient. First, go to reddit.com/prefs/apps and scroll to the bottom. Click 'Create App' or 'Create another app.' Choose the app type: select 'web app' for production use (supports OAuth 2.0 with refresh tokens), or 'script' for personal use with a single Reddit account (simpler but limited to your own account). Give your app a name and description. For the redirect URI, enter `https://localhost:3000/auth/callback` for initial setup — you will update this to your production URL after deployment. Copy your app's client ID (shown below the app name, looks like a random string) and generate a client secret (click 'edit' on the app to see the secret). These are your OAuth 2.0 credentials. Second, request Ads API access at business.reddithelp.com/helpcenter/s/contactsupport. Select 'Advertiser' as your issue type and explain your use case. Reddit manually reviews API access requests, with approval typically taking three to seven business days. While waiting for API access approval, you can build the dashboard frontend and API route structure using mock data. Once approved, Reddit will email your Ads account ID (format: `t2_xxxxxxxx`) and confirm your app has been granted Ads API permissions. Add your credentials to Bolt's .env file to proceed.
Add a .env file with REDDIT_CLIENT_ID=your-client-id, REDDIT_CLIENT_SECRET=your-client-secret, REDDIT_ACCESS_TOKEN=your-access-token, REDDIT_REFRESH_TOKEN=your-refresh-token, and REDDIT_ACCOUNT_ID=t2_your-account-id. Create a lib/reddit.ts file that exports a redditFetch helper function which takes an endpoint string and optional params. The function should call https://ads-api.reddit.com/api/v2.1/{endpoint} with Authorization: Bearer {REDDIT_ACCESS_TOKEN} header from process.env and return the parsed JSON. Include error handling that throws with Reddit's error message.
Paste this in Bolt.new chat
1// lib/reddit.ts2const REDDIT_ADS_BASE = 'https://ads-api.reddit.com/api/v2.1';34interface RedditFetchOptions {5 params?: Record<string, string>;6 method?: 'GET' | 'POST' | 'PATCH' | 'DELETE';7 body?: Record<string, unknown>;8}910export async function redditFetch<T = unknown>(11 endpoint: string,12 options: RedditFetchOptions = {}13): Promise<T> {14 const accessToken = process.env.REDDIT_ACCESS_TOKEN;15 if (!accessToken) throw new Error('REDDIT_ACCESS_TOKEN is not configured');1617 const url = new URL(`${REDDIT_ADS_BASE}/${endpoint}`);18 if (options.params) {19 Object.entries(options.params).forEach(([key, value]) => {20 url.searchParams.set(key, value);21 });22 }2324 const response = await fetch(url.toString(), {25 method: options.method || 'GET',26 headers: {27 Authorization: `Bearer ${accessToken}`,28 'Content-Type': 'application/json',29 'User-Agent': 'BoltApp/1.0',30 },31 body: options.body ? JSON.stringify(options.body) : undefined,32 });3334 if (!response.ok) {35 const errorText = await response.text();36 throw new Error(`Reddit Ads API error ${response.status}: ${errorText}`);37 }3839 return response.json() as Promise<T>;40}4142export async function refreshRedditToken(): Promise<string> {43 const credentials = Buffer.from(44 `${process.env.REDDIT_CLIENT_ID}:${process.env.REDDIT_CLIENT_SECRET}`45 ).toString('base64');4647 const response = await fetch('https://www.reddit.com/api/v1/access_token', {48 method: 'POST',49 headers: {50 Authorization: `Basic ${credentials}`,51 'Content-Type': 'application/x-www-form-urlencoded',52 'User-Agent': 'BoltApp/1.0',53 },54 body: new URLSearchParams({55 grant_type: 'refresh_token',56 refresh_token: process.env.REDDIT_REFRESH_TOKEN || '',57 }),58 });5960 const data = await response.json() as { access_token?: string; error?: string };61 if (data.error || !data.access_token) {62 throw new Error(`Token refresh failed: ${data.error}`);63 }64 return data.access_token;65}Pro tip: For single-account access (your own Reddit Ads account), use a 'script' type app instead of 'web app'. Script apps can obtain tokens via the password grant type without needing an OAuth redirect flow, which simplifies initial testing in Bolt's WebContainer.
Expected result: The Reddit API helper is set up with your credentials in .env. The redditFetch function is ready to call any Reddit Ads API endpoint with Bearer token authentication.
Obtain a Long-Lived Access Token via OAuth 2.0
Obtain a Long-Lived Access Token via OAuth 2.0
Reddit uses OAuth 2.0 for all Ads API access. The token flow depends on your app type. For 'script' apps (single Reddit account access), you can use the password grant to obtain tokens without a redirect flow — ideal for building personal or internal dashboards without deploying first. For 'web app' type (multi-user access), you need the authorization code flow with a redirect URI, which requires a deployed URL. For script apps: send a POST request to `https://www.reddit.com/api/v1/access_token` with Basic Auth using your client ID and secret, and a body with `grant_type=password`, `username=your-reddit-username`, and `password=your-reddit-password`. The response includes `access_token` (valid for one hour) and a `refresh_token` for renewal. For web apps (multi-user): construct an authorization URL pointing to `https://www.reddit.com/api/v1/authorize` with query params `client_id`, `response_type=code`, `state=random-string`, `redirect_uri=your-deployed-url/auth/callback`, `duration=permanent`, and `scope=ads:read ads:write`. After the user approves, Reddit redirects to your callback URL with a `code` parameter. Exchange this code for tokens via a POST to the token endpoint. The `duration=permanent` parameter is critical — it requests a refresh token that lets you obtain new access tokens indefinitely without re-authorization. For the refresh token obtained during initial setup, add it to your .env as `REDDIT_REFRESH_TOKEN`. When the access token expires (after one hour), your API route calls the refresh endpoint using the client credentials and refresh token to obtain a new access token. Build this refresh logic directly into your API routes so token expiry is handled transparently without manual intervention.
Create a Next.js API route at app/api/reddit/auth/route.ts that handles Reddit OAuth. On GET with a 'code' query param, exchange it for access and refresh tokens by POSTing to https://www.reddit.com/api/v1/access_token with Basic Auth (REDDIT_CLIENT_ID:REDDIT_CLIENT_SECRET) and grant_type=authorization_code. Return the tokens as JSON. Also create an app/api/reddit/refresh/route.ts that uses REDDIT_REFRESH_TOKEN from process.env to get a new access token and returns it.
Paste this in Bolt.new chat
1// app/api/reddit/refresh/route.ts2import { NextResponse } from 'next/server';34export async function POST() {5 const clientId = process.env.REDDIT_CLIENT_ID;6 const clientSecret = process.env.REDDIT_CLIENT_SECRET;7 const refreshToken = process.env.REDDIT_REFRESH_TOKEN;89 if (!clientId || !clientSecret || !refreshToken) {10 return NextResponse.json(11 { error: 'Reddit credentials not configured' },12 { status: 500 }13 );14 }1516 const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');1718 const response = await fetch('https://www.reddit.com/api/v1/access_token', {19 method: 'POST',20 headers: {21 Authorization: `Basic ${credentials}`,22 'Content-Type': 'application/x-www-form-urlencoded',23 'User-Agent': 'BoltApp/1.0',24 },25 body: new URLSearchParams({26 grant_type: 'refresh_token',27 refresh_token: refreshToken,28 }),29 });3031 const data = await response.json() as {32 access_token?: string;33 expires_in?: number;34 error?: string;35 };3637 if (!response.ok || data.error) {38 return NextResponse.json(39 { error: data.error || 'Token refresh failed' },40 { status: 401 }41 );42 }4344 return NextResponse.json({45 accessToken: data.access_token,46 expiresIn: data.expires_in,47 });48}Pro tip: Store the access token in a server-side cache (e.g., a module-level variable with an expiry timestamp) to avoid calling the refresh endpoint on every request. Reddit access tokens last one hour — refresh once per session rather than on each API call.
Expected result: The token refresh endpoint returns a fresh access token. You can verify this works by calling /api/reddit/refresh from your browser and checking that a valid access_token string is returned in the response.
Build the Campaign Analytics API Route
Build the Campaign Analytics API Route
Reddit's Ads API organizes campaigns in a three-level hierarchy: Campaigns (top-level objectives), Ad Groups (targeting and bidding), and Ads (creatives). Performance data is fetched via dedicated reporting endpoints rather than embedded in the campaign objects themselves. This separation means you typically need two API calls: one to fetch campaigns list and one to fetch their performance metrics. The campaigns endpoint is `GET /accounts/{account_id}/campaigns` and returns campaign metadata including name, status, objective, and budget configuration. The reporting endpoint for performance metrics is typically `GET /accounts/{account_id}/reports` with date range and breakdown parameters. Key metrics available include impressions, clicks, conversions, spend, CPM, CPC, CTR, and video views for video ads. Reddit Ads has unique metrics that differ from other platforms. The `ecpm` (effective CPM) reflects actual auction-clearing prices, and Reddit also provides `video_watched_75_percent` and `video_complete_views` for video creatives. The `reddit_engagement` metric counts post upvotes, comments, and shares that organic Reddit users take on your promoted posts — a metric unique to the platform that reflects content quality beyond click performance. For date ranges, the reporting API accepts ISO 8601 date strings for `start_time` and `end_time`. The granularity parameter controls whether you get aggregated totals or daily breakdowns — use `TOTAL` for summary cards and `DAY` for trend charts. A practical detail: Reddit's Ads API returns monetary values in microdollars (1,000,000 microdollars = $1.00). Divide all spend values by 1,000,000 before displaying them. This is different from Meta's API (which uses dollars as strings) and Stripe (which uses cents).
Create a Next.js API route at app/api/reddit/campaigns/route.ts that fetches campaign performance from Reddit Ads API. Accept query params: startDate and endDate (ISO 8601). Call GET /accounts/{REDDIT_ACCOUNT_ID}/campaigns to get campaigns, then GET /accounts/{REDDIT_ACCOUNT_ID}/reports with date_start, date_end, entity=campaign, and fields for impressions, clicks, spend, ecpm, ecpc, ctr, conversions. Join by campaign ID. Convert spend from microdollars (divide by 1000000). Return sorted by spend descending. Handle REDDIT_ACCESS_TOKEN from process.env.
Paste this in Bolt.new chat
1// app/api/reddit/campaigns/route.ts2import { NextResponse } from 'next/server';3import { redditFetch } from '@/lib/reddit';45interface RedditCampaign {6 id: string;7 name: string;8 status: string;9 objective: string;10 daily_budget_in_cents?: number;11 total_budget_in_cents?: number;12}1314interface RedditReportRow {15 id: string;16 impressions: number;17 clicks: number;18 spend: number; // microdollars19 ecpm: number;20 ecpc: number;21 ctr: number;22 conversions?: number;23}2425export async function GET(request: Request) {26 const { searchParams } = new URL(request.url);27 const startDate = searchParams.get('startDate') ||28 new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0];29 const endDate = searchParams.get('endDate') ||30 new Date().toISOString().split('T')[0];3132 const accountId = process.env.REDDIT_ACCOUNT_ID;33 if (!accountId) {34 return NextResponse.json({ error: 'REDDIT_ACCOUNT_ID is not configured' }, { status: 500 });35 }3637 try {38 const [campaignsData, reportsData] = await Promise.all([39 redditFetch<{ data: RedditCampaign[] }>(40 `accounts/${accountId}/campaigns`,41 { params: { limit: '100' } }42 ),43 redditFetch<{ data: RedditReportRow[] }>(44 `accounts/${accountId}/reports`,45 {46 params: {47 entity: 'campaign',48 fields: 'impressions,clicks,spend,ecpm,ecpc,ctr,conversions',49 date_start: startDate,50 date_end: endDate,51 granularity: 'TOTAL',52 },53 }54 ),55 ]);5657 const metricsMap = new Map<string, RedditReportRow>();58 (reportsData.data || []).forEach((row) => {59 metricsMap.set(row.id, row);60 });6162 const campaigns = (campaignsData.data || []).map((campaign) => {63 const metrics = metricsMap.get(campaign.id);64 return {65 id: campaign.id,66 name: campaign.name,67 status: campaign.status,68 objective: campaign.objective,69 dailyBudget: campaign.daily_budget_in_cents70 ? campaign.daily_budget_in_cents / 10071 : null,72 impressions: metrics?.impressions ?? 0,73 clicks: metrics?.clicks ?? 0,74 spend: metrics ? metrics.spend / 1_000_000 : 0,75 cpm: metrics ? metrics.ecpm / 1_000_000 : 0,76 cpc: metrics ? metrics.ecpc / 1_000_000 : 0,77 ctr: metrics?.ctr ?? 0,78 conversions: metrics?.conversions ?? 0,79 };80 });8182 campaigns.sort((a, b) => b.spend - a.spend);8384 const totals = campaigns.reduce(85 (acc, c) => ({86 spend: acc.spend + c.spend,87 impressions: acc.impressions + c.impressions,88 clicks: acc.clicks + c.clicks,89 conversions: acc.conversions + c.conversions,90 }),91 { spend: 0, impressions: 0, clicks: 0, conversions: 0 }92 );9394 return NextResponse.json({ campaigns, totals, dateRange: { startDate, endDate } });95 } catch (err) {96 const message = err instanceof Error ? err.message : 'Failed to fetch Reddit campaigns';97 return NextResponse.json({ error: message }, { status: 500 });98 }99}Pro tip: Reddit Ads API rate limits are 100 requests per minute per OAuth token. The parallel Promise.all fetch for campaigns and reports counts as 2 requests — well within limits. If you add more endpoints later (ad groups, ads), batch requests carefully to stay under the rate limit.
Expected result: The API route returns a campaigns array with performance metrics joined from the reports endpoint. Spend values should be in dollars (e.g., 127.43 not 127430000). Call /api/reddit/campaigns in the browser to verify your real campaign data appears.
Build the Reddit Ads Dashboard Interface
Build the Reddit Ads Dashboard Interface
Reddit Ads dashboards benefit from surfacing community-specific context that standard ad platform dashboards omit. Beyond the usual spend, clicks, and CTR metrics, Reddit advertisers care about which subreddit communities drove the best performance and how organic Reddit engagement (upvotes, comments) on promoted posts compares across creatives. For the dashboard layout, organize it as: a top summary row with total spend, total clicks, average CTR, and total conversions; a campaign table with sortable columns; and a secondary section showing performance by subreddit community when ad group data is available. The community breakdown is where Reddit Ads insight differs most from other platforms — seeing that r/personalfinance converts at 3x the rate of r/news for your financial product is the kind of insight that reshapes your entire targeting strategy. For date range selection, Reddit's API accepts specific ISO date strings, so build a date range picker with presets (Last 7 days, Last 30 days, Last 90 days) that compute the correct ISO strings. Use the same pattern as the Facebook Ads dashboard for the React date picker component — this consistency makes users comfortable across your multi-platform analytics app. For the campaign status display, Reddit campaigns have statuses including ACTIVE, PAUSED, COMPLETED, DRAFT, and SCHEDULED. Color-code these clearly: green for ACTIVE, yellow for PAUSED, gray for COMPLETED, and blue for SCHEDULED. The DRAFT status indicates campaigns that have been created but not yet submitted for review — these are important to surface separately since they are not yet spending. Charts that work well for Reddit Ads: a horizontal bar chart ranking campaigns by spend (same as other ad platforms), a scatter plot comparing CTR vs CPC to identify efficient subreddit targeting, and a conversion funnel chart from impressions to clicks to conversions.
Build a Reddit Ads dashboard page at /reddit-ads. Fetch from /api/reddit/campaigns with date range params defaulting to last 30 days. Show: (1) summary cards for total spend (formatted as $X.XX), total clicks, average CTR percentage, and total conversions; (2) a Recharts bar chart of spend by campaign; (3) a sortable table with columns for Campaign Name, Status (color-coded badge), Objective, Spend, Impressions, Clicks, CTR, CPC, and Conversions. Add a date range selector with Last 7d / Last 30d / Last 90d presets. Include loading skeletons and an error state.
Paste this in Bolt.new chat
Pro tip: Reddit Ads objective names differ from Meta's. Reddit uses TRAFFIC, CONVERSIONS, VIDEO_VIEWS, APP_INSTALLS, BRAND_AWARENESS, and LEAD_GENERATION. Map these to user-friendly display names in your dashboard (e.g., TRAFFIC → 'Website Traffic') for better readability.
Expected result: The Reddit Ads dashboard loads with real campaign data, displays spend and performance metrics with proper formatting, and the date range selector refreshes the data correctly when changed.
Deploy and Set Up OAuth Callback for Production
Deploy and Set Up OAuth Callback for Production
During development in Bolt's WebContainer, outbound calls to Reddit's Ads API work correctly for fetching campaign data. The constraint is OAuth 2.0 callback handling — when you build a multi-user app where different advertisers can connect their Reddit Ads accounts, the OAuth redirect URL must point to a stable HTTPS server. Bolt's WebContainer preview URL (`webcontainer-api.io` subdomain) changes and cannot receive the OAuth callback reliably. Deploy to Netlify via Settings → Applications → Connect Netlify, or click Publish to deploy to Bolt Cloud. After deployment, you will have a stable URL like `https://your-app.netlify.app`. Update your Reddit developer app at reddit.com/prefs/apps to add the production redirect URI: `https://your-app.netlify.app/api/reddit/auth/callback`. In your hosting dashboard (Netlify environment variables or Bolt Cloud secrets), add the production values: `REDDIT_CLIENT_ID`, `REDDIT_CLIENT_SECRET`, `REDDIT_ACCESS_TOKEN`, `REDDIT_REFRESH_TOKEN`, and `REDDIT_ACCOUNT_ID`. These should be your production credentials, not the development placeholders from your .env file. For building a SaaS product where multiple advertisers connect their Reddit Ads accounts, you need a proper token storage system. Each user's access token and refresh token must be stored in your database (Supabase or similar), associated with their user ID, and retrieved per-request. The single-token approach in .env only works for single-account access. Build the multi-tenant flow after validating the single-account integration works end-to-end in production.
Add an /api/reddit/health route that checks if REDDIT_ACCESS_TOKEN and REDDIT_ACCOUNT_ID are configured, then calls Reddit Ads API GET /accounts/{REDDIT_ACCOUNT_ID}/campaigns with limit=1 to verify authentication works. Return { connected: true, accountId } on success or { connected: false, error } if any check fails. Add a connection status banner at the top of the Reddit Ads dashboard page that shows this health check result.
Paste this in Bolt.new chat
1# Production environment variables — add in Netlify Dashboard or Bolt Cloud Secrets2# Never commit these values to your git repository34REDDIT_CLIENT_ID=your_client_id_here5REDDIT_CLIENT_SECRET=your_client_secret_here6REDDIT_ACCESS_TOKEN=your_access_token_here7REDDIT_REFRESH_TOKEN=your_refresh_token_here8REDDIT_ACCOUNT_ID=t2_your_account_id910# Redirect URI registered in Reddit developer app settings:11# https://your-app.netlify.app/api/reddit/auth/callbackPro tip: Reddit access tokens expire after one hour. For production, implement automatic token refresh: catch 401 errors in your redditFetch helper, call the refresh endpoint, update the stored token, and retry the original request. This makes token expiry transparent to your users.
Expected result: The app is deployed with production Reddit credentials configured. The health check confirms the API connection is working. OAuth callbacks to /api/reddit/auth/callback succeed with the deployed URL registered in your Reddit developer app.
Common use cases
Reddit Ads Campaign Performance Dashboard
Build a dashboard showing campaign-level performance across all active Reddit Ads campaigns. Display spend, impressions, clicks, CPC, CPM, and conversion metrics with the ability to drill down into individual ad groups and ads. Compare performance across campaigns targeting different subreddit communities to identify which audiences drive the best results.
Build a Reddit Ads analytics dashboard. Create a Next.js API route at /api/reddit/campaigns that calls Reddit's Ads API to fetch all campaigns with their performance metrics (spend, impressions, clicks, cpc, cpm, conversions) for a given date range. Accept accountId and dateRange as query params. Use REDDIT_ACCESS_TOKEN from process.env as a Bearer token. Build a React dashboard with a summary row of total spend and clicks, a sortable campaign table, and a bar chart comparing spend by campaign. Show subreddit targeting for each campaign.
Copy this prompt to try it in Bolt.new
Subreddit Targeting Performance Analyzer
Analyze which subreddit communities are driving the best campaign performance. Pull ad group targeting data to identify the most cost-efficient community placements, compare engagement rates across different subreddit categories, and surface insights about which communities convert best for your offer type.
Create a subreddit performance analyzer using Reddit Ads API. Build a Next.js API route at /api/reddit/ad-groups that fetches all ad groups in an account with their targeting details and performance metrics (impressions, clicks, cpc, ctr, conversions). Create a React component showing each ad group's targeted subreddits, key performance metrics, and a cost-per-conversion badge. Group ad groups by subreddit category (technology, finance, gaming, etc.) and show aggregate performance per category. Store REDDIT_ACCESS_TOKEN and REDDIT_ACCOUNT_ID in process.env.
Copy this prompt to try it in Bolt.new
Reddit Ads Budget Monitor and Alert System
Build a monitoring dashboard that tracks budget pacing across Reddit Ads campaigns and alerts when campaigns are over-spending or under-delivering against their daily budgets. Show real-time spend versus budget allocation to help optimize campaign budget distribution across subreddit targets.
Build a Reddit Ads budget monitoring dashboard. Create a Next.js API route at /api/reddit/budget-status that fetches all active campaigns with their daily budgets, current spend, and pacing. Calculate spend percentage for today. Return campaigns sorted by overpace (spending too fast) vs underpace (spending too slow). Build a React dashboard with color-coded pacing indicators: green for 80-110% of expected pace, yellow for 50-80% or 110-130%, red for under 50% or over 130%. Add a total account spend summary. Use REDDIT_ACCESS_TOKEN and REDDIT_ACCOUNT_ID from process.env.
Copy this prompt to try it in Bolt.new
Troubleshooting
401 Unauthorized — 'invalid_token' or 'token_expired' error from Reddit API
Cause: Reddit access tokens expire after exactly one hour. If your REDDIT_ACCESS_TOKEN in .env is more than an hour old, all API calls will return 401.
Solution: Call your /api/reddit/refresh endpoint to get a new access token, then update REDDIT_ACCESS_TOKEN in your .env file. For production, build automatic token refresh into your redditFetch helper to catch 401 responses, call the refresh endpoint, and retry the request automatically.
1// Add auto-refresh to lib/reddit.ts:2export async function redditFetchWithRefresh<T>(endpoint: string, options = {}): Promise<T> {3 try {4 return await redditFetch<T>(endpoint, options);5 } catch (err) {6 if (err instanceof Error && err.message.includes('401')) {7 const newToken = await refreshRedditToken();8 process.env.REDDIT_ACCESS_TOKEN = newToken;9 return redditFetch<T>(endpoint, options);10 }11 throw err;12 }13}403 Forbidden — 'insufficient_scope' or 'USER_REQUIRED' from Reddit Ads API
Cause: Either the OAuth token was generated without the required 'ads:read' and 'ads:write' scopes, or your Reddit developer app has not been approved for Ads API access by Reddit's team.
Solution: Verify your Reddit Ads API access has been approved by checking if you received a confirmation email from Reddit Business support. If approved, regenerate your access token and explicitly include the 'ads:read ads:write' scopes in your OAuth authorization request. Script app tokens must also include the correct scope in the authorization request.
Spend values appear as extremely large numbers (e.g., 12743000 instead of $12.74)
Cause: Reddit's Ads API returns monetary values in microdollars — multiply by 1,000,000 to get dollars. Forgetting to convert this results in spend figures that appear to be millions of dollars.
Solution: Divide all spend, CPM, and CPC values from the Reddit Reports API by 1,000,000 before displaying or storing them. Budget values from the campaigns endpoint are in cents — divide those by 100.
1// Correct conversion in API route:2spend: metrics ? metrics.spend / 1_000_000 : 0, // microdollars to dollars3cpm: metrics ? metrics.ecpm / 1_000_000 : 0, // microdollars to dollars4dailyBudget: campaign.daily_budget_in_cents / 100, // cents to dollarsOAuth callback fails during local development — redirect to localhost does not work in WebContainer
Cause: Bolt's WebContainer cannot reliably receive OAuth callbacks from external services like Reddit. The WebContainer uses dynamic URLs that change between sessions and cannot be registered as stable OAuth redirect URIs.
Solution: For single-account access, use a Reddit 'script' app type which allows password-grant authentication without a redirect URI. For web app OAuth flows, deploy to Netlify or Bolt Cloud first to get a stable HTTPS URL, register that URL as your redirect URI in the Reddit developer app, then complete the OAuth flow on the deployed site.
Best practices
- Use a 'script' type Reddit app and password grant for single-account internal dashboards — it eliminates the OAuth redirect complexity and works immediately without deploying.
- Store monetary values from Reddit's API as raw microdollars in any database, only converting to dollars at display time — this preserves precision and matches what the API returns natively.
- Cache Reddit Ads API responses for at least 5 minutes since campaign metrics update at most hourly and the API has a 100 requests per minute rate limit per token.
- Implement automatic token refresh in your API helper using try-catch on 401 errors — Reddit access tokens expire after exactly one hour and manual token rotation breaks production dashboards.
- Display Reddit's unique engagement metrics (upvotes, comments, shares on promoted posts) alongside standard click metrics — they signal creative quality in ways CTR does not capture.
- Always divide spend values by 1,000,000 and budget values by 100 before displaying — Reddit's API uses microdollars for metrics and cents for budget fields, mixing two different units.
- Add Reddit Ads account ID validation (must start with 't2_' prefix) to your API routes to provide clear error messages when the account ID is misconfigured.
- Build subreddit-level performance breakdowns using the ad group reporting endpoint — community-level insight is Reddit's key differentiator from other ad platforms and unlocks targeting optimization unavailable in standard dashboards.
Alternatives
Facebook Ads offers broader demographic reach and more mature API tooling compared to Reddit's community-based targeting, making it a better fit for consumer brands targeting wide audiences.
Google Ads captures high-intent keyword-based search traffic rather than community interest, offering complementary reach to Reddit's discovery-based advertising for most marketing strategies.
Twitter Ads targets real-time conversation topics and trending interests similar to Reddit's community approach, but with a different audience demographic skewing toward news and professional content.
LinkedIn Ads provides professional demographic targeting with job title and company size filters that Reddit cannot match, making it the better choice for B2B campaigns targeting specific professional roles.
Frequently asked questions
Does Bolt.new have a native Reddit Ads integration?
No — Bolt.new does not have a native Reddit Ads connector. The integration requires building Next.js API routes that call Reddit's Ads API endpoints with OAuth 2.0 authentication. Bolt's AI can generate this boilerplate from a descriptive prompt, significantly reducing the initial setup time even without a built-in connector.
Can I test the Reddit Ads dashboard in Bolt's WebContainer preview without deploying?
Yes for most features — outbound GET requests to Reddit's Ads API work in Bolt's WebContainer since they are standard HTTPS calls. You can fetch campaign data, populate your dashboard, and test the analytics display entirely in the preview. The only feature requiring deployment is the OAuth 2.0 authorization callback, which Reddit sends back to your redirect URI. For single-account access using a pre-obtained refresh token stored in .env, no OAuth callback is needed during development.
Do I need to apply separately for Reddit Ads API access beyond creating a developer app?
Yes — creating a developer app at reddit.com/prefs/apps is only the first step. You must also request Ads API access through Reddit's business help center and wait for manual approval, which typically takes three to seven business days. Without explicit API access approval, your developer app can authenticate with Reddit but cannot call the Ads API endpoints. The approval process requires explaining your use case.
Why does the Reddit Ads API show spend as multi-million dollar values?
Reddit's Ads API reports API returns monetary values in microdollars, not dollars. One US dollar equals 1,000,000 microdollars in their API response. Divide all spend, eCPM, and eCPC values from the reports endpoint by 1,000,000 to convert to dollars. Note that the campaigns endpoint returns budget values in cents (divide by 100), not microdollars — a different unit used for the same type of data.
How does Reddit Ads targeting differ from Facebook Ads in my dashboard?
Reddit targets users by the communities (subreddits) they join and engage with, not by demographic attributes like age, income, or interests inferred from behavior. This means your Reddit Ads dashboard should surface subreddit-level performance data — which communities drove the best CTR and conversions — rather than demographic breakdowns. Reddit's API provides targeting data per ad group, including the specific subreddits targeted, enabling community-level performance analysis that has no equivalent on other platforms.
How long do Reddit access tokens last and how should I handle expiry?
Reddit access tokens expire after exactly one hour. Refresh tokens obtained with 'duration=permanent' in the OAuth request do not expire and can generate new access tokens indefinitely. Build automatic token refresh into your API helper: catch 401 responses, call the refresh endpoint using your client credentials and refresh token, store the new access token, and retry the original request. This makes expiry invisible to dashboard users.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation