Why the Reddit Ads API was harder than it looks
Why the Reddit Ads API was harder than it looks<br>We budgeted a day to integrate Reddit Ads and it took two weeks. Here are the refusals we hit, why our tests never caught them, and how it ends.<br>GT<br>Growomat Dev Team<br>Published August 10, 2026·Updated August 11, 2026·9 min read
Share
On this page
On this page
Share
We assumed integrating Reddit ads would be a walk in the park. Boy, were we wrong.
After successfully setting up Google Ads and Microsoft Advertising with their stable interfaces, Reddit was the obvious next step for Growomat - especially since both our dev team and our users love the platform.
Things looked promising at first. We spent a week building the platform adapter and mock services, estimating that hooking up the live APIs would only take about a day.
Two weeks later, and a lot more hair and sleep loss, we realised it was more<br>complicated than it looked.
If your team is integrating with the Reddit API, here are some lessons you can<br>learn from.
The Reddit API is a new interface
Claude and Codex know well-established APIs like Google Ads inside out. Reddit's<br>they don't - there simply isn't much Reddit API material in their training<br>data.
No problem - they should just pull the latest API documentation from the Reddit<br>developer portal so they know what to build?
Oops - next problem, the Reddit developer portal blocked our automated tooling<br>from retrieving the documentation.
We resolved this by downloading the full Reddit OpenAPI spec file and using that<br>file locally for integration. This is a good learning to speed things up instead<br>of a ton of online calls.
Once we got these basic interface issues sorted, we got to the next set of<br>issues - weird error messages.
Weird error messages
Because we care about our users' ad budgets and controlling their spending, we<br>set budget optimization on (is_campaign_budget_optimization = true).
However, this gave an error when creating the campaign:
Cap is required for Cost Cap campaigns
Campaigns with budget optimization turned on have to send bid_strategy and<br>bid_type. Ours sent neither, because the user hadn't configured any bidding at<br>all. What came back wasn't "bid_strategy is required".
So we thought - let's just set the automatic bidding strategy,<br>MAXIMIZE_VOLUME. However, it isn't; it is the Cost Cap product, and it wants<br>a cap. The automatic one is BIDLESS, which is what Reddit uses in every<br>template in its own campaign setup guide.
The OpenAPI description, meanwhile, says the cap is "Optional when bid_strategy<br>is MAXIMIZE_VOLUME", contradicting both the guide and the live API. Believing it<br>cost us a lot of debugging.
So when a platform complains about a field you never set, go looking for a<br>default you never chose. And when the reference disagrees with the vendor's own<br>worked examples, back the examples; they usually describe the system that's<br>actually running.
Absent, null and "inherit" are three different things
Campaign accepted. We moved down a level, and Reddit refused the ad group:
missing required attributes: bid_strategy, bid_type
Under budget optimization the money lives on the campaign, so why does the ad<br>group need bidding fields at all?
Because bid_strategy and bid_value have to go up as explicit nulls, and that<br>null is doing real work. It's how an ad group says "inherit from the campaign".<br>Leaving the keys out isn't the same as sending them empty. Our compact()<br>helper strips nulls, so we had to attach these fields after it ran.
Then the same distinction bit us from the other direction:
Ad group field optimization_goal must match the campaign value.
We were computing that field in two places under two different rules, so a<br>clicks campaign went up with no goal at all while its ad group confidently<br>asserted one.
The fix wasn't a better default. It was to send nothing at all in the normal<br>case. Campaigns already on Reddit were created before we sent that field and<br>hold whatever Reddit derived for them, so anything we invent might disagree.<br>Sending nothing can't disagree with anything, and the ad group inherits.
Lesson: find out whether your API distinguishes "key absent" from "key present,<br>value null". Your JSON serializer already has an opinion, and you probably<br>haven't read it.
One payload for create and update
We compiled a single payload and used it for both verbs. Two bugs came out of<br>that, and both stayed invisible until something got pushed a second time.
The first: our update carried create-only fields. Reddit accepts objective and<br>is_campaign_budget_optimization when you create a campaign, and rejects the<br>entire PATCH if either one comes back. Every update of a linked campaign 400'd.
The second is nastier. Our model has no start date, so start_time was<br>synthesized as "now" during translation, which is correct exactly once. Every<br>later PATCH synthesized a fresher "now" and marched the campaign's start past ad<br>groups created minutes or days...