# Copy and Translate Content Across Locales with the API

> Source: https://agilitycms.com/docs/developers/copy-and-translate-content-with-the-api

The Management API has two groups of endpoints that copy pages and content from one locale into others:

- **Initialize** copies pages, content lists or content items into other locales as they are.
- **Translate** copies them and machine-translates them into the target language.

These are the API side of the copy actions editors use in the app (see [Copying and Translating Content Across Locales](https://agilitycms.com/docs/editors/copying-and-translating-content)). Use them to seed a new locale, copy a whole content list in one call, or script the same copy across many items.

Each call queues a **batch** and returns its batch ID. The copy happens when Agility processes the batch, so you poll the batch to find out when it has finished and whether any item failed.

> **Which approach fits?** If you want to write the translated field values yourself (from your own translation service, a translation agency's files or an AI agent), save the item into the target locale with the same `contentID` instead. See [Creating Content and Pages in Other Locales](https://agilitycms.com/docs/developers/creating-content-and-pages-in-other-locales). Use Initialize and Translate when Agility should do the copying.

## The six operations

All six are `POST` requests. None of the paths has a `{locale}` segment: the source and target locales go in the request body.

| Operation | Endpoint | Items to copy | Target locales |
| --- | --- | --- | --- |
| Initialize content items | `/api/v1/instance/{guid}/initialize/content` | `contentVersionIds` | `languageCodeTargets` (several) |
| Initialize content lists | `/api/v1/instance/{guid}/initialize/contentlist` | `contentViewIds` | `languageCodeTarget` (one) |
| Initialize pages | `/api/v1/instance/{guid}/initialize/page` | `pageVersionIds` | `languageCodeTargets` (several) |
| Translate content items | `/api/v1/instance/{guid}/translate/content` | `contentVersionIds` | `languageCodeTargets` (several) |
| Translate content lists | `/api/v1/instance/{guid}/translate/contentlist` | `contentViewIds` | `languageCodeTarget` (one) |
| Translate pages | `/api/v1/instance/{guid}/translate/page` | `pageVersionIds` | `languageCodeTargets` (several) |

Every request and response schema is in the [Management API reference](https://agilitycms.com/docs/api-reference), under the **Initialize** and **Translation** tags.

### Base URL

Send requests to the Management API host for your instance's region. The suffix of your instance GUID tells you which one:

| GUID suffix | Region | Host |
| --- | --- | --- |
| `-u` | US | `https://mgmt.aglty.io` |
| `-us2` | US 2 | `https://mgmt-usa2.aglty.io` |
| `-c` | Canada | `https://mgmt-ca.aglty.io` |
| `-e` | Europe | `https://mgmt-eu.aglty.io` |
| `-a` | Australia | `https://mgmt-aus.aglty.io` |

Authenticate the same way as any other Management API call, with an OAuth access token or a [Personal Access Token](https://agilitycms.com/docs/developers/personal-access-tokens) in the `Authorization: Bearer` header. The call runs with that user's permissions.

## The request body

| Field | Type | Used by | Meaning |
| --- | --- | --- | --- |
| `languageCodeSource` | string | all six | The locale to copy from, for example `en-us` |
| `languageCodeTargets` | string array | content items, pages | The locales to copy into |
| `languageCodeTarget` | string | content lists, pages | One locale to copy into |
| `contentVersionIds` | integer array | content items | The **version IDs** of the items to copy |
| `pageVersionIds` | integer array | pages | The **version IDs** of the pages to copy |
| `contentViewIds` | integer array | content lists | The **container IDs** of the lists to copy |

> ⚠️ **Pages and content items are identified by version ID, not by page ID or content ID.** Read the item or page from the Management API first and take the `versionID` from its `properties`. Content lists are identified by their container ID.

**Pages accept several target locales.** The page requests have both `languageCodeTargets` and an older single `languageCodeTarget`. The API ignores `languageCodeTarget` when `languageCodeTargets` is supplied, so use `languageCodeTargets` for new code. (Multi-locale page copy arrived in the app in September 2026. If your copy of the OpenAPI spec predates that, it shows only `languageCodeTarget` for pages.)

**Content lists take one target locale per call.** To copy a list into three locales, make three calls.

### Example: translate two content items into two locales

```http
POST https://mgmt.aglty.io/api/v1/instance/{guid}/translate/content
Authorization: Bearer {token}
Content-Type: application/json

{
  "languageCodeSource": "en-us",
  "languageCodeTargets": ["fr-ca", "es"],
  "contentVersionIds": [10678, 10684]
}
```

### Example: copy pages into several locales without translating

```http
POST https://mgmt.aglty.io/api/v1/instance/{guid}/initialize/page
Authorization: Bearer {token}
Content-Type: application/json

{
  "languageCodeSource": "en-us",
  "languageCodeTargets": ["fr-ca", "es"],
  "pageVersionIds": [11165]
}
```

### The response

A successful call returns `200` with a single integer: the batch ID.

```json
48213
```

That only means the batch was queued. It does not mean the copy has finished, or that every item succeeded.

## Waiting for the batch

Poll the batch until it is processed:

```http
GET https://mgmt.aglty.io/api/v1/instance/{guid}/batch/48213
Authorization: Bearer {token}
```

The response is a `Batch` object. `expandItems` defaults to `true`, so it includes one entry per item. The fields you need:

| Field | What it tells you |
| --- | --- |
| `batchState` | `1` Pending, `2` In process, `3` Processed, `4` Deleted |
| `percentComplete`, `numItemsProcessed` | Progress while the batch runs |
| `operationType` | `20` for an Initialize batch, `19` for a Translate batch |
| `errorData`, `statusMessage` | Batch-level problems |
| `items[].errorMessage` | Why a single item failed |
| `items[].languageCode` | The locale an item entry belongs to |
| `items[].processedItemVersionID` | The version the batch produced for that item |

Wait until `batchState` is `3`, then check every item's `errorMessage`. Permissions are enforced per item when the batch is processed, so one item can fail (for example, one the calling user can't edit) while the rest succeed. Treat a processed batch with item errors as a partial success, not a failure of the whole call.

Before you run a copy across a whole list or a new market, try it on one or two items and check the result in the target locale.

## SDK support

### .NET Management SDK 2.0

Version 2.0 of `Agility.Management.SDK` wraps all six operations in `client.Localization`:

| Method | Operation |
| --- | --- |
| `InitializeContentItemsAsync` | Initialize content items |
| `InitializeContentListsAsync` | Initialize content lists |
| `InitializePagesAsync` | Initialize pages |
| `TranslateContentItemsAsync` | Translate content items |
| `TranslateContentListsAsync` | Translate content lists |
| `TranslatePagesAsync` | Translate pages |

Each method waits for the batch by default (`waitForBatch: true`) and returns a `BatchResult`. If the batch finishes with failed items, the SDK throws `AgilityBatchException`, whose `Batch` property holds the items that succeeded and the ones that didn't. Full examples are in [Management SDK - Localization](https://agilitycms.com/docs/dotNet/management-sdk-dotnet-localization).

### JavaScript Management SDK

`@agility/management-sdk` (0.1.40, the current version on 2026-10-03) has no methods for Initialize or Translate. Call the REST endpoints directly, then poll with the SDK's `client.batchMethods.getBatch(batchID, guid)`:

```typescript
const res = await fetch(
  `https://mgmt.aglty.io/api/v1/instance/${guid}/initialize/content`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${token}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      languageCodeSource: "en-us",
      languageCodeTargets: ["fr-ca"],
      contentVersionIds: [10678],
    }),
  }
)
const batchID: number = await res.json()

// Poll until batchState is 3 (Processed), then check each item's errorMessage
const batch = await client.batchMethods.getBatch(batchID, guid)
```

## Choosing a way to copy content

| You want to | Use |
| --- | --- |
| Copy one item, a selection, a page or a whole locale by hand | The app: [Copying and Translating Content Across Locales](https://agilitycms.com/docs/editors/copying-and-translating-content) |
| Copy many items, lists or pages from a script, as they are | Initialize |
| Copy them and have Agility machine-translate them | Translate |
| Write your own translated values into the target locale | Save with the same `contentID`: [Creating Content and Pages in Other Locales](https://agilitycms.com/docs/developers/creating-content-and-pages-in-other-locales) |
| Have an AI assistant draft translations for review | [Draft Translations for Another Locale with AI](https://agilitycms.com/docs/editors/ai-recipe-translate-content) |

## Related

- [Choosing a Localization Strategy](https://agilitycms.com/docs/developers/choosing-a-localization-strategy)
- [Multi-Locale Guide](https://agilitycms.com/docs/developers/multi-locale-guide)
- [Management SDK - Localization (.NET)](https://agilitycms.com/docs/dotNet/management-sdk-dotnet-localization)
- [Management API reference](https://agilitycms.com/docs/api-reference)
