> ## Documentation Index
> Fetch the complete documentation index at: https://docs.appeeky.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Localize Metadata

> Translate app store metadata into up to 10 languages with an LLM and optionally publish to App Store Connect

```
POST /v1/localizations
GET  /v1/localizations/jobs/:jobId
```

Translate and culturally localize an app's store metadata — title, subtitle, keywords, promotional text, description, and what's new — into **up to 10 languages** in a single run. An LLM adapts each field for native speakers (not a word-for-word translation) and respects App Store character limits.

The work runs asynchronously on a background job, so `POST /v1/localizations` returns a `jobId` immediately (HTTP `202`). Poll `GET /v1/localizations/jobs/:jobId` for the per-locale results.

<Note>
  When the app is connected in your Appeeky account, each translation is saved as an editable **draft** (visible in the dashboard Localizations tab). Set `publish: true` to push each translation straight to the live **App Store Connect** listing after translating. Direct publishing requires a connected App Store Connect key — see [App Store Connect](/docs/app-store-connect-overview).
</Note>

***

## Create a run

```
POST /v1/localizations
```

### Request Body

| Field          | Type      | Required | Default | Description                                                                                            |
| -------------- | --------- | -------- | ------- | ------------------------------------------------------------------------------------------------------ |
| `appId`        | string    | Yes\*    | —       | App Store numeric id (preferred). Provide this **or** `userAppId`.                                     |
| `userAppId`    | string    | Yes\*    | —       | Appeeky `user_apps` row id (uuid). Alternative to `appId`.                                             |
| `locales`      | string\[] | Yes      | —       | BCP-47 target locales, e.g. `["tr","de-DE","ja","pt-BR"]`. **1–10** entries.                           |
| `sourceLocale` | string    | No       | `en-US` | Locale to translate **from**.                                                                          |
| `publish`      | boolean   | No       | `false` | When `true`, publish each translation to the live App Store Connect listing.                           |
| `source`       | object    | No       | —       | Source copy override (see below). Falls back to the app's existing draft / base metadata when omitted. |

\* Provide either `appId` or `userAppId`.

**`source` object** (all fields optional, all `string \| null`):

| Field              | Description                   |
| ------------------ | ----------------------------- |
| `title`            | App name                      |
| `subtitle`         | Subtitle                      |
| `description`      | Long description              |
| `keywords`         | Comma-separated keyword field |
| `promotional_text` | Promotional text              |
| `whats_new`        | Release notes / what's new    |

<Tip>
  Cost is metered at **3 credits per requested locale** (one LLM call each). A 5-language run costs 15 credits. The poll endpoint is free.
</Tip>

### Code Examples

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl -X POST "https://api.appeeky.com/v1/localizations" \
    -H "X-API-Key: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "appId": "913335252",
      "locales": ["tr", "de-DE", "ja", "pt-BR"],
      "sourceLocale": "en-US",
      "publish": false
    }'
  ```

  ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  // 1. Start the run
  const start = await fetch("https://api.appeeky.com/v1/localizations", {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      appId: "913335252",
      locales: ["tr", "de-DE", "ja", "pt-BR"],
      sourceLocale: "en-US",
      publish: false,
    }),
  });
  const { data } = await start.json();
  const jobId = data.jobId;

  // 2. Poll for results
  async function poll() {
    const res = await fetch(
      `https://api.appeeky.com/v1/localizations/jobs/${jobId}`,
      { headers: { "X-API-Key": "YOUR_API_KEY" } }
    );
    const { data } = await res.json();
    if (data.status === "completed" || data.status === "failed") return data;
    await new Promise((r) => setTimeout(r, 3000));
    return poll();
  }
  console.log(await poll());
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import time, requests

  headers = {"X-API-Key": "YOUR_API_KEY"}

  start = requests.post(
      "https://api.appeeky.com/v1/localizations",
      headers=headers,
      json={
          "appId": "913335252",
          "locales": ["tr", "de-DE", "ja", "pt-BR"],
          "sourceLocale": "en-US",
          "publish": False,
      },
  )
  job_id = start.json()["data"]["jobId"]

  while True:
      res = requests.get(
          f"https://api.appeeky.com/v1/localizations/jobs/{job_id}",
          headers=headers,
      ).json()["data"]
      if res["status"] in ("completed", "failed"):
          break
      time.sleep(3)

  print(res)
  ```
</CodeGroup>

### Response (`202 Accepted`)

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "data": {
    "jobId": "f3c1a2b4-...",
    "triggerRunId": "run_...",
    "status": "queued",
    "locales": ["tr", "de-DE", "ja", "pt-BR"],
    "publish": false,
    "tracked": true
  }
}
```

| Field          | Type    | Description                                                           |
| -------------- | ------- | --------------------------------------------------------------------- |
| `jobId`        | string  | Use this to poll for results.                                         |
| `triggerRunId` | string  | Underlying background run id (for support/debugging).                 |
| `status`       | string  | Always `queued` on creation.                                          |
| `tracked`      | boolean | Whether the app is connected in Appeeky (drafts are saved when true). |

***

## Poll for results

```
GET /v1/localizations/jobs/:jobId
```

### Response

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "data": {
    "jobId": "f3c1a2b4-...",
    "status": "completed",
    "appId": "913335252",
    "sourceLocale": "en-US",
    "locales": ["tr", "de-DE", "ja"],
    "publish": false,
    "results": [
      {
        "locale": "tr",
        "status": "translated",
        "saved": true,
        "published": false,
        "fields": {
          "title": "Block Blast! - Blok Bulmaca",
          "subtitle": "Rahatlatıcı blok oyunu",
          "keywords": "blok,bulmaca,puzzle,zeka,rahatlatıcı,oyun",
          "promotional_text": "Milyonlarca oyuncuya katıl!",
          "description": "...",
          "whats_new": null
        }
      },
      {
        "locale": "ja",
        "status": "failed",
        "saved": false,
        "published": false,
        "fields": null,
        "error": "..."
      }
    ],
    "error": null,
    "createdAt": "2026-06-20T21:00:00.000Z",
    "updatedAt": "2026-06-20T21:00:42.000Z"
  }
}
```

### Job Fields

| Field     | Type   | Description                                                |
| --------- | ------ | ---------------------------------------------------------- |
| `status`  | string | `queued` \| `processing` \| `completed` \| `failed`        |
| `results` | array  | One entry per locale (see below); `null` until processing. |
| `error`   | string | Set only if the whole job failed.                          |

### Result Fields (per locale)

| Field       | Type    | Description                                                                  |
| ----------- | ------- | ---------------------------------------------------------------------------- |
| `locale`    | string  | The target locale.                                                           |
| `status`    | string  | `translated` (draft ready) \| `published` (pushed to store) \| `failed`.     |
| `fields`    | object  | The localized metadata, or `null` on failure.                                |
| `saved`     | boolean | Whether the translation was saved as a draft (only when the app is tracked). |
| `published` | boolean | Whether it was published to the live App Store Connect listing.              |
| `error`     | string  | Failure reason for this locale, when applicable.                             |

***

## MCP Tools

| Tool                    | Description                                       |
| ----------------------- | ------------------------------------------------- |
| `localize_app_metadata` | Start a batch translation run; returns a `jobId`. |
| `get_localization_job`  | Poll status and per-locale results for a run.     |

***

## Errors

| Status | Code               | When                                                         |
| ------ | ------------------ | ------------------------------------------------------------ |
| 400    | BAD\_REQUEST       | `locales` empty, or neither `appId` nor `userAppId` provided |
| 400    | TOO\_MANY\_LOCALES | More than 10 locales requested in one run                    |
| 400    | NO\_SOURCE         | No source copy to translate and the app isn't connected      |
| 401    | UNAUTHORIZED       | Missing or invalid credentials                               |
| 403    | FORBIDDEN          | Polling a job that belongs to another account                |
| 404    | APP\_NOT\_FOUND    | `userAppId` not found for this user                          |
| 404    | JOB\_NOT\_FOUND    | Unknown `jobId`                                              |
