# How I run six figures of paid ads **TL;DR:** This is part two of how Fern leverages AI across our marketing. Part one was perfecting [our Midjourney prompts](/post/midjourney-blog-art) for on-brand assets. This one is the engine behind our paid ads: I run them out of our marketing site repo with a series of Claude agents handling the repetitive work. So far it has doubled our qualified demos, taken about 90% of the search impression share on our brand and competitor terms, and lifted site traffic 202%. Most of our prospective customers are weighing Fern against our competitors in the Docs and SDKs space. To make sure Fern stays in those conversations, we have to spend on paid ads. I run those paid ads at Fern as a team of one with a media-buying partner ([Outshine](https://www.outshine.com/)). Our paid marketing has now scaled to a six-figure monthly budget. The way I keep pace is by leveraging a series of marketing agents that handle much of the repetitive parts. This is our process, a loop where every week's results feed back into the next round of campaigns: ## 1. Plan the campaign A campaign is a set of ads on Google and LinkedIn, each pointing to a custom landing page built around one angle. Every campaign starts with who I'm trying to reach and what I say to them. I open Claude in plan mode and we work out the angle, the single reason a campaign exists. It might be a competitor shutting down, like [Stainless](/lp/leave-stainless), or a pain point teams already feel, like [hand-maintaining SDKs](/lp/sdk-generator). Right now I run seven campaigns, each pointed at a different angle and audience. Plan mode is how I drive this many campaigns at once. When I started this project, I manually seeded these campaigns, but over time I distilled a series of questions that gets me to the core marketing angle. Now I set off agents in parallel to seed campaigns. Each agent runs the same skill, a short markdown file that has Claude interview me until we land on one angle. Roughly: ```md title="launch-campaign/SKILL.md" Find the campaign's angle. Ask one question at a time: - Who are we targeting, and what are they struggling with today? - What are they using or considering instead of Fern? - Which single Fern capability solves that best? - What is the sharpest claim we can make, and what backs it up? Then state the angle in one sentence. ``` Take the `sdk-generator` campaign. The angle is that teams are tired of hand-maintaining SDKs across many languages, so I lead with "stop hand-writing SDKs" and send them to the custom `/lp/sdk-generator` landing page: nine idiomatic SDKs from one spec. ## 2. Gather everything in one folder Once I've set the angle, I scaffold the whole campaign in one folder inside our marketing site's repo. By the time a campaign ships, that folder holds one file for each step below: the brief, the copy, the images, and the results. ``` sdk-generator/ brief.md # who we target and the angle copy/ linkedin.md # the LinkedIn ad copy google.md # the Google ad copy creative/ README.md # notes and rules for the images SG-LI-SI-01.md # one file per ad exports/ # finished images land here results/ performance.csv # the daily numbers learnings.md # what to keep, cut, and try next ``` The directory holds the plan, the ad copy, the images, and the results. Nothing lives in an agency portal or a spreadsheet the agent has to go hunting for. Every ad also gets a short, stable name that follows it everywhere: on its copy, on its image, and on every row of results. For example, `SG-LI-SI-01` reads as the `sdk-generator` campaign, a LinkedIn single image, number 01. That same name rides in the ad's link, so every click reports back against it: ``` ?utm_source=linkedin&utm_medium=cpc&utm_campaign=sdk-generator&utm_content=SG-LI-SI-01 ``` So when a number moves, I can trace it straight back to the exact ad that caused it. Here is what the files actually look like. The LinkedIn copy file has one entry per ad variant, with every field the platform needs: ```md title="copy/linkedin.md" ## SG-LI-SI-01 — Stop hand-writing SDKs - Creative headline: "Stop hand-writing SDKs." - Intro: "Customers judge your API by its SDKs. Fern generates idiomatic clients in 9 languages from one spec, with retries, pagination, and tests built in." - Headline: "The SDK generator for your API" - Description: "9 languages from one spec. Book a demo." - Destination: `https://buildwithfern.com/lp/sdk-generator?utm_source=linkedin &utm_medium=cpc&utm_campaign=sdk-generator&utm_content=SG-LI-SI-01` ``` The Google file is structured as a responsive search ad: a pool of headlines and descriptions that Google mixes and matches. I give it 15 headlines and 4 descriptions and let the platform find the winning combinations: ```md title="copy/google.md" ## SG-GS-RSA-01 — Core terms Headlines (≤30 chars each): | SDK Generator | API SDK Generator | | Generate SDKs From OpenAPI | 9 SDK Languages, 1 Spec | | Open Source Generators | Retries & Pagination Built In | | Book a 30-min Demo | ... | Descriptions (≤90 chars each): | Generate idiomatic SDKs in 9 languages from one OpenAPI spec. Tests built in. | | Open source generators. Runs in your CI. Published to every registry on autopilot. | ``` The creative brief is one file per ad. It tells me (and Midjourney) exactly what to build: ```md title="creative/SG-LI-SI-01.md" # SG-LI-SI-01 — Stop hand-writing SDKs - Platform: LinkedIn Single Image, 1080×1080 - Headline: "STOP HAND-WRITING SDKS." - Sub-row: "9 languages | one spec | tests built in" - Language marks: ts, py, go, java, c#, php, ruby, swift, rust — native colors - Background: Midjourney scene at 50% opacity, dark green overlay at 45% Midjourney prompt: A single stone path branching into nine parallel trails across a terraced hillside at dawn, Hiroshi Yoshida style, muted earthy colors, analog grain --v 7.0 --raw --exp 100 --ar 9:16 ``` ## 3. Build the landing page Our ads guide users to a custom, focused landing page built for that campaign's angle. Click the `leave-stainless` ad and you land on [a page only for teams migrating off Stainless](/lp/leave-stainless). Click the `sdk-generator` ad and you land on the page below, with one thing to do: book a demo. I set up a Claude skill that automates this landing page creation. It builds each page from a few templates and the same design system as the rest of buildwithfern.com. ![The /lp/sdk-generator landing page, built from the same components as the main site, with the nav stripped to a single Book-a-demo CTA.](/post/paid-ads-agent/lp-sdk-generator.avif) *See it live at buildwithfern.com/lp/sdk-generator* The landing page is crucial for conversion. It is the most important moment to validate our claim and capture the visitor's information. ## 4. Generate the images Many social platforms (LinkedIn, Reddit, and X) require visual assets alongside the campaign. I've started to automate this too. The goal is for every ad to feel like Fern: calm, a little cinematic, and nature-forward. So the backgrounds are typically subtle landscapes in the style of [Hiroshi Yoshida](https://en.wikipedia.org/wiki/Hiroshi_Yoshida), generated in Midjourney, with our wordmark and brand green laid on top. Every background starts from the same prompt template: ``` [scene in Hiroshi Yoshida landscape style], atmospheric [weather], long-exposure smoothing motion, muted earthy colors, tactile analog grain --v 7.0 --p [your-profile-code] --raw --exp 100 --ar 9:16 ``` To assemble them I built a small tool at a `/ad` url that runs only on my machine. It takes a headline and a background and renders a finished image at every size I need. ![](/post/paid-ads-agent/ad-studio.avif) The first pass is rarely perfect, so I dial it in with the controls on the right, and now and then pull the frame into Figma with the [html.to.design](https://html.to.design/) plugin for a hand-tweak. Then I save the finished image back into the campaign folder. ## 5. Publish and hand off the spend With the copy, the page, and the images in the folder, the campaign is ready to go live. Then, I hand the finished campaign to Outshine, our media-buying partner. Outshine runs the actual buying and bidding inside Google Ads and LinkedIn, which is a full-time job that rewards platform expertise I would rather not own. They own the spend and the day-to-day optimization, and I own the creative strategy. That split is the main reason one person can keep this going. ## 6. Review the results and feed them back Every week Outshine sends back a results file with clicks, spend, impressions, and conversions for each ad, tied to its name. I update the repository with that data and the engine uses what is working, and what is not, to iterate on existing campaigns and improve future ones. ## Results so far The program is working. - **Paid is driving qualified demos.** Our demos have more than doubled since the start. - **We own our search.** Fern now holds about 90% of the search impression share on our brand and on competitor terms, and our brand cost per click has more than halved. - **Awareness is climbing.** Site traffic is up 202% over the period, and our best LinkedIn ad hit a 5.4% click-through rate, nearly 3x the platform average. The honest part: the top of the funnel still needs work. A cold "book a demo" ask converts poorly, so the next phase is running awareness ads to build a retargeting pool, then coming back to that warmer audience with a stronger offer. ## Want the skills? This whole process runs on a handful of Claude skills and one local `/ad` tool, all living in our marketing-site repo. - Grab the whole setup: the [marketing-site template](https://github.com/fern-api/fern-marketing-site-template). - Read how we run the rest of the site the same way: [building Fern's marketing site with Claude skills](/post/building-fern-site). - [DM me on X](https://x.com/kapilgowru) and I will send over the skills, or if you run paid and want to jam on any of this.