Skip to content
App Store
5 min read

Rejected under App Store guideline 2.3.7 for a price in a screenshot

Talkzy was rejected under guideline 2.3.7 for pricing in its screenshots. What was flagged, how I fixed the set, and the check that now guards the listing.

Cover: a phone screenshot set with the last frame, a paywall, crossed out
On this page
  1. What guideline 2.3.7 actually covers
  2. What was in the set
  3. The fix: one commit, four lines of captions
  4. The same fix, applied before the rejection
  5. Turning the lesson into a check
  6. Takeaways

Talkzy is my teleprompter app for iPhone and iPad: your script scrolls right under the camera, it can follow your voice, and every take is saved as its own clip. Its App Store screenshots ended with the paywall, headlined "Fair price. No weekly fees." I thought of it as an honest frame. App Review saw it differently. The submission came back under guideline 2.3.7, for pricing in the screenshots.

This post is the short version of what was flagged, what I changed, and what I now check before anything goes into App Store Connect. The fix itself was small. The interesting part is how many places the word "free" had crept into, and which of them a script can catch.

What guideline 2.3.7 actually covers

Guideline 2.3.7 is the "accurate metadata" rule about names, keywords and the rest of the listing. Among the things it tells you not to pack into metadata is pricing information. Screenshots are metadata. So a price on a screenshot, or a word that is really a pricing claim, is fair game for a rejection, even when the price is correct and the frame is a real capture of the app.

That last point is what caught me out. The paywall frame was not a mock-up. It was a genuine simulator capture of the paywall, framed like every other screenshot. It just happened to show three plan prices and a buy button with the price on it.

What was in the set

Talkzy's iPhone set was nine frames, each a two-line headline and a subline above a device-framed capture, composed by a small Python script (tools/screenshots/compose.py) that renders the captions with headless Chrome. The iPad set was seven frames built the same way.

Three iPhone frames had a problem:

FrameWhat it saidProblem
05 Mirror mode"Flip left–right or upside down for beam-splitter glass. Free.""Free" is a pricing claim
07 Remotes"Bluetooth page-turners and keyboards work in every mode. Free."same
09 Paywall"Fair price. No weekly fees." over the paywall, with plan pricesprices, plus "Free with no watermark" in the subline

The iPad set had the same three ideas: mirror and remotes ending in "Free.", and a paywall frame as number 07.

In my head, "Free." at the end of the mirror and remote sublines was a feature description: these work without paying. Read from the reviewer's side, it is a price, the price being zero.

Talkzy's iPhone screenshot set before and after: nine frames with 05 and 07 marked for "Free." and 09 for the paywall, then eight frames with the paywall gone
Before and after, iPhone set

The fix: one commit, four lines of captions

Because the frames are generated from a list in a script, the fix was an edit to that list and a re-render, not a design session. The diff on the iPhone side looked like this:

txt
- ('05', 'mirror', 'Mirror mode', 'for prompter rigs',
-  'Flip left–right or upside down for beam-splitter glass. Free.', ''),
+ ('05', 'mirror', 'Mirror mode', 'for prompter rigs',
+  'Flip left–right or upside down for beam-splitter glass.', ''),
  ...
- ('09', 'paywall', 'Fair price.', 'No weekly fees.',
-  'Free with no watermark. Pro yearly, monthly or once for life.', ''),

The remotes subline lost its "Free." the same way, and the iPad list got the same three edits. Result: the iPhone set went from nine frames to eight and the iPad set from seven to six. The paywall frame files were deleted from the repo, not just left out of the upload, so nobody (including me in three months) re-uploads them by accident.

Two things I deliberately did not change:

  • The paywall still has a screenshot, just not in the listing. Each in-app purchase in App Store Connect needs a review screenshot of where it is sold. Talkzy keeps a clean paywall capture for exactly that slot. That is private to App Review and is where a price belongs.
  • Pricing stays in the description. An auto-renewing subscription has to state its terms, so the description still names the plans, the free trial on the yearly plan and how renewal works. The rule is about screenshots and the indexed fields, not about hiding what things cost.

Here are three of the frames that shipped after the fix. No prices, no "free", just what the app does:

Three Talkzy App Store screenshots: the prompter over an illustrated camera view, the script editor with sections, and mirror mode with flipped text
Frames 01, 03 and 05 after the fix

The same fix, applied before the rejection

Talkzy's screenshot script was adapted from another of my apps, and Baton's is built the same way. The same morning I went looking for the same pattern elsewhere. Baton, my baby tracker (baton.io.vn), had a last frame headlined "Free forever. Or pay once." with the Plus prices in the subline, and an iPad charts frame whose subline said which part was free. Same commit message, same fix: the paywall frame went, the iPad subline was trimmed, and the iPhone set dropped from ten frames to nine.

It is a cheap lesson to apply twice. One rejection costs a review cycle. Applying the fix to a sibling app before it is reviewed costs a few minutes.

Turning the lesson into a check

A rejection that only lives in my memory will happen again. So the same day, the metadata checker that already validated Talkzy's listing in ten languages got a 2.3.7 section. It reads docs/app-store/locales/*.json and fails when the indexed fields carry anything that looks like a price:

py
# 2.3.7: no price / free / superlative in the indexed fields; no competitor or platform brands.
BANNED = ['free', 'gratis', 'gratuit', 'kostenlos', 'miễn phí', '無料', '免费', '무료',
          'best', 'tốt nhất', ...]
PRICE = re.compile(r"[$€£¥₫]|\d+\s*(k|đ|usd|vnd)\b|\d", re.I)

It checks the name, subtitle and keywords together, because that is what Apple indexes, and it exits with code 1 on any failure. The rule I follow is simple: run it before pasting anything into App Store Connect. The ASO pass I did right after (more on that in ASO in ten locales without paid tools) also took "free", "no weekly plans" and "no watermark" out of the promotional text. The promo field is not what 2.3.7 names, but it is the first thing a reviewer reads, and it should not depend on pricing either.

What the script checks today, name, subtitle and keywords in ten languages, next to what I still check by eye: screenshot captions and the text inside the captured screen
Script-checked vs. eyeballed

To be honest about the gap: the checker guards text fields, not images. Screenshot captions live in the compose script and the app's own UI text lives inside the captures. Those I still review by eye, frame by frame, before an upload. A "free" inside a captured settings screen or a usage counter is just as visible to a reviewer as one in the headline.

Takeaways

  • Treat screenshots as metadata, because they are. A real capture of a real paywall can still break 2.3.7.
  • "Free" is a price. If a caption ends in "Free.", rewrite it to say what the feature does.
  • Put the paywall where it belongs: the in-app purchase's review screenshot, and the subscription terms in the description.
  • Generate frames from a list. A rejection becomes a two-line diff and a re-render instead of a redesign.
  • Turn each rejection into a check, and be clear about what the check does not cover.

More about Talkzy at talkzy.io.vn, and the rest of my apps are on apps.vanthuongdao.id.vn. If you are about to ship your first in-app purchase, the next post is about a different App Store Connect trap: what happens when you pull a submission that carries your first IAP.

  • #Talkzy
  • #App Review
  • #Screenshots
  • #ASO
ShareXLinkedInFacebook
Dao Van Thuong

Mobile and fullstack engineer in Ho Chi Minh City. I build and ship my own indie iOS apps — Lockboxy, Linkeeper, Minivid, Ringsy, Talkzy, Baton and Stampzy.