Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,121 changes: 1,082 additions & 39 deletions api-reference/openapi.json

Large diffs are not rendered by default.

844 changes: 839 additions & 5 deletions api-reference/openapi.yaml

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions api-reference/translation-memory/delete-a-translation-memory.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
openapi: delete /v3/translation_memories/{translation_memory_id}
title: "Delete a translation memory"
description: "Learn how to permanently delete a translation memory and all of its segments."
---

Deletion is permanent and removes every segment in the translation memory. There is no recovery step and no trash to restore from. [Export the translation memory](/api-reference/translation-memory/export-a-translation-memory) first if you need a copy.

<Warning>
Translation requests that pass a deleted `translation_memory_id` fail. Update your integration to stop referencing the ID before you delete it, not after.
</Warning>

A successful delete returns `204 No Content` with an empty body, so check the status code rather than trying to parse a response.
74 changes: 74 additions & 0 deletions api-reference/translation-memory/export-a-translation-memory.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
openapi: post /v3/translation_memories/{translation_memory_id}/export
title: "Export a translation memory"
description: "Learn how to export a translation memory as a TMX file, including how DeepL reuses recent exports and when it returns 409."
---

Exporting runs as a background job. This request starts the job and returns a `job_id`; the TMX file is downloaded separately once the job completes. TMX is currently the only export format, so there is no format parameter.

## Exporting a translation memory

<Steps>
<Step title="Start the export">
```bash
curl -X POST "https://api.deepl.com/v3/translation_memories/a74d88fb-ed2a-4943-a664-a4512398b994/export" \
-H "Authorization: DeepL-Auth-Key <yourAuthKey>"
```

```json
{
"job_id": "7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13",
"parameters": {
"translation_memory_id": "a74d88fb-ed2a-4943-a664-a4512398b994"
}
}
```
</Step>

<Step title="Poll the job and download the file">
Check [the job](/api-reference/translation-memory/retrieve-a-translation-memory-job) until `status` is `completed`, then download the file from `download_url` on the job result.

```bash
curl -X GET "https://api.deepl.com/v3/translation_memories/jobs/7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13" \
-H "Authorization: DeepL-Auth-Key <yourAuthKey>"
```

```json
{
"job_id": "7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13",
"product": "translation_memory",
"operation": "export",
"creation_time": "2026-08-06T15:04:25.223Z",
"updated_time": "2026-08-06T15:05:02.771Z",
"parameters": {
"translation_memory_id": "a74d88fb-ed2a-4943-a664-a4512398b994"
},
"results": [
{
"status": "completed",
"download_url": "https://assets.deepl.com/download/7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13",
"expires_at": "2026-08-06T16:05:02.771Z"
}
]
}
```
</Step>
</Steps>

## Handling 200, 202, and 409

The status code tells you whether a new job was started:

| **Status** | **Meaning** | **What to do** |
|---|---|---|
| `202 Accepted` | A new export job was started | Poll `job_id` |
| `200 OK` | A recent export was still available and was reused | Poll `job_id` |
| `409 Conflict` | An export of this translation memory is already running | Poll the job you already have; do not retry the export |

Treat `200` and `202` the same way. Both return a usable `job_id`, and the only difference is whether DeepL did the work again.

<Warning>
Do not retry on `409`. Retrying will keep conflicting with the in-progress export. Poll the `job_id` from your earlier request instead, and hold onto that ID so you can recover without a retry loop.
</Warning>

`download_url` is short-lived and stops working at `expires_at`. Download the file when the job completes rather than storing the URL for later. If the URL has expired, start a new export.
102 changes: 102 additions & 0 deletions api-reference/translation-memory/import-a-translation-memory.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
openapi: post /v3/translation_memories/import
title: "Import a translation memory"
description: "Learn how to create a translation memory by declaring a TMX file, uploading it to a signed URL, and polling the import job."
---

This is how you create a translation memory: there is no endpoint that creates an empty one. Importing a TMX file creates the translation memory and fills it in a single job.

The request body describes the file you intend to upload; it does not carry the file. DeepL returns a signed `upload_url`, you upload the file to that URL, and processing starts automatically once the upload finishes.

## Importing a file

<Steps>
<Step title="Create the import job">
Send the file's name and size in bytes. The response returns the `job_id`, the `upload_url`, and the `expires_at` deadline for the upload.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we document what the max content length is?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just added here and also in the openapi specs


```bash
curl -X POST "https://api.deepl.com/v3/translation_memories/import" \
-H "Authorization: DeepL-Auth-Key <yourAuthKey>" \
-H "Content-Type: application/json" \
-d '{
"source_file": {
"file_name": "legal.tmx",
"content_type": "application/xml",
"content_length": 1024
},
"parameters": {
"display_name": "Legal"
}
}'
```

```json
{
"job_id": "0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40",
"upload_url": "https://assets.deepl.com/upload/0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40",
"expires_at": "2026-08-06T15:34:25.223Z"
}
```
</Step>

<Step title="Upload the TMX file">
`PUT` the file to `upload_url` before `expires_at`. The URL is already signed, so this request carries no DeepL credentials.

```bash
curl -X PUT "https://assets.deepl.com/upload/0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40" \
-H "Content-Type: application/xml" \
--data-binary @legal.tmx
```

A successful upload returns no DeepL response body. Processing begins on its own, so there is no call to confirm the upload.
</Step>

<Step title="Poll the job">
Check [the job](/api-reference/translation-memory/retrieve-a-translation-memory-job) until `status` is `completed` or `failed`.

```bash
curl -X GET "https://api.deepl.com/v3/translation_memories/jobs/0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40" \
-H "Authorization: DeepL-Auth-Key <yourAuthKey>"
```

On completion, the job result carries the `translation_memory_id` of the new translation memory.

```json
{
"job_id": "0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40",
"product": "translation_memory",
"operation": "import",
"creation_time": "2026-08-06T15:04:25.223Z",
"updated_time": "2026-08-06T15:06:11.418Z",
"source_file": {
"content_type": "application/xml",
"content_length": 1024
},
"parameters": {
"display_name": "Legal"
},
"results": [
{
"status": "completed",
"translation_memory_id": "a74d88fb-ed2a-4943-a664-a4512398b994",
"skipped_segment_count": 12
}
]
}
```
</Step>
</Steps>

## Common mistakes

`content_length` must be greater than 0 and at most 1 GB. An oversize value is rejected as `400 Bad Request` with a message pointing at `source_file.content_length`.

`file_name` accepts at most 100 characters and `content_type` at most 127, both rejected as `400 Bad Request` if longer. TMX is the only supported format, so leave `content_type` unset or set it to `application/xml`.

A `202 Accepted` means the job exists, not that a translation memory does. Until the upload completes and the job reports `completed`, no translation memory has been created and there is no ID to reference.

If `expires_at` passes before you upload, the signed URL stops working. Create a new import job to get a fresh URL rather than retrying the old one.

A non-zero `skipped_segment_count` on a completed import is not a failure. Some segments were not stored, for example because they were malformed or duplicated an existing segment, but the rest imported normally. Check the value if you are reconciling segment counts against your source file.

If your account has reached its translation memory limit, the request returns `456`. Delete a translation memory before importing another.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
openapi: get /v3/translation_memories/{translation_memory_id}/segments
title: "List translation memory segments"
description: "Learn how to page through the source segments and translations stored in a translation memory using cursor-based pagination."
---

## Paginating with cursors

This endpoint pages with an opaque cursor, unlike [List translation memories](/api-reference/translation-memory/list-translation-memories), which uses numbered `page` values. Omit `page_cursor` on the first request, then pass each response's `next_page_cursor` to get the following page. A response with no `next_page_cursor` is the last page.

```bash
# First page
curl -X GET "https://api.deepl.com/v3/translation_memories/a74d88fb-ed2a-4943-a664-a4512398b994/segments?page_size=50" \
-H "Authorization: DeepL-Auth-Key <yourAuthKey>"

# Next page, using next_page_cursor from the response above
curl -X GET "https://api.deepl.com/v3/translation_memories/a74d88fb-ed2a-4943-a664-a4512398b994/segments?page_size=50&page_cursor=eyJvZmZzZXQiOjUwfQ" \
-H "Authorization: DeepL-Auth-Key <yourAuthKey>"
```

Treat the cursor as opaque. Its format is not part of the API contract, so do not construct, decode, or increment it yourself. Cursors also encode the filter they were issued under: if you change `filter_text` or `filter_case_sensitive`, start again without a cursor.

<Warning>
`segment_count` is the total for the whole translation memory and is not reduced by `filter_text`. A filtered request still reports the full count, so do not use it to decide how many pages of filtered results to expect. Stop paginating when `next_page_cursor` is absent.
</Warning>

## Filtering

`filter_text` matches against both source text and every target translation, and must be at least 2 characters. Matching is case-insensitive unless you set `filter_case_sensitive=true`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
openapi: get /v3/translation_memories/jobs/{job_id}
title: "Retrieve an import or export job"
description: "Learn how to poll a translation memory import or export job and read its status values and results."
---

One endpoint covers both [import](/api-reference/translation-memory/import-a-translation-memory) and [export](/api-reference/translation-memory/export-a-translation-memory) jobs. Read `operation` to tell them apart, then read `results[0]` for the status and, once the job finishes, its output.

`results` always holds exactly one entry. It is an array so that jobs producing multiple outputs can be added later without a breaking change, so index into it rather than assuming a single object.

## Status values

| **Status** | **Applies to** | **Meaning** |
|---|---|---|
| `awaiting_input` | Import | The job exists but the file has not been uploaded yet. `status_metadata.required_action` says what is missing. |
| `processing` | Import, export | The file was received and is being processed. |
| `completed` | Import, export | The job succeeded. Read `translation_memory_id` (import) or `download_url` (export). |
| `failed` | Import, export | The job did not finish. `error.message` says why. |
| `expired` | Import, export | The job is too old to act on. Create a new one. |

Poll until the status is `completed`, `failed`, or `expired`. The other states are transient and will change on their own.

## Which fields to expect

Fields that do not apply to a job's `operation` are omitted rather than returned as `null`, so check for a field's presence before reading it:

- **Import jobs** include `source_file` and `parameters.display_name`. On completion, the result adds `translation_memory_id` and may add `skipped_segment_count`.
- **Export jobs** include `parameters.translation_memory_id`. On completion, the result adds `download_url` and `expires_at`.

<Note>
An import that sits at `awaiting_input` means DeepL is still waiting for the file. Upload it to the `upload_url` from the import response; the status will not advance on its own.
</Note>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
openapi: get /v3/translation_memories/{translation_memory_id}
title: "Retrieve a translation memory"
description: "Learn how to fetch a single translation memory's languages, segment count, and timestamps by its ID."
---

This returns the translation memory's metadata, not its contents. To read the stored segments, use [List translation memory segments](/api-reference/translation-memory/list-translation-memory-segments).

A translation memory that belongs to another account returns `404 Not Found` rather than `403 Forbidden`, so a 404 does not confirm that the ID is unused.
8 changes: 7 additions & 1 deletion docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,13 @@
{
"group": "Translation Memory",
"pages": [
"api-reference/translation-memory/list-translation-memories"
"api-reference/translation-memory/list-translation-memories",
"api-reference/translation-memory/retrieve-a-translation-memory",
"api-reference/translation-memory/list-translation-memory-segments",
"api-reference/translation-memory/import-a-translation-memory",
"api-reference/translation-memory/export-a-translation-memory",
"api-reference/translation-memory/retrieve-a-translation-memory-job",
"api-reference/translation-memory/delete-a-translation-memory"
],
"drilldown": false
}
Expand Down
15 changes: 14 additions & 1 deletion docs/admin/permission-scopes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,23 @@ Each scope grants a [scoped API key](/docs/admin/api-key-permissions) access to
| `DELETE` | [`/v3/style_rules/{style_id}/custom_instructions/{instruction_id}`](/api-reference/style-rules/delete-custom-instruction) |
</Accordion>

<Accordion title="Retrieve translation memories (translation_memories:read)">
<Accordion title="Retrieve and export translation memories (translation_memories:read)">
| **Method** | **Endpoint** |
| --- | --- |
| `GET` | [`/v3/translation_memories`](/api-reference/translation-memory/list-translation-memories) |
| `GET` | [`/v3/translation_memories/{translation_memory_id}`](/api-reference/translation-memory/retrieve-a-translation-memory) |
| `GET` | [`/v3/translation_memories/{translation_memory_id}/segments`](/api-reference/translation-memory/list-translation-memory-segments) |
| `POST` | [`/v3/translation_memories/{translation_memory_id}/export`](/api-reference/translation-memory/export-a-translation-memory) |
| `GET` | [`/v3/translation_memories/jobs/{job_id}`](/api-reference/translation-memory/retrieve-a-translation-memory-job) |

Exporting is a read operation, so it needs `translation_memories:read` rather than `translation_memories:write`, even though it uses `POST`.
</Accordion>

<Accordion title="Import and delete translation memories (translation_memories:write)">
| **Method** | **Endpoint** |
| --- | --- |
| `POST` | [`/v3/translation_memories/import`](/api-reference/translation-memory/import-a-translation-memory) |
| `DELETE` | [`/v3/translation_memories/{translation_memory_id}`](/api-reference/translation-memory/delete-a-translation-memory) |
</Accordion>

<Accordion title="Retrieve languages and resources (languages:read)">
Expand Down
12 changes: 11 additions & 1 deletion docs/resources/roadmap-and-release-notes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,21 @@ rss: true
---

<Update label="In active development">
- Support for uploading, modifying, and deleting [translation memories](/docs/customize/using-translation-memories) via API
- Editing the contents of an existing [translation memory](/docs/customize/using-translation-memories) via API
- Usage reporting by language pair
</Update>

<Update label="August 2026">
## August 11 - Translation Memory Management API
- You can now create, inspect, export, and delete [translation memories](/docs/customize/using-translation-memories) through the API. Translation memories store previously translated segments so the same source text produces consistent output across projects. Previously, the API could only list the translation memories on your account, and everything else had to be done in the DeepL UI.
- [`POST /v3/translation_memories/import`](/api-reference/translation-memory/import-a-translation-memory) creates a translation memory from a TMX file. The request declares the file and returns a signed upload URL plus a `job_id`; you upload the file to that URL and poll the job for the new `translation_memory_id`.
- [`POST /v3/translation_memories/{translation_memory_id}/export`](/api-reference/translation-memory/export-a-translation-memory) exports a translation memory as TMX, also as a background job.
- [`GET /v3/translation_memories/jobs/{job_id}`](/api-reference/translation-memory/retrieve-a-translation-memory-job) reports the status of both import and export jobs.
- [`GET /v3/translation_memories/{translation_memory_id}`](/api-reference/translation-memory/retrieve-a-translation-memory) retrieves a single translation memory, and [`GET /v3/translation_memories/{translation_memory_id}/segments`](/api-reference/translation-memory/list-translation-memory-segments) pages through its stored segments with cursor-based pagination.
- [`DELETE /v3/translation_memories/{translation_memory_id}`](/api-reference/translation-memory/delete-a-translation-memory) permanently deletes a translation memory and all of its segments.
- Reading requires an API key with the `translation_memories:read` scope; importing and deleting require `translation_memories:write`. See [permission scopes](/docs/admin/permission-scopes).
- Editing the contents of an existing translation memory is not yet supported. To change what a translation memory contains, import a new one.

## August 5 - Custom Tag-Level CSV Export
- Usage broken down by [custom reporting tag](/docs/learning-how-tos/examples-and-guides/how-to-use-custom-reporting-tags) can now be exported as a CSV report from the account UI, in addition to being available through the [custom tag usage analytics endpoint](/api-reference/admin-api/get-custom-tag-usage-analytics) in the Admin API. Custom tags attribute API usage to a team, project, or other category via the `X-DeepL-Reporting-Tag` request header.
- The "Download CSV usage report" button on the [API Keys & Limits tab](https://www.deepl.com/your-account/keys) and the [API Usage tab](https://www.deepl.com/your-account/usage) is now a dropdown: select "Custom tag-level report" or "API key-level report".
Expand Down
Loading