Reddit Ads
The Reddit Ads source syncs an ad account, campaigns, ads, ad groups, and daily account, campaign, ad group, and ad performance metrics from the Reddit Ads API v3 for a single ad account.
Prerequisites
- A Reddit Ads account with at least one ad account, and a Reddit user who can access the ad account you want to sync.
- The ID of the ad account you want to sync.
- If you use the Use your own Reddit app authentication option:
- A Reddit developer app, which gives you the app ID and secret this connector uses as its OAuth client ID and client secret. Only business admins with a verified Reddit account can create one.
- An OAuth refresh token for that app, authorized with the
adsreadscope.
Set up the Reddit Ads source
The connector offers two Authentication options:
- Authenticate with Reddit: Sign in to Reddit from Airbyte, and Airbyte obtains the refresh token for you. This is the default option in Airbyte Cloud. You don't need your own developer app, so skip Steps 1 and 2.
- Use your own Reddit app: Enter the client ID, client secret, and refresh token of a Reddit developer app you manage. Use this option in self-managed Airbyte, or if you already have a developer app. Complete Steps 1 and 2 first.
Sources created before version 0.2.0 move to Use your own Reddit app automatically and keep their existing client ID, client secret, and refresh token, so they need no action.
Step 1: Create a developer app
- In Reddit Ads Manager, open your business's Developer Applications page.
- Select Add Apps > Create an app, then fill in the app name, About URL, redirect URL, and primary contact. Reddit requires a redirect URL even though the connector never uses it interactively, so any URL you control works, such as
https://example.com/oauth/callback. - Select Create App. Reddit shows the app ID and secret. Copy both. The app ID is your OAuth Client ID and the secret is your OAuth Client Secret.
If you already have a Reddit app under Preferences > Apps, you can select Add Apps > Migrate an existing app instead. Migrating limits the app's scope to the Ads API.
For details, see Reddit's Create a Developer Application guide.
Step 2: Get a refresh token
The connector authenticates with the OAuth refresh token grant, so you need a long-lived refresh token. Get one with the authorization code flow described in Reddit's authentication guide.
-
Sign in to Reddit as the user whose ads data you want to sync, then open the following URL in a browser, replacing the placeholders:
https://www.reddit.com/api/v1/authorize?client_id=YOUR_APP_ID&response_type=code&state=airbyte&redirect_uri=YOUR_REDIRECT_URL&duration=permanent&scope=adsreadSet
durationtopermanent. Atemporaryauthorization doesn't return a refresh token, and the connector can't sync without one.adsreadis the only scope the connector needs. -
Select Allow. Reddit redirects to your redirect URL with a
codequery parameter. Copy the code, removing any trailing#_characters. The code expires after 10 minutes and works only once. -
Exchange the code for tokens:
curl -X POST https://www.reddit.com/api/v1/access_token \-H 'content-type: application/x-www-form-urlencoded' \-A 'YOUR_USER_AGENT' \-u 'YOUR_APP_ID:YOUR_APP_SECRET' \-d 'grant_type=authorization_code&code=YOUR_CODE&redirect_uri=YOUR_REDIRECT_URL' -
Copy the
refresh_tokenvalue from the response. That's your OAuth Refresh Token. The connector uses it to obtain access tokens itself, so you don't need theaccess_tokenvalue.
Step 3: Find your ad account ID
In Reddit Ads Manager, open Business Manager > Assets > Ad Accounts. The ID appears under the ad account's name and starts with a2_. You can also call List Ad Accounts By Business with the token you just created.
Step 4: Configure the source in Airbyte
-
Under Authentication, choose one option:
- Authenticate with Reddit: Select Authenticate your Reddit Ads account, sign in to Reddit as a user who can access the ad account you want to sync, and select Allow.
- Use your own Reddit app: Enter the OAuth Client ID, OAuth Client Secret, and OAuth Refresh Token from Steps 1 and 2.
-
Enter the Ad Account ID from Step 3.
-
Set the remaining optional fields:
- User Agent: Optional. A descriptive user agent in the format
platform:app_id:version (by /u/username). For example,airbyte:reddit-ads-sync:v1.0 (by /u/your-username). If you leave it empty, the connector sends an Airbyte user agent. Reddit heavily throttles default user agents such asPython/urlliborJava. - Start Date: Optional. The earliest data to sync, in RFC 3339 format with a UTC offset, such as
2024-05-11T00:00:00Z. It applies to the incremental streams: it's the earliestmodified_atforad,ad_group, andcampaign, and the earliest report date for the four report streams. If you leave it empty, those streams start 24 months before the current sync. - Number of Concurrent Workers: Optional. The number of requests the connector runs in parallel, from 1 to 10. The default is 3. Higher values can speed up syncs, but they make Reddit rate limiting more likely.
- Report Lookback Window (Days): Optional. How many whole days before the last synced report date to re-read on each incremental sync, from 0 to 30. The default is 7 days.
- User Agent: Optional. A descriptive user agent in the format
Supported streams
| Stream | API endpoint | Primary key | Incremental cursor |
|---|---|---|---|
ad_account | GET /api/v3/ad_accounts/{ad_account_id} | id | None |
ad | GET /api/v3/ad_accounts/{ad_account_id}/ads | id | modified_at |
ad_group | GET /api/v3/ad_accounts/{ad_account_id}/ad_groups | id | modified_at |
campaign | GET /api/v3/ad_accounts/{ad_account_id}/campaigns | id | modified_at |
ad_account_report | POST /api/v3/ad_accounts/{ad_account_id}/reports | account_id, date | date |
campaign_report | POST /api/v3/ad_accounts/{ad_account_id}/reports | campaign_id, date | date |
ad_group_report | POST /api/v3/ad_accounts/{ad_account_id}/reports | ad_group_id, date | date |
ad_report | POST /api/v3/ad_accounts/{ad_account_id}/reports | ad_id, date | date |
All eight streams read from the single ad account you configure. ad_account supports full refresh; the other seven streams also support incremental sync. To sync more than one ad account, create one source per account.
campaign_report, ad_account_report, ad_group_report, and ad_report return one row per campaign, account, ad group, or ad per UTC day, respectively, with delivery and conversion metrics such as impressions, clicks, spend, reach, frequency, and per-objective conversion counts and values.
Sync behavior and limitations
- Report metrics use micro-units. Reddit returns
spend,cpc,cpv,ecpm, and theconversion_*_ecpametrics multiplied by 1,000,000, and theconversion_*_total_valuemetrics multiplied by 100. The connector loads these values unchanged, so divide them in your destination before reporting on currency amounts. - Recent report data changes after it lands. Reddit says metrics can take up to 6 hours to stabilize, and it applies late conversions for earlier hours or days in a daily refresh. Each incremental report-stream sync re-reads the last synced date plus the configured Report Lookback Window (Days). Conversion metrics follow your ad account's attribution window, so raise the lookback to cover it, up to 30 days, if you need late conversions on older days. Use Incremental | Append + Deduped so re-read rows replace the earlier ones. The row for the current UTC day stays partial until a sync runs after that day ends.
- Report streams re-read days without data. A report stream saves the date of the latest row it read as its state. If an ad account has no delivery after that date, each incremental sync requests all later days again, and an account that has never had delivery requests the full 24 months on every sync.
- Refresh
campaign_reportafter upgrading from a version earlier than 0.3.0. Earlier versions requested report windows with gaps, so any sync that covered more than three days, including the first sync, left some dates missing or partial. Each incremental sync also re-sent the row for the day before the last synced date with only that day's last 7 hours of data. The lookback window only corrects recent days, so run a refresh with Refresh stream and retain records. In Incremental | Append mode, keep the latest row for eachcampaign_idanddate. Don't use Refresh stream and remove records or Clear: Reddit only serves the last 24 months of report data, so older rows can't be synced again. Full refresh syncs correct themselves on the next sync. - Report history has a limit. Delivery data goes back 24 months, and reach and frequency data start in June 2024. If you set Start Date earlier than 24 months ago, report streams still start 24 months before the current sync, because Reddit rejects requests for older report data.
ad,ad_group, andcampaignaren't clamped. - Report streams use fixed breakdowns. Each report stream breaks metrics down by its entity ID and UTC date.
ad_group_reportandad_reportare also broken down by campaign, so their rows includecampaign_id.ad_account_reportsetsaccount_idto your Ad Account ID. To get the ad group of anad_reportrow, joinadonad_id. Breakdowns such as country or region, and custom conversion events by name, aren't available. - Versions earlier than 0.0.9 synced only the first page of each stream. Reddit returns the next page token URL-encoded, and earlier versions passed it back without decoding it, so every stream stopped after its first page. If you upgraded from an earlier version and records are missing in your destination, clear the affected streams and run a new sync.
- Start Date filtering happens after the fetch. For
ad,ad_group, andcampaign, the Reddit API has no server-side filter onmodified_at, so the connector requests every record in the ad account on each sync and discards those modified before your cursor value. Sync duration scales with the size of the account, not with the amount of new data.
Performance considerations
Reddit applies rate limits per Reddit app, authorized user, and endpoint group, so sources that use the same app and Reddit user share those limits. The connector calls endpoints in two groups:
| Reddit rate limit group | Endpoints | Streams | Quota |
|---|---|---|---|
ads-campaign-management-read | Get Ad Account, List Ads, List Ad Groups, List Campaigns | ad_account, ad, ad_group, campaign | 400 requests per 60 seconds |
ads-reporting | Get A Report | ad_account_report, campaign_report, ad_group_report, ad_report | 60 requests per 60 seconds |
The connector throttles its own requests to stay within these quotas. It only counts the requests it makes itself, so other sources that use the same app and Reddit user can still push it over the limit. Each report stream requests contiguous three-day UTC windows per call, so a long backfill makes many report calls.
By default, the connector runs up to 3 requests in parallel. You can change this with Number of Concurrent Workers, up to a maximum of 10. If syncs hit rate limits often, lower this value.
The connector retries 429 and 5xx responses up to 10 times with exponential backoff. For the full list of policies, see Reddit's rate limiting documentation.
The connector reuses an access token until it expires, then refreshes it. If a request still fails with 401, the connector refreshes the token and retries that request. If Reddit returns a new refresh token during a refresh, the connector saves it to the source configuration, so you don't have to rotate it yourself.
Troubleshooting
| Error | Cause and fix |
|---|---|
| Refresh token was rejected by the OAuth provider | Reddit's token endpoint rejected the refresh token or the client credentials. If you use Authenticate with Reddit, select Authenticate your Reddit Ads account again. If you use Use your own Reddit app, confirm the OAuth Client ID and Client Secret, then get a new refresh token with duration=permanent. |
| Reddit denied access to ad account (HTTP 403) | The refresh token lacks the adsread scope, or the Reddit user who authorized it can't access this ad account. Re-authenticate, or get a new refresh token with the adsread scope, while signed in as a user with access to the ad account. |
| Reddit could not find ad account (HTTP 404) or Reddit rejected the ad account ID | The Ad Account ID is wrong. Find your ad account ID, which starts with a2_, and update the source. |
| Reddit rejected the request as invalid (HTTP 400) | Reddit rejected the request for a reason unrelated to the ad account. The error message includes the explanation Reddit returned. |
Configuration
| Input | Type | Description | Default Value |
|---|---|---|---|
credentials | object | Authentication: Authenticate with Reddit (auth_type: OAuth2.0) or Use your own Reddit app (auth_type: OwnApp). | |
credentials.client_id | string | OAuth Client ID. The client ID of your Reddit app. | |
credentials.client_secret | string | OAuth Client Secret. The client secret of your Reddit app. | |
credentials.refresh_token | string | OAuth Refresh Token. | |
start_time | string | Optional UTC start date applied to the incremental streams, in YYYY-MM-DDTHH:MM:SSZ format. A value earlier than 24 months ago is clamped for report streams, because Reddit only serves report data for the last 24 months. | 24 months before the current date |
user_agent | string | Optional. User Agent. A unique and descriptive user agent string in the format: platform:app_id:version (by /u/yourusername). If empty, the connector uses an Airbyte user agent. | airbyte:source-reddit-ads:v1 |
ad_account_id | string | The Reddit Ads account ID to sync, shown in Reddit Ads Manager, for example a2_abc123. | |
num_workers | integer | Number of concurrent workers. Higher values speed up syncs but increase the chance of Reddit rate limiting. | 3 |
report_lookback_window_days | integer | Optional. Number of whole days before the last synced report date to re-read for late-attributed conversions and restated metrics (0–30). | 7 |
Streams
| Stream Name | Primary Key | Pagination | Supports Full Sync | Supports Incremental |
|---|---|---|---|---|
| ad_account | id | No pagination | ✅ | ❌ |
| ad | id | DefaultPaginator | ✅ | ✅ |
| ad_group | id | DefaultPaginator | ✅ | ✅ |
| campaign | id | DefaultPaginator | ✅ | ✅ |
| ad_account_report | account_id.date | DefaultPaginator | ✅ | ✅ |
| campaign_report | campaign_id.date | DefaultPaginator | ✅ | ✅ |
| ad_group_report | ad_group_id.date | DefaultPaginator | ✅ | ✅ |
| ad_report | ad_id.date | DefaultPaginator | ✅ | ✅ |
IP allow list
If you use Airbyte Cloud and your organization restricts access to specific IPs, add the Airbyte Cloud IP addresses to your allow list.
Reference
Config fields reference
Changelog
Expand to review
| Version | Date | Pull Request | Subject |
|---|---|---|---|
| 0.4.0 | 2026-10-16 | 88964 | Add ad_account, ad_group, ad_account_report, ad_group_report and ad_report streams; show Reddit's field-level reason in HTTP 400 error messages |
| 0.3.0 | 2026-10-15 | 88963 | Fix campaign_report windows that skipped one day in every three and overwrote earlier days with partial data; add a configurable report lookback window |
| 0.2.0 | 2026-10-14 | 88145 | Add OAuth (Authenticate with Reddit) for Airbyte Cloud with automatic config migration to a credentials object; make User Agent optional |
| 0.1.1 | 2026-10-06 | 88015 | Update dependencies |
| 0.1.0 | 2026-10-05 | 87043 | Show actionable errors for rejected refresh tokens or client credentials, invalid ad account IDs, and 400/403/404 responses, add a client-side API budget and configurable concurrency, add suggested streams, make spec titles human-readable, and add unit tests |
| 0.0.12 | 2026-09-29 | 87338 | Update dependencies |
| 0.0.11 | 2026-09-22 | 86789 | Update dependencies |
| 0.0.10 | 2026-09-15 | 86198 | Update dependencies |
| 0.0.9 | 2026-09-09 | 84850 | Fix pagination so every stream syncs all pages instead of only the first |
| 0.0.8 | 2026-09-08 | 85629 | Update dependencies |
| 0.0.7 | 2026-08-18 | 84348 | Enable acceptance test suite with GSM test secrets |
| 0.0.6 | 2026-08-18 | 84725 | Update dependencies |
| 0.0.5 | 2026-08-11 | 84089 | Update dependencies |
| 0.0.4 | 2026-08-04 | 83603 | Update dependencies |
| 0.0.3 | 2026-08-04 | 83266 | Stop refreshing the access token before every request, and refresh it before retrying a 401. Make start_time optional, apply it to all three streams, and default it to 24 months before the sync; clamp campaign_report to Reddit's 24-month reporting window |
| 0.0.2 | 2026-07-28 | 83100 | Update dependencies |
| 0.0.1 | 2026-07-27 | 81399 | Initial release by @Ella6882 via Connector Builder |