skills/app-store-screenshots/SKILL.md
App Store screenshot research, competitor analysis, building, and upload tool for iOS/macOS apps. Use this skill when working with App Store screenshots for any of these tasks: (1) Finding and analyzing competitor screenshots in your category, (2) Downloading competitor screenshots locally for reference, (3) Analyzing screenshot strategies (styles, captions, features), (4) Researching ASO keywords for screenshot captions, (5) Planning your screenshot sequence and messaging, (6) Building screenshots using an HTML/CSS export tool, (7) Generating a local preview website to view and compare screenshots, (8) Exporting screenshots at correct dimensions via browser or Puppeteer, (9) Localizing screenshots for multiple App Store locales, (10) Uploading screenshots to App Store Connect via API.
npx skillsauth add onatcipli/skills app-store-screenshotsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Research competitor screenshots, plan your strategy, build production-ready screenshots, and upload them to App Store Connect.
Screenshots are critical for App Store conversion—90% of users don't scroll past the third screenshot. This skill covers the full pipeline from competitor research through final upload.
iPhone 6.9" (Required): 1260 x 2736 ← iPhone 16 Pro Max
iPhone 6.7": 1290 x 2796 ← iPhone 16 Plus, 15 Pro Max
iPad 13" (Required): 2064 x 2752 ← iPad Pro M4
WARNING: Many online sources (including older versions of this skill) incorrectly
list 1290x2796 or 1320x2868 as the 6.9" dimension. The correct 6.9" dimension
is 1260 x 2736. Verify at: https://developer.apple.com/help/app-store-connect/reference/screenshot-specifications/
Before starting, gather information about the user's app and goals.
1. Is this a new app or an existing app?
□ New app (focus on competitor research)
□ Existing app (can also analyze current performance)
2. What is your app's category?
[Select from App Store categories - see category-codes in references]
3. Describe your app in one sentence:
[Used to extract keywords for competitor search]
4. What is your app name?
[Used for folder organization]
5. What are your main competitors? (optional)
[If known, provide app names or IDs]
6. Do you have Sensor Tower API access?
□ Yes (enhanced competitor finding)
□ No (use iTunes API + RSS feeds only)
7. What locales do you need screenshots for?
[e.g., en-US, tr, de-DE, ja]
Create project folder structure:
{app_name}_screenshots/
├── competitors/
├── analysis/
├── your_app/
│ ├── iphone/
│ ├── ipad/
│ └── assets/
├── preview/
├── export/
│ └── locales/
│ ├── en-US/
│ └── {other locales}/
└── README.md
Identify the top competitors in your category to analyze.
Extract keywords from app description and search iTunes:
GET https://itunes.apple.com/search?term={keywords}&entity=software&country=us&limit=25
Get top apps in the category via RSS feed:
GET https://rss.applemarketingtools.com/api/v2/us/apps/top-free/50/apps.json?genre={genreId}
If Sensor Tower API available:
GET https://app.sensortower.com/api/ios/apps?app_ids={ids}
Use download/revenue estimates to find "winning" apps.
Present candidates to user:
## Competitor Candidates
| # | App Name | Rating | Reviews | Category Rank |
|---|----------|--------|---------|---------------|
| 1 | Calm | 4.8★ | 1.2M | #1 Health |
| 2 | Headspace | 4.9★ | 850K | #2 Health |
| 3 | Insight Timer | 4.9★ | 450K | #5 Health |
Select 3 competitors to analyze (enter numbers): [1, 2, 3]
Download competitor screenshots and generate analysis.
For each competitor:
GET https://itunes.apple.com/lookup?id={appId}&country=us
Extract from response:
screenshotUrls — iPhone screenshotsipadScreenshotUrls — iPad screenshotsMany apps return empty screenshotUrls: [] from the iTunes API. This is common
and not a rate limit issue—the API simply doesn't include screenshots for some apps.
Fallback strategy (try in order):
Try different country codes: us, gb, de, cy, tr, au
GET https://itunes.apple.com/lookup?id={appId}&country=gb
Scrape the App Store web page HTML: App Store pages render client-side via JavaScript, but the raw HTML source often contains mzstatic.com screenshot URLs embedded as data attributes.
Fetch: https://apps.apple.com/us/app/id{appId}
Search HTML source for: mzstatic.com URLs containing screenshot patterns
Filter for URLs with dimensions in the path (e.g., /1242x2208bb.png)
Manual capture: As a last resort, instruct the user to screenshot the App Store listing on their device or visit the web page in a browser.
import requests
import os
import json
import subprocess
from pathlib import Path
def download_screenshots(app_id, app_name, output_dir, country="us"):
# Fetch app data — try multiple countries if needed
countries = [country, "us", "gb", "de", "au", "cy"]
data = None
for cc in countries:
response = requests.get(f"https://itunes.apple.com/lookup?id={app_id}&country={cc}")
result = response.json()
if result["resultCount"] > 0:
app = result["results"][0]
if app.get("screenshotUrls"):
data = app
break
if not data:
print(f"WARNING: No screenshots found for {app_name} via iTunes API")
print("Try scraping the App Store web page HTML for mzstatic.com URLs")
return None
# Create directories
base_path = Path(output_dir) / "competitors" / app_name
iphone_path = base_path / "iphone"
ipad_path = base_path / "ipad"
iphone_path.mkdir(parents=True, exist_ok=True)
ipad_path.mkdir(parents=True, exist_ok=True)
# Download and verify dimensions
screenshots_meta = []
for i, url in enumerate(data.get("screenshotUrls", []), 1):
filepath = iphone_path / f"{i:02d}.png"
img = requests.get(url)
with open(filepath, "wb") as f:
f.write(img.content)
# Verify actual dimensions (CDN may return smaller than requested)
dims = get_image_dimensions(filepath)
screenshots_meta.append({
"index": i,
"url": url,
"actualWidth": dims[0],
"actualHeight": dims[1]
})
# Save enriched metadata
metadata = {
**data,
"downloadDate": str(date.today()),
"screenshotCount": len(data.get("screenshotUrls", [])),
"ipadScreenshotCount": len(data.get("ipadScreenshotUrls", [])),
"screenshotDetails": screenshots_meta
}
with open(base_path / "metadata.json", "w") as f:
json.dump(metadata, f, indent=2)
def get_image_dimensions(filepath):
"""Get actual pixel dimensions. CDN URLs don't upscale."""
# macOS: sips -g pixelWidth -g pixelHeight {file}
# Linux: identify {file} (ImageMagick) or python Pillow
from PIL import Image
img = Image.open(filepath)
return img.size
The CDN does NOT upscale images. If a developer uploaded 5.5" screenshots
(1242x2208), requesting a URL ending in 1320x2868bb.png silently returns the
original 1242x2208 image. Always verify actual dimensions after downloading.
For each competitor, analyze and document:
## Screenshot Analysis: {App Name}
### Overview
- **iPhone Screenshots:** {count}
- **iPad Screenshots:** {count}
- **Actual Dimensions:** {width}x{height} (may differ from URL)
- **Visual Style:** [Device mockup / Lifestyle / UI-only / Hybrid]
- **Color Palette:** [Primary colors observed]
### Screenshot Breakdown
| # | Type | Caption/Text | Feature Shown | Notes |
|---|------|--------------|---------------|-------|
| 1 | {type} | "{caption}" | {feature} | {notes} |
| 2 | {type} | "{caption}" | {feature} | {notes} |
...
### Strategy Analysis
- **Hook (Screenshot 1):** {description}
- **Flow:** {how screenshots progress}
- **Text Style:** {short/long, benefit/feature focused}
- **Device Frames:** {yes/no, style}
- **ASO Keywords in Captions:** {observed keywords}
### Strengths
- {strength 1}
- {strength 2}
### Weaknesses
- {weakness 1}
- {weakness 2}
Save as competitors/{app_name}/README.md
Generate a cross-competitor analysis.
## Competitor Comparison
### Visual Style
| Aspect | Competitor 1 | Competitor 2 | Competitor 3 |
|--------|--------------|--------------|--------------|
| Screenshot Count | 8 | 6 | 10 |
| Style | Device mockup | Lifestyle | Hybrid |
| Text Amount | Minimal | Heavy | Medium |
| Color Theme | Blue/Purple | Green/White | Orange/Black |
| Device Frames | Yes | No | Yes |
| Frame Type | Dynamic Island | N/A | Dynamic Island |
### Caption Patterns
| Screenshot | Competitor 1 | Competitor 2 | Competitor 3 |
|------------|--------------|--------------|--------------|
| 1 (Hook) | "Find Peace" | "Sleep Better" | "Meditate Daily" |
| 2 | "Sleep Stories" | "Guided Sessions" | "Track Progress" |
| 3 | "Daily Calm" | "Reduce Stress" | "Join Community" |
### ASO Keywords in Screenshots
| Keyword | Comp 1 | Comp 2 | Comp 3 | Search Volume |
|---------|--------|--------|--------|---------------|
| "calorie" | ✓ | ✓ | ✗ | High |
| "tracker" | ✓ | ✗ | ✓ | High |
| "AI" | ✗ | ✓ | ✓ | Medium |
### Common Patterns
1. {pattern 1}
2. {pattern 2}
3. {pattern 3}
### Differentiation Opportunities
1. {opportunity 1}
2. {opportunity 2}
Save as analysis/competitor_comparison.md
Do this BEFORE writing captions. Screenshot captions are now indexed by Apple's search algorithm (as of June 2025), making keyword placement critical.
GET https://itunes.apple.com/search?term={category}&entity=software&country=us&limit=25
Extract keyword patterns from competitor app names and subtitles:
"Cal AI - Calorie Tracker" → keywords: cal, ai, calorie, tracker
"Lifesum: AI Calorie Counter" → keywords: ai, calorie, counter
"YAZIO – Calorie Counter & Diet" → keywords: calorie, counter, diet
From competitor analysis, rank keywords by:
Each screenshot caption should contain at least one target keyword:
## Keyword-Optimized Screenshot Captions
Target keywords: calorie, tracker, AI, scan, food, diet
| # | Before (generic) | After (ASO-optimized) | Keywords |
|---|-------------------|-----------------------|----------|
| 1 | "Snap It. Track It." | "Scan Food, Count Calories" | scan, food, calories |
| 2 | "See Your Progress" | "AI Calorie Tracker" | AI, calorie, tracker |
| 3 | "Eat Smarter" | "Smart Diet Insights" | diet, insights |
Different markets search differently:
| Locale | Top Keywords |
|--------|--------------|
| en-US | calorie tracker, food scanner, diet app |
| tr | kalori sayacı, yemek takip, diyet uygulaması |
| de-DE | kalorienzähler, ernährung, diät tracker |
| ja | カロリー計算, 食事管理, ダイエット |
Based on analysis and keyword research, create a screenshot plan.
The "Value-Usage-Trust" Formula:
1. VALUE — What the user gets (hook) + primary keyword
2. USAGE — How it works (key features) + feature keywords
3. TRUST — Social proof (ratings, awards)
## Screenshot Plan: {Your App Name}
### Strategy
- **Style:** {chosen style based on analysis}
- **Count:** {recommended: 6-8}
- **Text Approach:** {based on competitor analysis}
- **Differentiator:** {what makes yours unique}
- **Target Keywords:** {from Workflow 4}
### Screenshot Sequence
#### Screenshot 1: Hook / Value Proposition
- **Caption:** "{ASO-optimized caption}"
- **Subtitle:** "{supporting text}"
- **Visual:** {description of what to show}
- **Keywords:** {keywords included}
- **Goal:** Grab attention, communicate core value
#### Screenshot 2: Key Feature #1
...
### Design Guidelines
- **Colors:** {recommended palette}
- **Font:** {recommendation}
- **Device Frame:** Use Dynamic Island style (not notch)
- **Safe Zones:** Keep text 120px from edges, 160px from top
- **Caption Font Size:** 140-160px title, 48-56px subtitle
- **Letter Spacing:** -4px to -5px for bold impact
### Export Dimensions
- [ ] iPhone 6.9": 1260 x 2736 (iPhone 16 Pro Max)
- [ ] iPad 13": 2064 x 2752
- [ ] PNG format, no transparency
- [ ] Under 8 MB each
Save as your_app/plan.md
Create production screenshots using an HTML/CSS builder.
Create a self-contained HTML file that renders each screenshot at exact pixel dimensions. See templates/export-tool.html for the full template.
Key components:
transform: scale() for in-browser preview displayUse a modern Dynamic Island frame (not the outdated notch):
.phone-frame {
position: relative;
width: 380px;
height: 780px;
border-radius: 55px;
border: 8px solid #8a8a8f; /* Titanium color */
background: #000;
box-shadow:
inset 0 0 0 3px #1a1a1a,
6px 0 0 -2px #6b6b6f, /* Power button */
-6px 40px 0 -2px #6b6b6f, /* Volume up */
-6px 90px 0 -2px #6b6b6f; /* Volume down */
overflow: hidden;
}
.dynamic-island {
position: absolute;
top: 16px;
left: 50%;
transform: translateX(-50%);
width: 130px;
height: 40px;
background: #000;
border-radius: 22px;
z-index: 10;
}
Standardized caption layout for consistency:
.screenshot-title {
font-size: 140px;
font-weight: 900;
letter-spacing: -4px;
line-height: 1.05;
text-align: center;
}
.screenshot-subtitle {
font-size: 52px;
font-weight: 500;
letter-spacing: -1px;
opacity: 0.85;
text-align: center;
}
Guide for incorporating app assets:
⚠️ NEVER open export-tool.html as a file:// URL.
Canvas export will fail with: "Tainted canvases may not be exported"
✅ ALWAYS serve via HTTP:
python3 -m http.server 8080
Then visit: http://localhost:8080/export-tool.html
✅ Add crossorigin="anonymous" to ALL <img> tags
✅ In html2canvas config, use: { useCORS: true }
Two methods for exporting at correct dimensions.
Include html2canvas via CDN in your export tool:
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<script>
async function exportScreenshot(elementId, filename) {
const element = document.getElementById(elementId);
const canvas = await html2canvas(element, {
width: 1260,
height: 2736,
scale: 1,
useCORS: true,
logging: false
});
const link = document.createElement('a');
link.download = filename;
link.href = canvas.toDataURL('image/png');
link.click();
}
</script>
Puppeteer produces more reliable output for fonts and images.
const puppeteer = require('puppeteer');
async function captureScreenshot(url, elementId, outputPath) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
// Set viewport to exact screenshot dimensions
await page.setViewport({ width: 1260, height: 2736 });
await page.goto(url, { waitUntil: 'networkidle0' });
// Clone element into a fixed full-size wrapper
// (direct capture fails because of CSS transform: scale)
await page.evaluate((id) => {
// Hide all existing page content
for (const child of document.body.children) {
child.style.display = 'none';
}
// Create fixed wrapper at exact viewport size
const wrapper = document.createElement('div');
wrapper.style.cssText = `
position: fixed; top: 0; left: 0;
width: 1260px; height: 2736px;
overflow: hidden; z-index: 99999;
`;
// Clone the target element at full size (no transform)
const original = document.getElementById(id);
const clone = original.cloneNode(true);
clone.style.cssText = `
transform: none;
width: 1260px; height: 2736px;
position: absolute; top: 0; left: 0;
`;
wrapper.appendChild(clone);
document.body.prepend(wrapper);
}, elementId);
await page.screenshot({
path: outputPath,
clip: { x: 0, y: 0, width: 1260, height: 2736 }
});
await browser.close();
}
Always verify after export:
# macOS
sips -g pixelWidth -g pixelHeight screenshot.png
# Linux (ImageMagick)
identify screenshot.png
# Expected: 1260 x 2736 (iPhone 6.9")
{nn}_{name}_{locale}.png
Examples:
01_hook_en-US.png
02_scan_food_en-US.png
03_ai_tracker_en-US.png
01_hook_tr.png
02_yemek_tara_tr.png
Create localized versions for multiple App Store locales.
Design screenshots with swappable text layers:
Use ASO-optimized keywords per locale (from Workflow 4):
| Screenshot | en-US | tr | de-DE |
|------------|-------|-----|-------|
| 1 | "Scan Food, Count Calories" | "Yemeği Tara, Kalori Say" | "Essen Scannen, Kalorien Zählen" |
| 2 | "AI Calorie Tracker" | "AI Kalori Takibi" | "KI Kalorienzähler" |
| 3 | "Smart Diet Insights" | "Akıllı Diyet Analizi" | "Smarte Diät-Einblicke" |
export/
└── locales/
├── en-US/
│ ├── 01_hook.png
│ ├── 02_feature1.png
│ └── ...
├── tr/
│ ├── 01_hook.png
│ └── ...
└── de-DE/
├── 01_hook.png
└── ...
Preview all locales together before uploading to verify consistency.
Upload screenshots programmatically via the App Store Connect API.
const jwt = require('jsonwebtoken');
const fs = require('fs');
const privateKey = fs.readFileSync('AuthKey_{keyId}.p8');
const token = jwt.sign(
{
iss: ISSUER_ID,
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 1200,
aud: 'appstoreconnect-v1'
},
privateKey,
{ algorithm: 'ES256', header: { alg: 'ES256', kid: KEY_ID, typ: 'JWT' } }
);
Find app:
GET /v1/apps?filter[bundleId]={bundleId}
Find editable version:
GET /v1/apps/{id}/appStoreVersions?filter[appStoreState]=PREPARE_FOR_SUBMISSION
Valid filter states: PREPARE_FOR_SUBMISSION, DEVELOPER_REJECTED,
REJECTED, METADATA_REJECTED, WAITING_FOR_REVIEW, IN_REVIEW,
PENDING_DEVELOPER_RELEASE, READY_FOR_SALE
WARNING: READY_FOR_DISTRIBUTION is NOT a valid state value.
Get/create localizations:
GET /v1/appStoreVersions/{id}/appStoreVersionLocalizations
Create screenshot set:
POST /v1/appScreenshotSets
Body:
{
"data": {
"type": "appScreenshotSets",
"attributes": {
"screenshotDisplayType": "APP_IPHONE_67"
},
"relationships": {
"appStoreVersionLocalization": {
"data": { "type": "appStoreVersionLocalizations", "id": "{locId}" }
}
}
}
}
Delete existing screenshots (if replacing):
DELETE /v1/appScreenshots/{screenshotId}
For each screenshot:
a. Reserve: POST /v1/appScreenshots
{ fileName, fileSize, sourceFileChecksum (MD5) }
b. Upload binary to the URL(s) provided in response
c. Commit: PATCH /v1/appScreenshots/{id}
{ uploaded: true, sourceFileChecksum: MD5 }
IMPORTANT: APP_IPHONE_69 does NOT exist in the API.
iPhone 6.9" screenshots map to the 6.7" display type.
| Device | API Display Type | Accepted Dimensions |
|-----------------|--------------------------|---------------------|
| iPhone 6.9" | APP_IPHONE_67 | 1260 x 2736 |
| iPhone 6.7" | APP_IPHONE_67 | 1290 x 2796 |
| iPhone 6.5" | APP_IPHONE_65 | 1284 x 2778 |
| iPhone 6.1" | APP_IPHONE_61 | 1179 x 2556 |
| iPad 13" | APP_IPAD_PRO_3GEN_129 | 2064 x 2752 |
| iPad 12.9" | APP_IPAD_PRO_3GEN_129 | 2048 x 2732 |
See asc-upload-api.md for full details.
| Task | Endpoint |
|------|----------|
| Search apps | itunes.apple.com/search?term={q}&entity=software |
| Lookup app | itunes.apple.com/lookup?id={id}&country={cc} |
| Top Free | rss.applemarketingtools.com/api/v2/{cc}/apps/top-free/{limit}/apps.json |
| Top Paid | rss.applemarketingtools.com/api/v2/{cc}/apps/top-paid/{limit}/apps.json |
| By Category | Add ?genre={genreId} to RSS feeds |
| ASC: Screenshot sets | POST /v1/appScreenshotSets |
| ASC: Upload screenshot | POST /v1/appScreenshots |
{app_name}_screenshots/
├── competitors/
│ ├── {competitor_1}/
│ │ ├── iphone/
│ │ │ ├── 01.png
│ │ │ └── ...
│ │ ├── ipad/
│ │ │ └── ...
│ │ ├── metadata.json
│ │ └── README.md
│ ├── {competitor_2}/
│ └── {competitor_3}/
├── analysis/
│ ├── competitor_comparison.md
│ └── patterns.md
├── your_app/
│ ├── plan.md
│ ├── iphone/
│ │ ├── 01_hook.png
│ │ └── ...
│ ├── ipad/
│ │ └── ...
│ └── assets/
│ ├── ui_screenshots/
│ └── photos/
├── preview/
│ └── index.html
├── export/
│ ├── export-tool.html
│ ├── capture.js
│ └── locales/
│ ├── en-US/
│ └── {other locales}/
└── README.md
| File | Purpose | |------|---------| | screenshot-specs.md | All device sizes and technical requirements | | analysis-templates.md | Templates for competitor analysis | | itunes-api.md | API endpoints + fallback strategies | | asc-upload-api.md | App Store Connect upload API | | export-pipeline.md | html2canvas + Puppeteer export guide | | preview-website.html | Preview tool template | | export-tool.html | Screenshot builder template |
tools
A native-first SwiftUI design language for building consistent, premium iOS app UI — a dark-first, image-forward, softly-rounded aesthetic (inspired by apps like Luma) expressed almost entirely through native SwiftUI components. Use this skill for any UI/UX work in a SwiftUI/iOS project: (1) Building new screens, views, or components, (2) Redesigning or restyling existing screens, (3) Refactoring ad-hoc styling into reusable design tokens, (4) Reviewing UI for visual consistency, (5) Implementing dark interfaces with materials/Liquid Glass, navigation stacks, tab views, lists, toolbars, buttons, search, sheets, empty states, hero headers, chips, and avatar groups. The guiding rule is NATIVE FIRST: reach for built-in SwiftUI components and modifiers before writing any custom view, and only build a custom component when the platform genuinely can't express the design. Trigger when the user mentions UI, design, styling, layout, theming, redesign, restyle, refactor the UI, design consistency, or "make it look good/premium/like Luma" in any SwiftUI/iOS project.
development
App Store review analysis, competitor intelligence, and automated response generation for iOS/macOS apps. Use this skill when working with Apple App Store reviews for any of these tasks: (1) Fetching and analyzing customer reviews for your own apps via the App Store Connect API, (2) Fetching competitor app reviews via the public RSS feed or iTunes Search API, (3) Performing sentiment analysis, theme extraction, or bug/feature-request categorization on reviews, (4) Generating context-aware developer replies to customer reviews, (5) Comparing ratings, review sentiment, or feature gaps across competing apps, (6) Tracking rating trends and review volume over time, (7) Producing executive summaries or actionable insight reports from review data, (8) Searching the App Store for apps by name or category, (9) Setting up app-specific reply configurations through an interactive onboarding interview that auto-detects patterns from existing responses.
development
Price localization intelligence for iOS/macOS apps using purchasing power parity analysis. Use this skill when working with App Store pricing for any of these tasks: (1) Building a "Price Index" by analyzing reference app prices across territories, (2) Comparing your app's pricing against PPP-adjusted benchmarks, (3) Generating localized price recommendations based on economic data, (4) Automating price updates via App Store Connect API, (5) Analyzing competitor pricing strategies across markets, (6) Identifying overpriced/underpriced territories hurting conversions or revenue, (7) Creating pricing health reports with actionable recommendations.
development
App Store Optimization and metadata localization for iOS/macOS apps. Use this skill when working with App Store metadata for any of these tasks: (1) Analyzing current app metadata health (character usage, missing localizations), (2) Keyword research and optimization across locales, (3) Translating and localizing metadata to 38+ languages, (4) Implementing cross-localization strategies to maximize keyword coverage, (5) Bulk updating metadata via App Store Connect API, (6) Competitor ASO analysis and keyword gap identification, (7) Generating optimized titles, subtitles, keywords, and descriptions, (8) Auditing metadata for ASO best practices compliance.