Skip to content

ModelsLab/reddit-ads-mcp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

reddit-ads-mcp

MCP server for Reddit Ads API v3 over stdio.

This repo is intended to be handed to Claude Code, Codex, or other MCP-capable agents so they can inspect, report on, and operate Reddit Ads accounts programmatically.

What it exposes

  • 94 Reddit Ads API operations from the checked-in OpenAPI spec
  • 4 auth helper tools
  • 98 MCP tools total
  • automatic bearer token handling
  • refresh-token support
  • optional pagination following for paginated endpoints

Tool names follow this pattern:

  • reddit_ads_list_campaigns
  • reddit_ads_create_campaign
  • reddit_ads_get_a_report
  • reddit_ads_post_conversion_events
  • reddit_ads_generate_bid_suggestion

Auth helper tools:

  • reddit_ads_auth_status
  • reddit_ads_auth_get_authorization_url
  • reddit_ads_auth_exchange_code
  • reddit_ads_auth_refresh_access_token

Status

All endpoints from the current OpenAPI spec are registered in MCP.

Live verification was run against a real authorized Reddit Ads account on March 11, 2026. Most of the surface is working. Two endpoints are implemented but currently return upstream Reddit 500 errors with valid requests:

  • List Keyword Suggestions
  • Get Catalog Import Report

Prerequisites

Install these first:

  • Node.js 20+
  • npm
  • a Reddit developer app with Ads API access
  • a user-authorized Reddit OAuth refresh token for the advertiser account you want to operate

Check versions:

node -v
npm -v

Get started

1. Clone and install

git clone <your-repo-url>
cd reddit-ads-mcp
npm install

2. Configure environment

Copy the example env file:

cp .env.example .env

Set at least:

REDDIT_APP_ID=...
REDDIT_APP_SECRET=...
REDDIT_REDIRECT_URI=...
REDDIT_REFRESH_TOKEN=...
REDDIT_SCOPES=adsread,adsedit,adsconversions,history,read
REDDIT_USER_AGENT=mcp:reddit-ads-mcp:v0.1.0 (by /u/your_reddit_username)

If you do not have a refresh token yet, follow the OAuth flow below first.

3. Build

npm run build

4. Smoke test locally

npm test
npm run test:discovery

If you have a real refresh token configured, continue with:

npm run test:authorized
npm run test:full-writes
npm run test:remaining

5. Run the MCP server

npm start

This starts the MCP server over stdio using:

node dist/src/index.js

MCP client wiring

Example stdio MCP config:

{
  "mcpServers": {
    "reddit-ads": {
      "command": "node",
      "args": ["/absolute/path/to/reddit-ads-mcp/dist/src/index.js"],
      "cwd": "/absolute/path/to/reddit-ads-mcp",
      "env": {
        "REDDIT_APP_ID": "your_app_id",
        "REDDIT_APP_SECRET": "your_app_secret",
        "REDDIT_REDIRECT_URI": "https://your-redirect.example/",
        "REDDIT_REFRESH_TOKEN": "your_refresh_token",
        "REDDIT_SCOPES": "adsread,adsedit,adsconversions,history,read",
        "REDDIT_USER_AGENT": "mcp:reddit-ads-mcp:v0.1.0 (by /u/your_reddit_username)"
      }
    }
  }
}

If your MCP client supports inheriting env from the shell, you can keep the secrets in .env and only set command, args, and cwd.

Setup

  1. Install dependencies.
  2. Configure environment variables.
  3. Build the server.
  4. Run it over stdio for your MCP client.
npm install
npm run build
npm start

Required env

Use .env.example as the template.

Recommended auth mode:

  • REDDIT_APP_ID
  • REDDIT_APP_SECRET
  • REDDIT_REDIRECT_URI
  • REDDIT_REFRESH_TOKEN
  • REDDIT_SCOPES
  • REDDIT_USER_AGENT

Important:

  • For real Ads account access, use a user-authorized OAuth refresh token.
  • App ID + secret alone are not enough for normal /me, business, campaign, reporting, and account CRUD access.
  • client_credentials is only useful for limited fallback reads and is off by default.

OAuth flow

Use the auth helper tools or do the exchange yourself.

Typical flow:

  1. Call reddit_ads_auth_get_authorization_url
  2. Open the returned URL in a browser while logged into the correct Reddit Ads user
  3. Approve access
  4. Capture the returned code
  5. Exchange it with reddit_ads_auth_exchange_code
  6. Save the refresh_token into REDDIT_REFRESH_TOKEN

Once REDDIT_REFRESH_TOKEN is present, the MCP server can refresh access tokens automatically.

Verified live account patterns

Live verification was performed against a real authorized Reddit Ads account, but no account-specific IDs are documented here.

Agents should always discover resources dynamically instead of hardcoding them.

Recommended discovery order:

  1. reddit_ads_auth_status
  2. reddit_ads_list_my_businesses
  3. reddit_ads_get_business
  4. reddit_ads_list_ad_accounts_by_business
  5. reddit_ads_get_ad_account
  6. reddit_ads_list_profiles_by_business
  7. reddit_ads_list_pixels_by_business
  8. reddit_ads_list_product_catalogs

Live-tested coverage

Verified working live through MCP:

  • business and account discovery
  • campaign create/get/update/archive
  • ad group create/get/update/archive
  • ad create/get/update/archive
  • standard post creation for ad creative
  • structured post creation job, polling, retrieval, and update
  • saved audience create/get/update
  • custom audience create, add users, remove users, delete
  • reporting
  • ad account history
  • conversions API posting
  • pixels, profiles, funding instruments reads
  • lead gen form create/list/get
  • product catalog create/get/update/delete
  • product create/list/delete
  • product set create/list/get/update/list-products/delete
  • product feed create/list/get/update/delete
  • catalog import listing
  • catalog import issues listing
  • geolocations list + validation
  • communities list + search + suggestions
  • keyword validation
  • bid suggestion
  • channel planning reach

Implemented but currently returning upstream Reddit 500 on live retest:

  • reddit_ads_list_keyword_suggestions
  • reddit_ads_get_catalog_import_report

Important payload lessons

Some endpoints required payload details that were not obvious from a superficial reading of the spec.

Campaign creation

This minimal payload worked:

{
  "data": {
    "name": "example-campaign",
    "configured_status": "PAUSED",
    "objective": "IMPRESSIONS"
  }
}

Ad group creation

This required delivery configuration:

{
  "data": {
    "campaign_id": "campaign_id_here",
    "name": "example-adgroup",
    "configured_status": "PAUSED",
    "bid_type": "CPM",
    "bid_strategy": "BIDLESS",
    "goal_type": "DAILY_SPEND",
    "goal_value": 5000000,
    "start_time": "2026-03-11T05:00:00Z",
    "end_time": "2026-03-18T05:00:00Z",
    "targeting": {
      "communities": ["StableDiffusion"],
      "custom_audience_ids": [],
      "devices": [],
      "excluded_communities": [],
      "excluded_custom_audience_ids": [],
      "excluded_geolocations": [],
      "excluded_keywords": [],
      "expand_targeting": false,
      "geolocations": ["US"],
      "interests": [],
      "keywords": [],
      "carriers": [],
      "suppression_event_types": [],
      "age_targeting": {},
      "languages": null,
      "gender": null
    }
  }
}

Ad creation

This worked:

{
  "data": {
    "ad_group_id": "ad_group_id_here",
    "configured_status": "PAUSED",
    "name": "example-ad",
    "post_id": "post_id_here"
  }
}

Reports

Use the body wrapper:

{
  "data": {
    "starts_at": "2025-07-28T00:00:00Z",
    "ends_at": "2026-03-10T23:00:00Z",
    "fields": ["IMPRESSIONS", "CLICKS", "SPEND"],
    "breakdowns": ["DATE"]
  }
}

Bid suggestions

A valid request needed bid_strategy and delivery config:

{
  "data": {
    "bid_type": "CPM",
    "bid_strategy": "BIDLESS",
    "goal_type": "DAILY_SPEND",
    "goal_value": 5000000,
    "currency": "USD",
    "duration": {
      "start_time": "2026-03-11T05:00:00Z",
      "end_time": "2026-03-18T05:00:00Z"
    },
    "targeting": {
      "communities": ["StableDiffusion"],
      "custom_audience_ids": [],
      "devices": [],
      "excluded_communities": [],
      "excluded_custom_audience_ids": [],
      "excluded_geolocations": [],
      "excluded_keywords": [],
      "expand_targeting": false,
      "geolocations": ["US"],
      "interests": [],
      "keywords": [],
      "carriers": [],
      "locations": ["FEED"],
      "platforms": ["DESKTOP"],
      "suppression_event_types": [],
      "view_modes": ["CARD"],
      "age_targeting": {},
      "languages": null,
      "gender": null
    }
  }
}

Product sets

The safe filter that worked in live tests was:

{
  "data": {
    "name": "example-product-set",
    "filter": "{}"
  }
}

Human-readable filter strings such as brand = "ModelsLab" returned 400 invalid filter format.

Structured posts

Creation is asynchronous:

  1. reddit_ads_create_structured_post_creation_job
  2. poll reddit_ads_get_structured_post_creation_job
  3. wait for status: "SUCCESS"
  4. use returned post_id
  5. call reddit_ads_get_structured_post
  6. call reddit_ads_update_structured_post if needed

Product feeds and imports

Feed object CRUD works immediately. Import records appear only after schedule-driven import processing begins, so agents should poll reddit_ads_list_catalog_imports for a short period instead of assuming immediate availability.

Known upstream issues

These are not MCP registration problems. They were reproduced with valid authenticated requests:

  • reddit_ads_list_keyword_suggestions -> 500 Internal Server Error
  • reddit_ads_get_catalog_import_report -> 500 error retrieving presigned URL

Agents should treat these as upstream Reddit failures and degrade gracefully.

Test scripts

Useful scripts in this repo:

Run them with:

npm run build
npm test
npm run test:discovery
npm run test:authorized
npm run test:full-writes
npm run test:remaining

Guidance for agents

If you are an agent using this MCP:

  1. Start with discovery, not writes.
  2. Prefer refresh-token auth.
  3. Expect some endpoints to require polling.
  4. Handle 429 and 500 responses explicitly.
  5. Use PAUSED or non-delivering states for validation writes when possible.
  6. Clean up temporary catalogs, feeds, campaigns, ad groups, ads, and audiences after tests.
  7. Treat keyword suggestions and catalog import report as known upstream failures unless Reddit behavior changes.

Spec refresh

To refresh the spec:

npm run refresh:spec

Source:

https://ads-api.reddit.com/api/v3/openapi.json

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors