Pacing Algorithm for Advertising Campaigns and Inventory Allocations

I’ve was trying to figure out what to do with my Sunday. My options were: build a little header bidding ad server plugin for WordPress; run, sleep and eat; or write up some blog post on a pacing algorithm, because people still seem to be producing crappy ones. Since you’re reading this, you can probably guess which choice I made. I mean, it’s not the first post I’ve written on the subject.

It showed up again last week. I didn’t expect it, but I guess I never do. A saw-tooth pattern on a chart, indicative of a capping of sorts. A chart that says, “I want a thing to happen, but only so much.” In this case it was a traffic allocation. This was a surprise.

A little history

Most of the time when I run into a bad pacing algorithm it’s in the form of a campaign trying to limit itself. It only needs to acquire a few thousand impressions every five minutes, for example. So the hastily written algorithm might divvy up the impression allocation into five minutes buckets. Effectively that’s 12 buckets every hour. So it takes an hour’s worth of impression needs and divides it by twelve. One twelfth of the impressions are purchased every five minutes. Unfortunately at that point it switches to a simple counter that says, “for the next five minutes buy impressions until the number purchased reaches 1/12th of what I need in this hour.”

You end up with a purchase graph that looks like this.
Sawtooth Pattern exposes a bad pacing algorithm

See that blue spiky thing? That’s the one that’ll get ya.

The algorithm has no idea how much inventory is available for the campaign. In many cases it is plentiful. So the campaign serves it’s entire five minute allocation of impressions in the first minute. Then the buying goes dormant for four minutes until the next allocation is released and we start the dance all over again.

Apparently this can happen on both ends of the equation. A media owner or seller can cap the amount of media made available to particular technology partners. In much the same way a campaign pacing can reveal itself with the buying pattern, an impression allocation can show up in a similar way.

Details on the cause

How might one come to write such a curiosity? If this happens to speak to you, then yes, I’m talking to you. You’ve written a odd thing. In an effort to really speak to you about this, I’m doing my best to get inside your head.

Whether due to inexperience, or lack of thought about the magnitude of the problem you were about to introduce as your solution to another problem, or a lack of faith in an alternative approach, you thought this might be a good solution. The thought process probably went something like this:

We have a campaign or impression allocation for the month. Let’s start by dividing that by 30. Now we have a day. Let’s break that down by 24. Now we have an hour. Okay – well, buying all the impressions in an hour right up front might lead to a big spike at the beginning of each hour, right? So, well… let’s just divide it by 12 and we have five minute intervals. That can’t possibly cause a problem, right?

Impact

Problems are what it causes. Not just for you, but for your partners, and their partners, and they tell two friends and so on, and so on. Imagine if everyone did this. We’d have a sixty second blitz of liquidity every five minutes followed by four minutes of empty ad space.

We’d have intensely busy servers to handle the inventory for one minute, and then absolutely idle machines for the other four. Now you’re thinking, “Wait, would I need five times the infrastructure to handle that momentary load?” Yes, yes you would. And so would your partners, and they’d tell two friends… you get the picture now, right? It’s a snowball turned avalanche.

Fixing your pacing algorithm

You might be asking yourself, “How then might one go about fixing this, or rather, build a proper pacing algorithm in the first place?” I’m so glad you’re thinking about this, because I think about it all the time. I think about it a lot when I see some crap, saw-tooth graph. But even when one of those is not available, I still think about it. I imagine the graph just so I can get mad at it.

The answer is akin to flipping a coin, as my friend Dr. Neal Richter might suggest. Rather than basing your pacing algorithm on counting and capping, base it on a percentage allocation of impressions. Instead of serving til you hit the cap, flip a coin and only serve when it lands on heads. Of course, this simplified model only works if you need 50% of the inventory (or are allocating 50% of the inventory to a partner). For a moment though, imagine that it’s a D&D die instead of a coin. Then imagine that you only need to occasionally win, like you need the equivalent of two of the sides of that die, relative to the other 18. Winning twice in every twenty roles is just about perfect.

How to apply a simple percentage based pacing

Mathematically you’d want to boil this down to an equation so that you can write code against it. I’m not going to challenge you with a series equation here. You’ve already written a questionable algorithm, let’s not test the deeper mathematical waters just yet.

Instead I’ll write something akin to pseudo-code. Let’s say allocation requirements are 15% of the available inventory. Your algorithm should do something like:

  • Choose random decimal number (float) between 0 and 1.
  • If that number is less than 0.15, then serve.

Almost anyone can write that into code. It’s like two or three lines. Nice, right?

Determine the volume

At this point you should have figured out that you need to know how much inventory is available in order to calculate the percentage that your campaign or inventory allocation needs. Good. You’re asking the right question.

Fortunately you don’t need to burn the house down to make a perfect forecasting solution in order that you might have a better pacing algorithm. You could, I suppose, if you wanted to. Nothing’s quite as nice as a perfect forecasting tool. It’s like a fluffy pillow or a warm blanket. You can cozy up on the couch knowing the exact availability of the thing you’re looking for.

My friends all love to quote Akin’s laws of spacecraft design. In this case: 14. (Edison’s Law) “Better” is the enemy of “good.” So don’t run down the rabbit hole of a perfect forecasting tool.

How much inventory do you have left?

You simply need to determine how much volume you’ve got left available for your campaign’s duration. For that, you just look at what happened in the past, like yesterday even. “Hey, how many impression did I have yesterday? 1,000,000. Cool. So let’s just assume I’ll have 1,000,000 every day.” Seriously, it’s that simple. I mean, you can make it more complicated. Look at last week and divide it by seven, or even last month and divide it by 30. You did that before, remember. It produced a piece of garbage and cost everyone a 5X on their AWS bill, but hey, “learning.”

So, if your campaign needs to run for three weeks it looks like you’ve got about 21,000,000 impressions available to it. If you’re campaign only needs 3,000,000 of those, then you’re allocation percentage is 14.3%.

3,000,000 / 21,000,000 = 0.142857142857143; about 14.3%

Seems like I’m writing a lot of fluff in here for a few simple equations. I’ll be sure to highlight the important bits. Most of us are skimming posts these days and miss things. I guess for news it’s okay. Skimming posts like this might lead you to write a “pacing algorithm,” but maybe not the right one. If you are skimming, you might also be missing all my snark. Sad really. My snark is top-shelf.

Varying the time frame

One of the fun parts about pacing with this algorithm is that you get to follow the rise and fall of the traffic tide with your allocation. It just naturally happens! If there’s more traffic at noon, you’re still buying your 14.3% of it. When traffic dwindles at 11PM (23:00) your just buying a bit, but still 14.3%. You get to follow the ebb and flow of internet traffic for free!

Your old algorithm didn’t do that, did it? If it did, you had to do some crazy coding gymnastics to make it work. I tried to help you avoid all this, and I guess I failed some of you. This other post is on the same subject, but it had those tricksy series equations in it.

Varying the addressable audience

We’ve got three more points to cover before you’re ready to go. Firstly, the audience factor. Really this can be any sort of targeting parameter applied to the campaign or inventory allocation. Basically you want to look at how much of the inventory had that parameter in the past, to determine the allocation percentage. It will modify your equation’s denominator. Instead of 21,000,000 impressions you will have fewer. Maybe your campaign is only aiming at shoe enthusiasts and your system only expects to see 8,000,000 of those in the next three weeks. That means your allocation percentage is now 37.5%.

3,000,000 / 8,000,000 = 0.357; 35.7%

Competing campaigns (and how to address)

Now you’ve got your campaign humming along buying 37.5% of all shoe enthusiast impressions. This a really fantastic progress. Consider where you started. It’s really quite astonishing. Nothing can stop you now, except maybe another campaign that is also, for whatever reason, buying up your shoe enthusiasts. Holy Moly! Now what?

Are you supposed to look at all the campaign targeting and try to determine which campaigns are targeting the same users that your campaign is targeting? Good lord, what a mess that would be, right? Can you imagine the (Mom’s) spaghetti code you’d have to write in order to accomplish such an omniscient technology? That’s some Nobel Prize winning ad-tech is what that is. (Yeah, I’m totally in the “dash” camp of ad-tech. Deal with it.)

Don’t overthink it

There must be a simpler solution. There’s totally a simpler solution. All you need to do is incorporate the win rate (or render rate) into your calculation. Imagine you are only winning 65% of the time. Oh, snap! That’s gonna hit the denominator again because the reality is that you’ve only got 65% of your 8,000,000 impressions available to your campaign now. So I guess the math gets a little trickier now.

3,000,000 / (8,000,000 * 0.65) = 0.576923076923077: about 57.7%.

This just makes your campaign buying a bit more aggressive than it usually is. It’s the equivalent of giving your campaign a dagger to go all stabby on the other campaign.

Of course, an alternative here would be to raise the price your campaign is paying for the inventory. I’m not going to assume your system is in a bidding environment. If it were, you could also pull the pricing lever to actually change the win rate to something more to your liking. That’s where things get a little more complicated. You have to monitor how the new prices change the win rate and populate a lookup table so you know what happens at different prices.

For this exercise let’s keep it simple. I’m thinking that you’re just trying to make pacing decisions with non-monetary levers to pull.

Now you’ve got a complete algorithm that, in a perfect environment, will conclude your campaign exactly at the correct allocation without over or under performing. Then again, when was the last time you saw perfection?

Guardrails

Total Cap

Much like the Price is Right, you want to get the exact amount without going over to win that bonus $100. So you need to add a little logic to your pacing system to put the brakes on when the campaign hits the goal. Ad-tech systems are kinda big these days with servers spread out all over the world. Sometimes you just need a handbrake to stop buying on the thing when it’s done what it needed to do. So add that.

Overburn

The other thing you’ll want to do is run your campaign hot. Add five percent to the numerator. If your total cap function is working then you should hit the hard stop in advance of the final hours of your end date. I’m saying five percent for simplicity’s sake. Maybe you over burn at one percent at the beginning of the campaign and five percent toward the end, with some graduated increase over the weeks or months of the run. Heck, maybe you run hot at the beginning, slow in the middle and hot at the end.

I asked my good friend Neal. He says overburn for the first 80% of the campaign’s budget, then you can let it chill on the beach for the last 20%. He didn’t use those words, but the sentiment is there.

I don’t know. Do what feels natural. Fly casual.

Recalculate again and again and again

Absolutely reevaluate your campaign hourly, every half hour maybe? Do it often. Redo the numbers:

  1. How much inventory is available in the time I have left?
  2. How much of that inventory does my campaign need?
  3. What’s my win rate?

The equation

In case you want to start adding more fun stuff to the equation here’s the template to follow:

allocation percentage = campaign goal / (available inventory * win rate)

You’ve finished and you’re winning

If you’ve made it to the end here, good on ya. Thank you for reading. I hope this helps, really I do. I know I’ve been hard on you for the questionable decisions you’ve made in the past, but that’s all behind us now. Let us look forward to a bright future where your campaigns are humming along, or your inventory allocations are distributing smoothly. Turns out that unbeknownst to any of us, you’ve just implemented a sensor feedback proportional controller. Tell your grandparents, they’ll understand.

If you enjoyed this post, pick something out something nice from my Amazon wish list and hit the one-click.

Leave a Reply