App Connect Translate

How to sync app metadata to App Store Connect without Fastlane (2026)

Fastlane is the de-facto answer for automating App Store Connect updates, but it's not the only option — and for plenty of teams it's overkill. Here are the three real ways to sync metadata to App Store Connect without Fastlane, including the raw App Store Connect API call you'd actually write yourself, and the trade-offs of each.

Why skip Fastlane?

Fastlane is genuinely excellent. But it carries a tax that doesn't always make sense:

  • Ruby toolchain. Maintaining a working Ruby + Bundler setup on modern macOS is a recurring annoyance. Apple keeps shipping new system Ruby versions; chruby / rbenv drift; native gem builds break.
  • Maintenance. Fastfiles age. Apple changes the App Store Connect surface periodically, and the Fastlane community catches up days or weeks later. You'll occasionally find yourself pinning gem versions or debugging metadata pushes the day before a release.
  • Mismatch of scope. If the only thing you want to automate is updating metadata, Fastlane is a thousand lines of config for a thirty-line problem.
  • No translation. Fastlane deliver doesn't translate anything. You still need to produce the per-locale strings somehow. For most indie teams, that's the actual hard part.

You have three alternatives. Each one trades off effort, automation, and translation differently.

Option A: The App Store Connect web UI

The baseline. Open App Store Connect → My Apps → [your app] → App Information. Use the language dropdown to add or edit a locale. Paste in the name, subtitle, description, keywords, promotional text, what's new. Save.

What's good. Zero setup. No CI, no API keys, no code. Apple's official tool. Free.

What's bad. It doesn't scale. For 30 locales × 6–7 fields each, you're pasting 180–210 individual strings — and that's per release if you're updating "what's new." Character limits are enforced silently after you save. Brand names get clobbered if you used Google Translate to draft the strings. And there's no audit trail.

Realistic ceiling: 2–3 locales. Past that, you need automation.

Option B: The raw App Store Connect REST API

Apple publishes the App Store Connect API: a REST API for everything Fastlane does, and a few things it doesn't. You can write a 50–100 line script in any language and skip Fastlane entirely.

Authentication is the only mildly fiddly bit. You generate an API key in App Store Connect (step-by-step guide here), download a .p8 file, then sign a short-lived JWT with the ES256 algorithm. Every API request includes that JWT as a Bearer token.

Here's the minimum viable Node.js: sign a JWT, then PATCH a single localization.

// auth.mjs — sign an App Store Connect API JWT
import crypto from "node:crypto";
import { readFileSync } from "node:fs";

const KEY_ID = process.env.ASC_KEY_ID;            // 10-char string from App Store Connect
const ISSUER_ID = process.env.ASC_ISSUER_ID;      // UUID from your team
const PRIVATE_KEY = readFileSync("AuthKey_XXXXX.p8", "utf8");

export function buildJWT() {
  const header = { alg: "ES256", kid: KEY_ID, typ: "JWT" };
  const now = Math.floor(Date.now() / 1000);
  const payload = {
    iss: ISSUER_ID,
    iat: now,
    exp: now + 60 * 15,         // 15-min token
    aud: "appstoreconnect-v1",
  };
  const b64 = (o) => Buffer.from(JSON.stringify(o)).toString("base64url");
  const signingInput = `${b64(header)}.${b64(payload)}`;
  const signature = crypto.sign(null, Buffer.from(signingInput), {
    key: PRIVATE_KEY,
    dsaEncoding: "ieee-p1363",  // Apple wants raw R||S, not DER
  });
  return `${signingInput}.${signature.toString("base64url")}`;
}

Then a PATCH against the localization for one locale:

// push.mjs — update one App Store version localization
import { buildJWT } from "./auth.mjs";

const jwt = buildJWT();
const VERSION_ID = "abcdef-1234-..."; // appStoreVersions/<id>
const LOCALE = "de-DE";

// 1. Find (or create) the per-locale row
const list = await fetch(
  `https://api.appstoreconnect.apple.com/v1/appStoreVersions/${VERSION_ID}/appStoreVersionLocalizations?limit=200`,
  { headers: { Authorization: `Bearer ${jwt}` } },
).then((r) => r.json());

const existing = list.data.find((l) => l.attributes.locale === LOCALE);

// 2. PATCH the fields you want to update. Omitted fields are preserved.
const body = {
  data: {
    type: "appStoreVersionLocalizations",
    id: existing.id,
    attributes: {
      description: "Eine native Aufgabenverwaltung für iOS.",
      keywords: "aufgaben,produktivität,todo,liste",
      promotionalText: "Neu: Synchronisation mit iCloud.",
      whatsNew: "• Schnellerer Start\n• Behebt einen Absturz beim Export",
    },
  },
};

await fetch(
  `https://api.appstoreconnect.apple.com/v1/appStoreVersionLocalizations/${existing.id}`,
  {
    method: "PATCH",
    headers: {
      Authorization: `Bearer ${jwt}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  },
);

That's it. Repeat the PATCH per locale. App-level fields (name, subtitle) live on a different resource — /v1/appInfoLocalizations/<id> — but the pattern is identical.

What's good. No Ruby, no gems, no Fastfile. You own the script. Easy to run anywhere — local machine, GitHub Action, cron. Full control over which fields update and in what order.

What's bad. You're writing infrastructure. You still need to produce the translated strings yourself. Edge cases — 409 conflicts when a locale already exists, fields that are read-only at certain App Store states, Apple's silent character-limit enforcement — all become your problem to handle.

Realistic ceiling: this works well if your translations are already done and you just want to push them. For a team that ships frequently and has translators, this is a clean replacement for Fastlane deliver.

Option C: A focused metadata translation tool

The third option is the one we built because Options A and B both leave the translation problem unsolved. App Connect Translate is a single-purpose tool: paste source metadata once, AI-translate into all 30+ App Store locales (with character-limit handling, brand-glossary protection, and keyword-field idioms baked in), review every translation side-by-side, then one click pushes everything to App Store Connect via the same API as Option B.

It's the option to pick if the part of "metadata sync" you actually wanted to skip was writing the translations, not just running the PATCH.

What's good. No setup, no Ruby, no script to maintain. Translations are AI-generated and fit to character limits automatically. Empty fields are skipped, so existing App Store Connect content is preserved. Side-by-side diff before publishing — nothing goes live until you approve it.

What's bad. Paid (one-time credits, no subscription). Not a CI tool — there's a deliberate human-in-the-loop review step before push. Doesn't handle screenshots, signing, or TestFlight (use Fastlane or Xcode Cloud for those).

Pick the option that fits your team

SituationUse
One app, 2–3 locales, you ship 2× a yearApp Store Connect web UI
Translations already done, you ship often, you have CIDirect API script (Option B) or Fastlane
Translations not done, 10+ locales, you don't want to learn RubyApp Connect Translate
Need screenshots, signing, TestFlight, plus metadataFastlane (still the right answer for the full pipeline)
Want a clean Fastlane deliver replacement specifically for metadataApp Connect Translate — see the dedicated comparison

None of these are mutually exclusive. The cleanest setup for many indie teams is Fastlane for builds + signing + screenshots, and App Connect Translate (or a custom Option B script) for metadata.

Skip the script, keep the API

App Connect Translate pushes through the same App Store Connect API you'd call yourself — plus AI translation, character-limit handling, and a side-by-side review step.

Try App Connect Translate →

FAQ

Can the App Store Connect API push to all 30+ locales in one call?

No — locales are independent resources, one PATCH per locale. You can fire them in parallel from your script, but Apple enforces a soft rate limit (~50/second for most accounts), so for 30+ locales it's worth batching with a small concurrency limit (5–10 in flight) to be safe.

Does the App Store Connect API let me submit a new version for review?

Yes — there's POST /v1/appStoreVersionSubmissions for that, plus questionnaire endpoints for the App Review questions. This is the same surface Fastlane uses under the hood.

Can I push metadata while a version is in "Waiting for Review"?

Some fields, yes — promotional text is editable at any time. Others (name, description, keywords, what's new) lock when the version moves to "In Review" and reopen on rejection. App Connect Translate handles this with a field-stripping retry: if Apple rejects an individual field as read-only, it skips that field and continues with the others.

What happens to existing localizations if I PATCH a partial set of fields?

App Store Connect's API uses PATCH semantics — only the fields included in the body are updated. Omitted fields are preserved. So if you push only whatsNew, the existing description for that locale stays untouched.

Is there a Python or Swift alternative to Ruby Fastlane?

For "drive the App Store Connect API from a script," yes — the API is language-agnostic REST + JSON, so Python, Swift, Go, or anything that can do HTTP + ES256 signing works. There are Python libraries (appstoreconnect-api on PyPI) and Swift packages that wrap the API. Pick the one that matches your team's language.

Related

App Connect Translate vs Fastlane deliver — the direct comparison. Same trade-offs as this article but framed as a head-to-head rather than "what are my options."