Automation Pipeline6 min read

My Instagram App Cards Were Still Selling Web Apps

Told to promote only store apps, I audited four channels and found just one still pushing web apps. Swapping its content pool wasn't enough. Language, price claims, and tone all came along with it, and a running experiment window had to be reopened.

#instagram#localization#itunes-lookup#automation#experiment-design
Diagram of four promotion channels where only the Instagram app cards still read the web app manifest
Only one place needed fixing, but language, price, and tone were all tangled up in it.

The instruction was one line

'No need to promote the web apps. We're only promoting Android/iOS apps.' That was the whole instruction. There were four promotion channels: a Threads account, a Facebook page, app download Shorts (ko/en/ja), and app cards on three Instagram accounts.

I expected to rip everything up. Then I listed which content pool each channel reads, and three of them were already store-only. Threads had its schedule cleaned up on 09-22, Facebook started on the same pool on 09-23, and the Shorts had read the store app list from day one. Only the Instagram app cards were still reading the web app manifest and posting three web apps a day.

At that point it looked like a one-line fix: change the pool file path. It wasn't.

In your own auto-publishing pipeline, if you swap a content pool, are you sure the language, pricing, and tone attached to it change correctly too?

Swap the pool and Korean cards go to overseas accounts

The store app pool (apps_hook.json) was Korean only. App names, hook lines, and scene captions were all ko. Wired in as-is, it would have sent Korean cards to the EN and JA accounts. The only other listing file was also Korean, taken from the Korean App Store.

The captions were also telling two lies

The old app card caption said, roughly, 'Try it free now via the link in profile.' Two things were wrong. First, the tone: it was web-app phrasing, telling people to try the app right in the browser. Second, the price: it called every app free, and some store apps are paid. The hashtags were from the web quiz world too: #무료테스트, #freetest, #無料診断.

If I had only swapped the pool, the cards would have pitched store apps in web-app language and called paid apps free.

What would you do? Run the Korean copy through a translation API, or pull the copy each country's store already has?

The source of truth was already on the store

I went with the second. iTunes Lookup needs no authentication and works per storefront. Calling ?id=&country=kr|us|jp returns that country's listing title, description, and price as written. I wrote that copy myself in the store console, so it is also the right source for card text.

The new module, store_listings.py, has a 7-day cache and a demo() self-check, and nothing more. It follows one rule: a failed lookup never becomes an empty string. If the result is None, that app is dropped from the pool. Missing English or Japanese copy is never filled in with Korean.

One result from a real run across 3 locales and 48 apps:

ko  Composly / 좋은 사진을 위해 복잡한 촬영 용어를 배울 필요는 없습니다…
en  Composly / You do not need to learn photography terms to frame a better shot…
ja  Composly / 写真用語を覚えなくても、よりよい構図で撮影できます…

I opened the rendered frame of the JA card to check it. No characters rendered as empty boxes, the CTA read 無料で試す, and the brand line read App Store.

Captions switched to store phrasing (App Store에서 받아보세요 / Get it on the App Store / App Storeでダウンロード). Links in captions aren't clickable, so the path still goes through the profile link. A price claim appears only when the listing itself shows the app is free (listing.free). This isn't a line in the prompt telling the model not to say free. It's a gate on the data. Hashtags changed to #앱추천, #앱스토어, #appstore, #iPhoneアプリ.

There are two regression tests. One fails if a paid app's caption contains a price word. The other fails if the pool falls back to the web app manifest.

The cost: reopening a running experiment window

The fix had a cost. A gate measuring Instagram app card conversion to Reels was running over a 09-20 to 10-17 window, with the verdict due 10-18. The fix landed on day four. Copy, hashtags, and CTA had all changed, so I had altered the very thing being measured in the middle of the window. What was observed from 09-20 to 09-23 is not the same thing as what comes after.

So I reopened the window as 09-24 to 10-21, with the verdict moved to 10-22. The threshold (reach of 15 or more per post), the baseline (11.1), and the window length are unchanged. Only the start date moved. That doesn't lower the bar. It throws away four days of data. It's the same rule I used earlier when I closed out a different secondary metric that had been confounded.

Self-check

  • Have you ever put every promotion channel in one table next to the content pool it reads? When a policy changes, there may be fewer places to fix than you think, or they may be hiding.
  • Do the price claims in your captions and CTAs come from data, or are they hard-coded in a template?
  • When a lookup fails in a multilingual pool, what gets published? Is it being filled with an empty value or the default language?

The honest part

This isn't finished. The Instagram bio link still points to the web hub. The card says get it on the App Store, then the link opens a web app, so the path breaks there. Next is pointing the bio link at an app download page, but that's an account setting, so a person has to do it by hand. My pipeline can't. I also don't know whether any of this helped Reels conversion. The reopened window's verdict is due 10-22, and until then I'm not writing down hopes or conclusions.

Related