TL;DR: ASC has no public API for setting IAP price tiers. CDP + Playwright + JS clicks (not Playwright clicks) gets it done in ~30s per IAP. 7-step flow with exact selectors below.
The problem
Apple's App Store Connect API lets you create IAPs, manage availability, and submit for review — but does not let you set the price tier. The tier is gatekept behind their web UI.
For a single IAP this is fine. For 3+ apps with similar tiers, manual setup means clicking through ~15 modal steps × 3 apps × 7 steps each = 100+ clicks.
I'd rather automate it once.
What blocks Playwright
page.locator('button:has-text("添加定价")').click()
# TimeoutError: subtree intercepts pointer events
ASC's React SPA wraps every interactive element in a <dialog> overlay during transitions. Playwright's intelligent click waits for "stable" — which never happens because animations keep firing. After 30s, timeout.
The fix: JS click via page.evaluate
Bypass Playwright entirely and use the DOM API directly:
page.evaluate("""
() => {
const btn = Array.from(document.querySelectorAll('button'))
.find(b => (b.innerText||'').trim() === '添加定价' && b.offsetParent);
if (!btn) return 'NOT FOUND';
btn.scrollIntoView({block: 'center'});
btn.click();
return 'OK';
}
""")
Native Element.click() doesn't go through Playwright's stability checks. The React event handler fires immediately.
The 7-step flow for ASC IAP pricing
For each IAP, after navigating to /apps/<app_id>/distribution/iaps/<iap_id>:
- 添加定价 (Add Pricing) — opens region selection modal
- 选取 (in dialog) — opens tier dropdown
- $1.99 (in dialog, exact text match) — selects tier
- 下一步 (in dialog) — review per-region prices auto-converted from USD base
- 下一步 (in dialog) — go to final confirm
- 确认 (in dialog) — commit
- 保存 (page header button) — save the IAP record
After step 7, verify:
body = page.evaluate("document.body.innerText")
section = body[body.index("价格时间表"):body.index("价格时间表") + 800]
saved = "添加定价" not in section
If 添加定价 is gone from the section, pricing is locked in.
Critical timing
ASC SPA hydration is slow. After page.goto(url, wait_until="domcontentloaded"), you need:
- 22-25s before the page is interactive
- 4-6s after each click for next step to render
Skip these and you'll race the SPA. The :has-text("$1.99") selector matches both $1.99 and $19.99 — use exact innerText === '$1.99' instead.
Production result
3 IAPs (AutoChoice / AltitudeNow / DaysUntil) priced at $1.99 USD in 4 minutes wall-clock. Apple-side state confirmed via the same API I bootstrap with for app/IAP IDs.
Source
Full script (~140 LOC, Python 3.10+, Playwright 1.40+): I'll share via the AutoApp newsletter — 60-day indie iOS dev real-time log: https://autoappnotes.substack.com
Caveats
- This works for price tier selection only. Localizations / images / Submit-for-Review are separate flows (and the API can do those).
- Apple may change CSS selectors or button text any week. The brittle parts:
添加定价,选取,下一步,确认,保存. If your locale is English, swap to "Add Pricing" / "Select" / "Next" / "Confirm" / "Save". - Don't run this in headless mode without first verifying the flow visually. CDP + visible Chrome catches selector drift in 5 seconds vs 5 hours in CI.
Want the full pricing automation playbook? iOS Indie Launch Playbook on Gumroad — 50pp PDF, real numbers from shipping 4 apps in 60 days, including the ASC bureaucracy chapter.
Top comments (0)