ANTIGRAVITY LABJP
Articles/App Development
App Development/2026-04-15Advanced

Antigravity × App Store Connect API: Complete Automation Guide for Revenue, ASO & Reviews

A hands-on guide to automating App Store Connect with Antigravity. From JWT authentication to sales report fetching, review sentiment analysis, ASO tracking, and a daily Slack dashboard — everything indie developers need to reclaim development time.

App Store Connect API2iOS27ASO2review analysisautomation83Antigravity338Swift8monetization31

Premium Article

Every indie developer knows the routine: open App Store Connect in the morning, check yesterday's sales, skim new reviews, wonder why that keyword ranking dropped. Multiply that by the number of apps you run, and suddenly a significant chunk of your working day disappears into a dashboard that never changes itself.

App Store Connect API has existed since 2018, yet many indie developers never get past the initial JWT authentication hurdle. The official documentation is comprehensive but scattered, and the authentication mechanism — signing tokens with an EC private key — feels unfamiliar compared to standard OAuth flows. This guide uses Antigravity as your AI coding partner to cut through that friction, walking you through authentication, sales automation, review monitoring, and a morning dashboard that runs without you.

What the App Store Connect API Can Actually Do

Before diving into code, it helps to understand the API's scope. The three areas most valuable to indie developers are:

Sales and Trends Reports: Daily, weekly, and monthly revenue broken down by country, product type, and app. Unlike the App Store Connect dashboard (limited to 365 days), the API gives you your entire sales history in a structured format suitable for long-term trend analysis.

Customer Reviews: Fetch all reviews, filter by rating or territory, and post responses — all programmatically. The key operational use case is detecting low-rated reviews within hours rather than stumbling on them the next day.

App Analytics: Impression counts, page view rates, conversion funnels, and source attribution (search vs. browse vs. referral). This data underlies any serious ASO effort.

With these three pillars automated, you essentially have a personal business intelligence layer running in the background at zero marginal cost.

JWT Authentication: Getting Past the First Wall

App Store Connect uses JWT signed with an EC private key (ES256 algorithm). Unlike bearer tokens you'd get from an OAuth exchange, you generate these locally every time — or more practically, every 20 minutes, since that's Apple's maximum token validity.

Generating Your API Key

Log into App Store Connect, navigate to Users and Access → Integrations → App Store Connect API, and create a new API key. For a read-heavy setup covering sales and reviews, select the "Sales and Trends" and "Customer Support" roles. Write down the following before leaving the page:

  • Issuer ID: A UUID like XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
  • Key ID: A 10-character alphanumeric string
  • .p8 private key file: Download it immediately — it cannot be re-downloaded

Store the .p8 file in a password manager or encrypted vault. If it's compromised, revoke it from App Store Connect immediately and generate a new one.

Implementing the Auth Class

Use this Antigravity prompt to scaffold the authentication layer:

Implement a TypeScript class that generates App Store Connect API JWT tokens.
Requirements:
- Accept issuer_id, key_id, and path to a .p8 private key file
- Sign using ES256 algorithm
- Set token expiry to 20 minutes (Apple's maximum)
- Cache and auto-refresh the token (refresh 2 minutes before expiry)
- Include proper error handling with descriptive messages

The resulting implementation should look like this:

import * as jwt from 'jsonwebtoken';
import * as fs from 'fs';
 
class AppStoreConnectAuth {
  private readonly issuerId: string;
  private readonly keyId: string;
  private readonly privateKey: string;
  private cachedToken: string | null = null;
  private tokenExpiry: number = 0;
 
  constructor(issuerId: string, keyId: string, privateKeyPath: string) {
    this.issuerId = issuerId;
    this.keyId = keyId;
    // Normalize line endings — critical if the key was ever opened on Windows
    this.privateKey = fs.readFileSync(privateKeyPath, 'utf8').replace(/\r\n/g, '\n');
  }
 
  generateToken(): string {
    const now = Math.floor(Date.now() / 1000);
 
    // Return cached token if still valid (with 2-minute buffer)
    if (this.cachedToken && this.tokenExpiry > now + 120) {
      return this.cachedToken;
    }
 
    const payload = {
      iss: this.issuerId,
      iat: now,
      exp: now + 1200, // 20 minutes
      aud: 'appstoreconnect-v1',
    };
 
    try {
      this.cachedToken = jwt.sign(payload, this.privateKey, {
        algorithm: 'ES256',
        header: { alg: 'ES256', kid: this.keyId, typ: 'JWT' },
      });
      this.tokenExpiry = now + 1200;
      return this.cachedToken;
    } catch (error) {
      // Most common cause: malformed private key (missing PEM headers or bad line endings)
      throw new Error(
        `JWT generation failed: ${error instanceof Error ? error.message : String(error)}. ` +
        `Verify the .p8 file includes BEGIN/END PRIVATE KEY headers.`
      );
    }
  }
 
  getAuthHeader(): { Authorization: string } {
    return { Authorization: `Bearer ${this.generateToken()}` };
  }
}
 
// Environment variables (store in .env or CI secrets — never commit)
// ASC_ISSUER_ID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
// ASC_KEY_ID=XXXXXXXXXX
// ASC_VENDOR_NUMBER=XXXXXXXX  (found in Payments and Financial Reports)
// APP_ID=XXXXXXXXXX           (the Apple ID number of your app, not the bundle ID)

Common Mistake 1: Line Ending Corruption

If you open the .p8 file on Windows, copy-paste its contents into a text field, or let your IDE auto-convert line endings, the jsonwebtoken library will throw a PEM_read_bio error. Always read the file directly with fs.readFileSync and apply .replace(/\r\n/g, '\n') as a safeguard.

Common Mistake 2: Wrong aud Claim

The aud field must be the string "appstoreconnect-v1" — not an array, not capitalized differently. Any deviation results in a 401 with an unhelpful "FORBIDDEN" message.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Implement App Store Connect API JWT authentication with ES256 and get a reusable token class that handles auto-renewal
Fetch sales, reviews, and ASO metrics in parallel and ship a morning revenue summary to Slack within the GitHub Actions free tier
Go from instant negative-review detection to semi-automated replies — the automation patterns indie developers actually need in production
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

App Dev2026-06-23
When StoreKit 2 Users Say They Paid but Can't Access — Field Notes on Subscription Entitlement Drift
StoreKit 2 subscriptions are harder to operate than to implement. This is a record of the drift between currentEntitlements, subscription.status, and server notifications — and the reconcile logic that finally stopped the support tickets.
App Dev2026-04-04
Mastering Swift Concurrency with Antigravity — async/await, Actors, and Structured Concurrency in Production iOS Apps
A practical, production-focused walkthrough of Swift Concurrency's async/await, Actor model, and Structured Concurrency with Antigravity AI assistance. Build production-quality iOS apps free from data races and callback hell.
App Dev2026-04-01
Antigravity × SwiftData Practical Guide — Migrating from Core Data and Mastering the New Standard
Learn how to implement SwiftData — Apple's modern data persistence framework for iOS 17+ — with Antigravity's AI agents. Covers key differences from Core Data, basic CRUD operations, and a step-by-step migration guide with code examples.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →