# Get custom tag usage analytics Source: https://developers.deepl.com/api-reference/admin-api/get-custom-tag-usage-analytics get /v2/admin/analytics/custom-tags Retrieve usage statistics broken down by custom tags within a specified date range. Optionally aggregate results by day or over the entire period. Results are paginated; use the `page` parameter with the `next_page` value from a previous response to retrieve subsequent pages. ## Date range Specify the reporting window using the required `start_date` and `end_date` parameters in ISO 8601 date format (e.g., `2026-05-01`). The maximum date range is 366 days. ## Aggregation The optional `aggregate_by` parameter controls how usage is grouped: | **Value** | **Description** | | :----------------- | :------------------------------------------------------------ | | `period` (default) | Returns total usage per custom tag over the entire date range | | `day` | Returns usage per custom tag broken down by individual day | Use `period` when you need a summary for billing or reporting. Use `day` when you need to analyze usage trends over time. ## Response structure The response contains a `custom_tag_usage_report` object with the following fields: | **Field** | **Description** | | :------------- | :-------------------------------------------------------------------------------- | | `aggregate_by` | The aggregation method used (`period` or `day`) | | `start_date` | Start of the reporting period (ISO 8601 datetime) | | `end_date` | End of the reporting period (ISO 8601 datetime) | | `next_page` | Integer cursor for the next page of results. `null` if there are no further pages | | `usage` | Array of usage entries, one per custom tag | Each entry in `usage` includes: * `custom_tag`: the tag identifier * `usage_date`: the day the usage was recorded (ISO 8601 datetime). Only returned when `aggregate_by` is `day` * `breakdown`: usage split by service type ### Usage breakdown | **Field** | **Description** | | :---------------------------- | :----------------------------------------------------------------------------------- | | `total_characters` | Combined character usage across character-based services | | `text_translation_characters` | Characters used for text translation | | `text_improvement_characters` | Characters used for text improvement (rephrasing) | | `speech_to_text_minutes` | Minutes of speech-to-text usage. Fractional values are returned, for example `12.5` | | `speech_to_speech_minutes` | Minutes of speech-to-speech usage. Fractional values are returned, for example `4.2` | Voice usage is reported in minutes and is not included in `total_characters`, which covers character-based services only. Custom tag data is recorded only for text translation and Voice API requests. Support for other request types will be added in a future update. ## Pagination Results are paginated. If the response includes a non-null `next_page` value, pass it as the `page` parameter in your next request to retrieve the following page. Continue until `next_page` is `null`. # Get usage analytics Source: https://developers.deepl.com/api-reference/admin-api/get-usage-analytics get /v2/admin/analytics Retrieve usage statistics for the organization within a specified date range. Optionally group the results by API key or by API key and day. ## Date range Specify the reporting window using the required `start_date` and `end_date` parameters. Both support the following formats (all in UTC): | **Format** | **Example** | | :-------------------- | :-------------------- | | `YYYY-MM-DD` | `2025-09-29` | | `YYYY-MM-DDTHH:MM:SS` | `2025-09-29T14:30:00` | The maximum date range is 366 days. ## Grouping The optional `group_by` parameter controls how results are organized: | **Value** | **Description** | | :------------ | :---------------------------------------------------------------------------------- | | (default) | Returns total usage for the entire date range across all API keys | | `key` | Groups usage by individual API key, showing totals for each key over the date range | | `key_and_day` | Groups usage by both API key and day, providing daily breakdowns per key | ## Response structure All responses include a breakdown of usage by service type: | **Field** | **Description** | | :-------------------------------- | :------------------------------------------------ | | `total_characters` | Combined character usage across all services | | `text_translation_characters` | Characters used for text translation | | `document_translation_characters` | Characters used for document translation | | `text_improvement_characters` | Characters used for text improvement (rephrasing) | | `speech_to_text_minutes` | Duration of speech-to-text usage in minutes | # Create a developer key Source: https://developers.deepl.com/api-reference/admin-api/managing-developer-keys/create-key post /v2/admin/developer-keys # Deactivate a developer key Source: https://developers.deepl.com/api-reference/admin-api/managing-developer-keys/deactivate-key put /v2/admin/developer-keys/deactivate # Get developer keys Source: https://developers.deepl.com/api-reference/admin-api/managing-developer-keys/get-keys get /v2/admin/developer-keys # Rename a developer key Source: https://developers.deepl.com/api-reference/admin-api/managing-developer-keys/rename-key put /v2/admin/developer-keys/label # Set developer key limits Source: https://developers.deepl.com/api-reference/admin-api/managing-developer-keys/set-usage-limits put /v2/admin/developer-keys/limits # Detect Language Source: https://developers.deepl.com/api-reference/detect-language/detect-language-beta Detect the language of text using DeepL's language detection service **Beta Feature — Opt-in Required** The Detect Language API is currently in beta and requires explicit opt-in for your account. To request access, please [send a support request](https://support.deepl.com/hc/en-us/requests/new). The Detect Language API allows you to identify the language of text content. This is a beta feature that requires special access permissions and is currently available only to select accounts. ## Best Practices 1. **Text Length**: Provide sufficient text for accurate detection. Very short texts may result in `insufficient_confidence` or `no_language_detected` status. 2. **Batch Processing**: You can detect multiple texts in a single request by providing an array of strings. 3. **Error Handling**: Check the `detection_status` field to understand the result. Only when `detection_status` is `success` will the `detected_language` field contain a valid language code. 4. **Rate Limiting**: Be mindful of API rate limits when making frequent requests. ## Feature Availability This is a **beta feature** that requires: * Account enrollment in the language detection beta program * Compatible API key with appropriate permissions Reach out to your DeepL contact to request access to the language detection API beta. ## Endpoint ``` POST /v3/detect/language ``` ## Authentication This endpoint requires authentication using the `DeepL-Auth-Key` header with a valid API key that has access to the language detection feature. ## Request Formats ```bash theme={null} curl -X POST "https://api.deepl.com/v3/detect/language" \ -H "Authorization: DeepL-Auth-Key YOUR_AUTH_KEY" \ -H "Content-Type: application/json" \ -d '{ "text": [ "Hello, how are you?", "Hola, ¿cómo estás?" ] }' ``` ```bash theme={null} curl -X POST "https://api.deepl.com/v3/detect/language" \ -H "Authorization: DeepL-Auth-Key YOUR_AUTH_KEY" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "text=Hello, how are you?" \ -d "text=Hola, ¿cómo estás?" ``` ```http theme={null} POST /v3/detect/language HTTP/1.1 Host: api.deepl.com Authorization: DeepL-Auth-Key YOUR_AUTH_KEY Content-Type: application/json Content-Length: 84 { "text": [ "Hello, how are you?", "Hola, ¿cómo estás?" ] } ``` ### Request Parameters List of text strings to detect languages for. Each string will be processed separately. The response detections are returned in the same order as they are requested. Each of the parameter values may contain multiple sentences. Up to 50 texts can sent in one request. ## Response Format ```json theme={null} { "results": [ { "detected_language": "en", "detection_status": "success" }, { "detected_language": "es", "detection_status": "success" } ] } ``` Example responses for different detection statuses: ```json theme={null} { "results": [ { "detected_language": "en", "detection_status": "success" }, { "detection_status": "insufficient_confidence" }, { "detection_status": "unsupported_language" }, { "detection_status": "no_language_detected" } ] } ``` ```json theme={null} { "message": "Missing parameter text." } ``` This error occurs when: * The inputs in the request are malformed ```json theme={null} { "message": "Forbidden" } ``` This error occurs when: * The API key doesn't have access to the language detection feature * The account doesn't have the required permissions * The feature flag is not enabled for the account ```json theme={null} { "message": "Called with an unsupported media type. Supported media types: application/json or application/x-www-form-urlencoded. Please set the Content-Type header to a supported type." } ``` This error occurs when: * The request is in an unsupported format ```json theme={null} { "message": "Internal server error." } ``` This error occurs when there are issues with the internal language detection service. ### Response Fields Array of detection results, one for each input text. Each result object contains a `detected_language` and a `detection_status` field. The detected language code when detection is successful. **Language Codes** (when `detection_successful` is `true`): * Standard BCP-47 language codes: one of `"en"`, `"de"`, `"fr"`, `"es"`, `"pt"`, `"it"`, `"nl"`, `"pl"`, `"ru"`, `"zh"`, `"ja"`, `"bg"`, `"cs"`, `"da"`, `"el"`, `"et"`, `"fi"`, `"hu"`, `"lt"`, `"lv"`, `"ro"`, `"sk"`, `"sl"`, `"sv"`, `"tr"`, `"id"`, `"uk"`, `"ko"`, `"nb"`, `"ar"`, `"vi"`, `"he"`, `"th"` * See [supported languages](/docs/getting-started/supported-languages) for more info When `detection_status` is not `success`, this field is not defined. Indicates the result of language detection. Possible values: * `"success"` - Language successfully detected and returned in `detected_language` * `"insufficient_confidence"` - Detection confidence too low to return a reliable single result * `"unsupported_language"` - A language was detected but it's not supported by DeepL's translation service * `"no_language_detected"` - No language could be detected from the input text ## Limitations * Only supports detection of languages that are available in DeepL's translation service * Requires high confidence to return the language * Only returns the first top language guess for any single text, may not work well with language variants that are very similar to each other * Limited to text-based detection (no support for document formats) # Check document status Source: https://developers.deepl.com/api-reference/document/check-document-status post /v2/document/{document_id} # Download translated document Source: https://developers.deepl.com/api-reference/document/download-translated-document post /v2/document/{document_id}/result # Upload and translate a document Source: https://developers.deepl.com/api-reference/document/upload-and-translate-a-document post /v2/document # Create a glossary Source: https://developers.deepl.com/api-reference/glossaries/create-a-glossary post /v2/glossaries # Delete a glossary Source: https://developers.deepl.com/api-reference/glossaries/delete-a-glossary delete /v2/glossaries/{glossary_id} Deletes the specified glossary. # List all glossaries Source: https://developers.deepl.com/api-reference/glossaries/list-all-glossaries get /v2/glossaries List all glossaries and their meta-information, but not the glossary entries. # Retrieve glossary details Source: https://developers.deepl.com/api-reference/glossaries/retrieve-glossary-details get /v2/glossaries/{glossary_id} Retrieve meta information for a single glossary, omitting the glossary entries. # Retrieve glossary entries Source: https://developers.deepl.com/api-reference/glossaries/retrieve-glossary-entries get /v2/glossaries/{glossary_id}/entries List the entries of a single glossary in the format specified by the `Accept` header. # Correct text Source: https://developers.deepl.com/api-reference/improve-text/correct-text post /v2/write/correct Fixes spelling and grammar errors with minimal changes to wording. The `/v2/write/correct` endpoint fixes spelling and grammar errors in one or more texts with minimal changes to wording, matching the "Corrections Only" mode in the DeepL Translator UI. It does not accept `writing_style` or `tone` and does not rephrase sentences for clarity. The broader [`/v2/write/rephrase`](/api-reference/improve-text/request-text-improvement) endpoint supports those parameters. # Improve text Source: https://developers.deepl.com/api-reference/improve-text/request-text-improvement post /v2/write/rephrase Improves text by correcting spelling and grammar and rewriting for a target writing style or tone. The `/v2/write/rephrase` endpoint improves one or more texts by correcting spelling and grammar and rephrasing sentences for clarity. With the optional `writing_style` or `tone` parameters, the rewrite targets a specific style or tone. Its corrections-only counterpart, [`/v2/write/correct`](/api-reference/improve-text/correct-text), is limited to fixing mistakes with minimal changes to wording. # Retrieve languages Source: https://developers.deepl.com/api-reference/languages/retrieve-languages-by-resource get /v3/languages Returns languages supported by the specified DeepL API resource. Each language indicates whether it can be used as a source language, a target language, or both, along with the features it supports for that resource. # Retrieve language resources Source: https://developers.deepl.com/api-reference/languages/retrieve-resources get /v3/languages/resources Learn how to retrieve the list of DeepL API resources and their supported language features. # Retrieve supported languages (v2) Source: https://developers.deepl.com/api-reference/languages/retrieve-supported-languages get /v2/languages **Deprecated.** Use `GET /v3/languages?resource=translate_text` (or the appropriate resource) instead. See the [migration guide](https://developers.deepl.com/docs/languages/migrating-from-v2-languages) for details. **`/v2/languages` is deprecated.** Use [`GET /v3/languages`](/docs/languages/using-the-languages-api) instead. See the [migration guide](/docs/languages/migrating-from-v2-languages) for details. We do not yet have a timeline for sunsetting /v2/languages, and we'll share more detail on this topic by the end of Q2 2026. # Create a glossary Source: https://developers.deepl.com/api-reference/multilingual-glossaries/create-a-glossary post /v3/glossaries # Delete a glossary Source: https://developers.deepl.com/api-reference/multilingual-glossaries/delete-a-glossary delete /v3/glossaries/{glossary_id} Deletes the specified glossary. # Deletes the dictionary associated with the given language pair with the given glossary ID. Source: https://developers.deepl.com/api-reference/multilingual-glossaries/deletes-the-dictionary-associated-with-the-given-language-pair-with-the-given-glossary-id delete /v3/glossaries/{glossary_id}/dictionaries # Edit glossary details Source: https://developers.deepl.com/api-reference/multilingual-glossaries/edit-glossary-details patch /v3/glossaries/{glossary_id} Edit glossary details, such as name or a dictionary for a source and target language. # List all glossaries Source: https://developers.deepl.com/api-reference/multilingual-glossaries/list-all-glossaries get /v3/glossaries List all glossaries and their meta-information, but not the glossary entries. # List language pairs supported for glossaries Source: https://developers.deepl.com/api-reference/multilingual-glossaries/list-language-pairs-supported-by-glossaries get /v2/glossary-language-pairs **Deprecated.** Use `GET /v3/languages?resource=glossary` instead, which returns per-language availability including source and target roles. Retrieve the list of language pairs supported by the glossary feature. **`/v2/glossary-language-pairs` is deprecated.** Use [`GET /v3/languages?resource=glossary`](/docs/languages/using-the-languages-api) instead. See the [migration guide](/docs/languages/migrating-from-v2-languages) for details. This endpoint returns all possible language pairs for glossaries. [This list of supported languages](/docs/getting-started/supported-languages) may also be useful. # Replaces or creates a dictionary in the glossary with the specified entries. Source: https://developers.deepl.com/api-reference/multilingual-glossaries/replaces-or-creates-a-dictionary-in-the-glossary-with-the-specified-entries put /v3/glossaries/{glossary_id}/dictionaries # Retrieve glossary details Source: https://developers.deepl.com/api-reference/multilingual-glossaries/retrieve-glossary-details get /v3/glossaries/{glossary_id} Retrieve meta information for a single glossary, omitting the glossary entries. # Retrieve glossary entries Source: https://developers.deepl.com/api-reference/multilingual-glossaries/retrieve-glossary-entries get /v3/glossaries/{glossary_id}/entries List the entries of a single glossary in tsv format. # Create Spoken Terms collection Source: https://developers.deepl.com/api-reference/spoken-terms/create-spoken-terms-collection post /v3/spoken-terms Create a new Spoken Terms collection with one or more term lists for voice transcription # Delete Spoken Terms collection Source: https://developers.deepl.com/api-reference/spoken-terms/delete-spoken-terms delete /v3/spoken-terms/{spoken_terms_id} Remove a Spoken Terms collection and all its term lists # Delete a term list Source: https://developers.deepl.com/api-reference/spoken-terms/delete-spoken-terms-list delete /v3/spoken-terms/{spoken_terms_id}/term-lists Remove a term list for a specific language from the Spoken Terms collection # Edit Spoken Terms collection details Source: https://developers.deepl.com/api-reference/spoken-terms/edit-spoken-terms-details patch /v3/spoken-terms/{spoken_terms_id} Update the collection name or merge new terms into an existing term list # List all Spoken Terms collections Source: https://developers.deepl.com/api-reference/spoken-terms/list-all-spoken-terms get /v3/spoken-terms Retrieve all Spoken Terms collections and their metadata # Replace or create a term list Source: https://developers.deepl.com/api-reference/spoken-terms/replace-or-create-term-list put /v3/spoken-terms/{spoken_terms_id}/term-lists Replace an existing term list for a language, or create a new one if it doesn't exist # Retrieve Spoken Terms collection details Source: https://developers.deepl.com/api-reference/spoken-terms/retrieve-spoken-terms-details get /v3/spoken-terms/{spoken_terms_id} Get metadata for a specific Spoken Terms collection, including term list information # Retrieve Spoken Terms entries Source: https://developers.deepl.com/api-reference/spoken-terms/retrieve-spoken-terms-entries get /v3/spoken-terms/{spoken_terms_id}/entries Get the actual terms for a specific language from a Spoken Terms collection # Create a custom instruction Source: https://developers.deepl.com/api-reference/style-rules/create-custom-instruction post /v3/style_rules/{style_id}/custom_instructions # Create a style rule list Source: https://developers.deepl.com/api-reference/style-rules/create-style-rule post /v3/style_rules # Delete a custom instruction Source: https://developers.deepl.com/api-reference/style-rules/delete-custom-instruction delete /v3/style_rules/{style_id}/custom_instructions/{instruction_id} # Delete a style rule list Source: https://developers.deepl.com/api-reference/style-rules/delete-style-rule delete /v3/style_rules/{style_id} # Get a custom instruction Source: https://developers.deepl.com/api-reference/style-rules/get-custom-instruction get /v3/style_rules/{style_id}/custom_instructions/{instruction_id} # Get a style rule list Source: https://developers.deepl.com/api-reference/style-rules/get-style-rule get /v3/style_rules/{style_id} # Get all style rule lists Source: https://developers.deepl.com/api-reference/style-rules/list-all-style-rules get /v3/style_rules # Replace configured rules Source: https://developers.deepl.com/api-reference/style-rules/update-configured-rules put /v3/style_rules/{style_id}/configured_rules # Replace a custom instruction Source: https://developers.deepl.com/api-reference/style-rules/update-custom-instruction put /v3/style_rules/{style_id}/custom_instructions/{instruction_id} # Update a style rule list's name Source: https://developers.deepl.com/api-reference/style-rules/update-style-rule patch /v3/style_rules/{style_id} # Translate text Source: https://developers.deepl.com/api-reference/translate/request-translation post /v2/translate # Delete a translation memory Source: https://developers.deepl.com/api-reference/translation-memory/delete-a-translation-memory delete /v3/translation_memories/{translation_memory_id} 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. Translation requests that pass a deleted `translation_memory_id` fail. Update your integration to stop referencing the ID before you delete it, not after. A successful delete returns `204 No Content` with an empty body, so check the status code rather than trying to parse a response. # Export a translation memory Source: https://developers.deepl.com/api-reference/translation-memory/export-a-translation-memory post /v3/translation_memories/{translation_memory_id}/export 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 ```bash theme={null} curl -X POST "https://api.deepl.com/v3/translation_memories/a74d88fb-ed2a-4943-a664-a4512398b994/export" \ -H "Authorization: DeepL-Auth-Key " ``` ```json theme={null} { "job_id": "7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13", "parameters": { "translation_memory_id": "a74d88fb-ed2a-4943-a664-a4512398b994" } } ``` 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 theme={null} curl -X GET "https://api.deepl.com/v3/translation_memories/jobs/7c2e5a91-3b8d-4f16-8e0a-6d4c2b7f9a13" \ -H "Authorization: DeepL-Auth-Key " ``` ```json theme={null} { "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" } ] } ``` ## 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. 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. `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. # Import a translation memory Source: https://developers.deepl.com/api-reference/translation-memory/import-a-translation-memory post /v3/translation_memories/import 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 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. ```bash theme={null} curl -X POST "https://api.deepl.com/v3/translation_memories/import" \ -H "Authorization: DeepL-Auth-Key " \ -H "Content-Type: application/json" \ -d '{ "source_file": { "file_name": "legal.tmx", "content_type": "application/xml", "content_length": 1024 }, "parameters": { "display_name": "Legal" } }' ``` ```json theme={null} { "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" } ``` `PUT` the file to `upload_url` before `expires_at`. The URL is already signed, so this request carries no DeepL credentials. ```bash theme={null} 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. Check [the job](/api-reference/translation-memory/retrieve-a-translation-memory-job) until `status` is `completed` or `failed`. ```bash theme={null} curl -X GET "https://api.deepl.com/v3/translation_memories/jobs/0f8b6c1e-4d2a-4c77-9a3e-1b5d8c9e2f40" \ -H "Authorization: DeepL-Auth-Key " ``` On completion, the job result carries the `translation_memory_id` of the new translation memory. ```json theme={null} { "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 } ] } ``` ## 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. # List translation memories Source: https://developers.deepl.com/api-reference/translation-memory/list-translation-memories get /v3/translation_memories Retrieve translation memories associated with your account, used to store and reuse previously created translations. See [How to use translation memories](/docs/customize/using-translation-memories) to use them in translation requests. # List translation memory segments Source: https://developers.deepl.com/api-reference/translation-memory/list-translation-memory-segments get /v3/translation_memories/{translation_memory_id}/segments 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 theme={null} # 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 " # 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 " ``` 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. `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. ## 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`. # Retrieve a translation memory Source: https://developers.deepl.com/api-reference/translation-memory/retrieve-a-translation-memory get /v3/translation_memories/{translation_memory_id} 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. # Retrieve an import or export job Source: https://developers.deepl.com/api-reference/translation-memory/retrieve-a-translation-memory-job get /v3/translation_memories/jobs/{job_id} 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`. 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. # Check usage and limits Source: https://developers.deepl.com/api-reference/usage-and-quota/check-usage-and-limits get /v2/usage Retrieve near-real-time character usage and account limits for the current billing period. Returns usage within the current billing period together with the corresponding limits. Data is near-real-time: typically up to date within a few minutes of the usage being generated. Response behavior the schema alone doesn't convey: * For Pro API accounts, the response breaks usage down by product (`translate`, `write`) and reports both account totals and counts for the API key used to make the request. Responses for Free API and Pro Classic accounts contain only `character_count` and `character_limit`. * `character_count` sums text translation, document translation, and text improvement characters. Characters are counted from the source text length in Unicode code points, so "A", "Δ", "あ", and "深" each count as one character. * `character_limit` reflects a [Cost Control](/docs/best-practices/cost-control) limit if one is set; `api_key_character_limit` reflects a [key-level usage limit](/docs/admin/managing-api-keys#set-a-key-level-usage-limit) if one is set. Both fields return `1000000000000` when no limit is configured. For usage data beyond the current billing period, or broken down by key or custom tag, see [Retrieving Usage Data](/docs/admin/retrieving-usage-data). For general API limitations, see [Usage limits](/docs/resources/usage-limits). # Reconnect Session Source: https://developers.deepl.com/api-reference/voice/reconnect-session get /v3/voice/realtime # Request Session Source: https://developers.deepl.com/api-reference/voice/request-session post /v3/voice/realtime # WebSocket Streaming Source: https://developers.deepl.com/api-reference/voice/websocket-streaming WebSocket channel for streaming audio and receiving transcriptions and translations. Messages are exchanged in JSON or MessagePack format. WebSocket messages are exchanged in TEXT frames when using JSON format and in BINARY frames when using MessagePack format. Sending the wrong frame type will result in connection errors. # Understanding API Key Permissions Source: https://developers.deepl.com/docs/admin/api-key-permissions Why scoped API keys exist, how DeepL enforces permission scopes, and when to choose a scoped key over an unrestricted one. API key permissions let you limit what a developer API key can do. Instead of one key with full access to every endpoint, you can issue keys that are scoped to specific operations, for example a key that can only translate text or a key that can only read glossaries. Permissions are available on the API Pro, API Developer, API Growth, and API Enterprise plans, and currently apply only to developer keys; [admin keys](/docs/admin/managing-api-keys#manage-admin-api-keys) can't be scoped. This page explains how the permissions model works. For the UI steps, see [Set key permissions](/docs/admin/managing-api-keys#set-key-permissions); for the full list of scopes, see [Permission Scopes](/docs/admin/permission-scopes). ## Permissions and scopes Permissions are implemented as scopes. Each scope groups a set of related operations into a single capability you can grant to a key: `translate:text` covers text translation, `glossaries:read` covers reading glossaries, and so on. "Permissions" is the user-facing feature; "scopes" is the technical mechanism behind it. ## Why scoped keys Before permissions were introduced in June 2026, every DeepL API key had full access to every endpoint. That's convenient, but it means a key embedded in a translation widget could also delete your glossaries, and a leaked key exposes your entire API surface. Scoped keys apply the principle of least privilege: each key gets exactly the access its workload needs. A scoped key that leaks, or a service with a bug, can only do what its scopes allow. | **Key type** | **Choose when** | | ------------ | ----------------------------------------------------------------------------------------------------------------- | | Unrestricted | The key needs access to any endpoint and doesn't need to be limited | | Scoped | The key should reach only specific endpoints, for example to prevent glossaries from being modified inadvertently | ## How enforcement works A developer key becomes scoped the moment you assign it one or more scopes. Once scoped: * The key can call only the endpoints fully covered by its scopes. * Every other endpoint returns `403 Forbidden`, including endpoints that have no scope requirement of their own (currently the case for Voice API endpoints). * Some endpoints require more than one scope. The key must hold all of them; if any is missing, the request is denied. When a scoped key calls an endpoint outside its scopes, the `detail` field of the response lists what's missing: ```json theme={null} { "message": "Forbidden", "detail": "Missing required scope(s): glossaries:write" } ``` Enforcement applies only to scoped keys. Unrestricted keys retain full access to every endpoint, and existing keys remain unrestricted until you assign them scopes. An account can hold any mix of scoped and unrestricted keys. # Managing API Keys in the Account UI Source: https://developers.deepl.com/docs/admin/managing-api-keys Create, rename, deactivate, and set usage limits and permissions on DeepL API keys in the account UI. Manage your API keys in the ["API Keys & Limits" tab](https://www.deepl.com/your-account/keys) when signed into your DeepL API account. A single subscription can have multiple simultaneously active keys: up to 25 on Pro API plans and up to 2 on Free API plans. To create, deactivate, and limit developer keys programmatically instead, use the [Admin API](/docs/admin/overview#the-admin-api). ## Create a key Click "Create key". You can optionally name the key during creation; if you don't, it's named "DeepL API Key" automatically. Naming keys lets you find them later using the search bar on the "API keys" tab. After you confirm, a popup shows the new key. Copy it from this popup to use it immediately, or copy it from the key table at any time. To create a key that can only access specific endpoints, select **Custom permissions** during creation. See [Set key permissions](#set-key-permissions). ## Copy a key Click the "Copy" icon next to the key. For security reasons, the full key is never shown in the key table. Both active and deactivated keys can be copied. ## Rename a key Select "Rename key" from the key's options menu. Both active and deactivated keys can be renamed, and two keys can share the same name. ## Deactivate a key A key stops working immediately when deactivated, and deactivation is permanent: a deactivated key cannot be reactivated. Select "Deactivate key" from the key's options menu, then confirm. ## Set a key-level usage limit Key-level limits cap the total characters (across text translation, document translation, and text improvement) a key can consume in a one-month usage period. For example, a key with a 1,000,000 character limit stops consuming at 1,000,000 characters and starts fresh when the next usage period begins. You can see your current usage period dates in the [API Usage tab](https://www.deepl.com/your-account/usage). To set a limit, select "Set limit" from the key's options menu, activate the limit, and enter a character amount. Setting the limit to 0 prevents the key from consuming any characters. As with subscription-level [cost control](/docs/best-practices/cost-control): * You receive notification emails when a key reaches 80% and 100% of its limit * The API responds with `456 Quota exceeded` errors once 100% of the limit is reached To monitor consumption against a limit, check the "Characters consumed" column in the key table or see [Retrieving Usage Data](/docs/admin/retrieving-usage-data). ## Set key permissions Permissions restrict a developer key to specific endpoints. This section covers the UI steps; to understand how permissions are enforced, see [Understanding API Key Permissions](/docs/admin/api-key-permissions), and for what each scope covers, see [Permission Scopes](/docs/admin/permission-scopes). To create a scoped key, click "Create key", select **Custom permissions**, choose one or more scopes from the list, and confirm. To change permissions on an existing key, select "Edit permissions" from the key's options menu. Choose **All access** to make the key unrestricted, or **Custom permissions** to select specific scopes, then save. The "Permissions" column in the key table shows each key's status as a badge. Hover over a badge to see the assigned scopes. ## Manage admin API keys On the API Pro, API Growth, and API Enterprise plans, you manage [admin keys](/docs/admin/overview#the-admin-api) separately in the ["Admin Keys" tab](https://www.deepl.com/your-account/admin). Admin keys support the same actions as developer keys: create, copy, rename, and deactivate them from the "Admin Keys" tab exactly as described above. The differences: * Admin keys always end with an `:adm` suffix, which distinguishes them from developer keys * Unnamed admin keys are called "DeepL Admin Key" by default * You can create up to 25 simultaneously active admin keys * Usage limits and permissions don't apply to admin keys # Admin Source: https://developers.deepl.com/docs/admin/overview Manage API keys, control what each key can access, and monitor usage across your organization, from the account UI or programmatically with the Admin API. You can administer your DeepL API subscription in two ways: * **Account UI**: the [API Keys & Limits](https://www.deepl.com/your-account/keys) and [API Usage](https://www.deepl.com/your-account/usage) tabs of your DeepL account, available on all plans * **Admin API**: programmatic key management and usage analytics, for automating key provisioning, enforcing limits, and feeding usage data into your own dashboards or billing systems (see the [Admin API reference](/api-reference/admin-api/managing-developer-keys/create-key)) ## The Admin API The Admin API is available to all API Pro, API Growth, and API Enterprise subscribers. The Admin API uses its own key type: admin keys, which always carry an `:adm` suffix to distinguish them from developer keys. You create admin keys in the ["Admin Keys" tab](https://www.deepl.com/your-account/admin) of your account (see [Managing API Keys in the Account UI](/docs/admin/managing-api-keys#manage-admin-api-keys)) and pass them in the `Authorization` header of each request, just like developer keys: `Authorization: DeepL-Auth-Key [yourAdminKey]` Admin API endpoints are available on the Pro endpoint `https://api.deepl.com`. See the [Admin API reference](/api-reference/admin-api/managing-developer-keys/create-key) for request and response schemas. ## Start here Create a developer key, cap its usage, and pull a usage report in four API calls. Create, rename, deactivate, and set usage limits on developer and admin keys in the account UI. Understand how permission scopes restrict what a developer key can do. Pick the right usage data source and pull consumption data for your account, keys, or custom tags. Endpoint reference for programmatic key management and usage analytics. # Permission Scopes Source: https://developers.deepl.com/docs/admin/permission-scopes Every permission scope for DeepL developer API keys and the endpoints each scope covers. Each scope grants a [scoped API key](/docs/admin/api-key-permissions) access to the endpoints listed under it. A scoped key can call only endpoints fully covered by its scopes; requests to any other endpoint return `403 Forbidden`. | **Method** | **Endpoint** | | ---------- | --------------------------------------------------------------- | | `POST` | [`/v2/translate`](/api-reference/translate/request-translation) | | **Method** | **Endpoint** | | ---------- | ------------------------------------------------------------------------------------------- | | `POST` | [`/v2/document`](/api-reference/document/upload-and-translate-a-document) | | `GET` | [`/v2/document/{document_id}`](/api-reference/document/check-document-status) | | `GET` | [`/v2/document/{document_id}/result`](/api-reference/document/download-translated-document) | | **Method** | **Endpoint** | | ---------- | ---------------------------------------------------------------------------- | | `POST` | [`/v2/write/rephrase`](/api-reference/improve-text/request-text-improvement) | | `POST` | [`/v2/write/correct`](/api-reference/improve-text/correct-text) | | **Method** | **Endpoint** | | ---------- | ------------------------------------------------------------------------------------------------------------------- | | `GET` | [`/v3/glossaries`](/api-reference/multilingual-glossaries/list-all-glossaries) | | `GET` | [`/v3/glossaries/{glossary_id}`](/api-reference/multilingual-glossaries/retrieve-glossary-details) | | `GET` | [`/v3/glossaries/{glossary_id}/entries`](/api-reference/multilingual-glossaries/retrieve-glossary-entries) | | `GET` | [`/v2/glossaries`](/api-reference/glossaries/list-all-glossaries) | | `GET` | [`/v2/glossaries/{glossary_id}`](/api-reference/glossaries/retrieve-glossary-details) | | `GET` | [`/v2/glossaries/{glossary_id}/entries`](/api-reference/glossaries/retrieve-glossary-entries) | | `GET` | [`/v2/glossary-language-pairs`](/api-reference/multilingual-glossaries/list-language-pairs-supported-by-glossaries) | | **Method** | **Endpoint** | | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `POST` | [`/v3/glossaries`](/api-reference/multilingual-glossaries/create-a-glossary) | | `PATCH` | [`/v3/glossaries/{glossary_id}`](/api-reference/multilingual-glossaries/edit-glossary-details) | | `PUT` | [`/v3/glossaries/{glossary_id}/dictionaries`](/api-reference/multilingual-glossaries/replaces-or-creates-a-dictionary-in-the-glossary-with-the-specified-entries) | | `DELETE` | [`/v3/glossaries/{glossary_id}`](/api-reference/multilingual-glossaries/delete-a-glossary) | | `DELETE` | [`/v3/glossaries/{glossary_id}/dictionaries`](/api-reference/multilingual-glossaries/deletes-the-dictionary-associated-with-the-given-language-pair-with-the-given-glossary-id) | | `POST` | [`/v2/glossaries`](/api-reference/glossaries/create-a-glossary) | | `DELETE` | [`/v2/glossaries/{glossary_id}`](/api-reference/glossaries/delete-a-glossary) | | **Method** | **Endpoint** | | ---------- | ---------------------------------------------------------------------------------------------------------------------- | | `GET` | [`/v3/style_rules`](/api-reference/style-rules/list-all-style-rules) | | `GET` | [`/v3/style_rules/{style_id}`](/api-reference/style-rules/get-style-rule) | | `GET` | [`/v3/style_rules/{style_id}/custom_instructions/{instruction_id}`](/api-reference/style-rules/get-custom-instruction) | | **Method** | **Endpoint** | | ---------- | ------------------------------------------------------------------------------------------------------------------------- | | `POST` | [`/v3/style_rules`](/api-reference/style-rules/create-style-rule) | | `PATCH` | [`/v3/style_rules/{style_id}`](/api-reference/style-rules/update-style-rule) | | `PUT` | [`/v3/style_rules/{style_id}/configured_rules`](/api-reference/style-rules/update-configured-rules) | | `DELETE` | [`/v3/style_rules/{style_id}`](/api-reference/style-rules/delete-style-rule) | | `POST` | [`/v3/style_rules/{style_id}/custom_instructions`](/api-reference/style-rules/create-custom-instruction) | | `PUT` | [`/v3/style_rules/{style_id}/custom_instructions/{instruction_id}`](/api-reference/style-rules/update-custom-instruction) | | `DELETE` | [`/v3/style_rules/{style_id}/custom_instructions/{instruction_id}`](/api-reference/style-rules/delete-custom-instruction) | | **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`. | **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) | | **Method** | **Endpoint** | | ---------- | -------------------------------------------------------------------------- | | `GET` | [`/v3/languages`](/api-reference/languages/retrieve-languages-by-resource) | | `GET` | [`/v3/languages/resources`](/api-reference/languages/retrieve-resources) | | `GET` | [`/v2/languages`](/api-reference/languages/retrieve-supported-languages) | | **Method** | **Endpoint** | | ---------- | -------------------------------------------------------------------- | | `GET` | [`/v2/usage`](/api-reference/usage-and-quota/check-usage-and-limits) | No scope covers the Voice API yet, so Voice endpoints are accessible only with unrestricted keys. Voice scopes will be added in a future update. # Admin API Quickstart Source: https://developers.deepl.com/docs/admin/quickstart Create a developer API key, set a usage limit on it, and pull a per-key usage report in four calls to the Admin API. In this tutorial, we'll use the Admin API to provision a new developer API key for a team, set a character limit to control costs, and then retrieve a usage report to confirm activity. By the end, you'll have made four API calls that cover the core Admin API workflow. ## What you'll learn * How to create a developer API key programmatically * How to apply a character usage limit to a key * How to retrieve an organization-wide usage report broken down by key * What the response objects look like at each step ## Prerequisites * An API Pro, API Growth, or API Enterprise subscription (see [The Admin API](/docs/admin/overview#the-admin-api)) * An [**admin API key**](/docs/admin/managing-api-keys#manage-admin-api-keys) for your DeepL organization. Regular developer keys cannot access Admin API endpoints. * `curl` installed on your machine Responses in this tutorial are shown formatted. To pretty-print JSON in your terminal, install [jq](https://jqlang.org/) and pipe curl output through it: `curl ... | jq` ## Building with an AI coding agent? Wire it up to the [DeepL Docs MCP Server](/docs/getting-started/docs-mcp-server) so it can search and read this documentation while it writes code. In Claude Code: ```bash theme={null} claude mcp add --transport http deepl-docs https://developers.deepl.com/mcp ``` Then describe what you want to build. To get the same result as this tutorial, paste: ```text wrap theme={null} Using the DeepL Admin API, write a script that creates a developer API key labeled "Staging Team Key", sets a 500,000 character usage limit on it, and then displays a per-key usage report for the last month. ``` ## Step 1: List existing developer keys Before creating a new key, let's see what's already in the organization. This gives us a baseline and confirms that authentication is working. ```bash theme={null} curl https://api.deepl.com/v2/admin/developer-keys \ -H "Authorization: DeepL-Auth-Key YOUR_ADMIN_KEY" ``` You'll receive an array with one object per developer key, active and deactivated: ```json theme={null} [ { "key_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890:1f2e3d4c-5b6a-7980-dcba-0987654321fe", "label": "Production API Key", "creation_time": "2026-05-12T09:41:03.512Z", "deactivated_time": null, "is_deactivated": false, "usage_limits": { "characters": null, "speech_to_text_milliseconds": null } } ] ``` Notice the `key_id` field, composed of two GUIDs separated by a `:` symbol. You'll use it to target specific keys in later steps. `null` usage limits mean no cap is set on that key. If you get a `403 Forbidden` response here, double-check that you're using an admin key with an `:adm` suffix, not a developer key. ## Step 2: Create a new developer key Now let's create a key for a new team or service. We'll give it a descriptive label so it's easy to identify later. ```bash theme={null} curl https://api.deepl.com/v2/admin/developer-keys \ -H "Authorization: DeepL-Auth-Key YOUR_ADMIN_KEY" \ -H "Content-Type: application/json" \ -d '{"label": "Staging Team Key"}' ``` The response returns the newly created key object: ```json theme={null} { "key_id": "f9e8d7c6-b5a4-3210-fedc-ba9876543210:0a1b2c3d-4e5f-6789-abcd-ef0123456789", "label": "Staging Team Key", "creation_time": "2026-07-09T10:02:47.118Z", "deactivated_time": null, "is_deactivated": false, "usage_limits": { "characters": null, "speech_to_text_milliseconds": null } } ``` Copy the `key_id` from this response. You'll need it in the next step. ## Step 3: Set a character usage limit By default, a new key has no usage cap. Let's set a character limit so the staging key can't consume more quota than intended. Replace `KEY_ID` with the `key_id` from the previous step. ```bash theme={null} curl https://api.deepl.com/v2/admin/developer-keys/limits \ -X PUT \ -H "Authorization: DeepL-Auth-Key YOUR_ADMIN_KEY" \ -H "Content-Type: application/json" \ -d '{ "key_id": "KEY_ID", "characters": 500000 }' ``` The response returns the updated key object. Confirm that `usage_limits.characters` now shows `500000`: ```json theme={null} { "key_id": "f9e8d7c6-b5a4-3210-fedc-ba9876543210:0a1b2c3d-4e5f-6789-abcd-ef0123456789", "label": "Staging Team Key", "creation_time": "2026-07-09T10:02:47.118Z", "deactivated_time": null, "is_deactivated": false, "usage_limits": { "characters": 500000, "speech_to_text_milliseconds": null } } ``` Once the key reaches its character limit, requests using it return `456 Quota exceeded` errors until the next usage period starts or you raise the limit. ## Step 4: Pull a usage report by key After the team has started using the key, let's retrieve a usage report to see how much quota each key has consumed. We'll group the results by API key so we can see per-key totals. Replace the dates with a range that covers the period you want to inspect. Date ranges can span up to 366 days, and data is available up to and including the previous UTC calendar day. ```bash theme={null} curl "https://api.deepl.com/v2/admin/analytics?start_date=2026-06-01&end_date=2026-07-01&group_by=key" \ -H "Authorization: DeepL-Auth-Key YOUR_ADMIN_KEY" ``` The response includes a `usage_report` object with per-key breakdowns and an organization-wide total: ```json theme={null} { "usage_report": { "start_date": "2026-06-01T00:00:00", "end_date": "2026-07-01T00:00:00", "group_by": "key", "key_usages": [ { "api_key": "f9e8****6789", "api_key_label": "Staging Team Key", "usage": { "text_translation_characters": 4892, "text_improvement_characters": 4727, "document_translation_characters": 0, "speech_to_text_minutes": 107.46, "total_characters": 9619 } } ], "total_usage": { "text_translation_characters": 4892, "text_improvement_characters": 4727, "document_translation_characters": 0, "speech_to_text_minutes": 107.46, "total_characters": 9619 } } } ``` Notice that `api_key` values are partially masked in the response for security. You can match keys to their labels using the `api_key_label` field. You have now created a key, capped its usage, and confirmed activity through the analytics endpoint. ## What's next * **Rename or reorganize keys**: Use the [rename endpoint](/api-reference/admin-api/managing-developer-keys/rename-key) to update a key's label as teams or services change. * **Deactivate a key**: When a key is no longer needed, [deactivate it](/api-reference/admin-api/managing-developer-keys/deactivate-key). Deactivation is permanent, but deactivated keys remain visible in your key list. * **Daily usage breakdowns**: Change `group_by` to `key_and_day` to see per-key usage broken down by day, which is useful for spotting spikes. * **Custom tag analytics**: If you annotate API requests with custom tags, use the [custom tag analytics endpoint](/api-reference/admin-api/get-custom-tag-usage-analytics) to break down usage by tag. * **Other data sources**: See [Retrieving Usage Data](/docs/admin/retrieving-usage-data) for a comparison of all the ways to monitor your DeepL API usage. # Retrieving Usage Data Source: https://developers.deepl.com/docs/admin/retrieving-usage-data Compare DeepL's usage data sources and pull character and minute consumption for your account, API keys, or custom tags. DeepL offers several ways to retrieve usage data so you can track character and minute consumption and the associated costs. This guide compares the available data sources and shows how to use each one. Consumption data in the account UI, in CSV exports, and in the [Admin API analytics endpoints](/api-reference/admin-api/get-usage-analytics) is available up to and including the previous UTC calendar day. For near-real-time data, use the [`/usage` endpoint](/api-reference/usage-and-quota/check-usage-and-limits). ## Choose a data source | Data source | Scope | Time range | Data freshness | Interface | | ------------------------------------------------- | ------------------------------------------------- | ---------------------------------------------------------------- | ------------------------------- | ------------ | | [API Keys & Limits tab](#api-keys-and-limits-tab) | Per API key | Current usage period only | Previous UTC day | Account UI | | [API Usage tab](#api-usage-tab) | Account total (plus year-to-date on yearly plans) | Current usage period (and yearly period) | Previous UTC day | Account UI | | [CSV export](#csv-export) | Per API key or per custom tag | Custom or preset ranges up to 4 months, optional grouping by day | Previous UTC day | CSV download | | [`/usage` endpoint](#the-usage-endpoint) | Account total | Current usage period only | Near-real-time (within minutes) | REST API | | [Admin API analytics](#admin-api-analytics) | Per API key or per custom tag | Custom date range, optional grouping by day | Previous UTC day | REST API | Use the **account UI** for a quick check while signed in, the **CSV export** for detailed or offline analysis per key or per custom tag, the **`/usage` endpoint** for programmatic near-real-time quota checks, and the **Admin API analytics endpoints** for programmatic reporting and cost attribution. ## API Keys and Limits tab To check per-key consumption for the current monthly usage period, open the [API Keys & Limits tab](https://www.deepl.com/your-account/keys). It shows character and speech-to-text (STT) minute consumption for each API key. ## API Usage tab To check your account totals, open the [API Usage tab](https://www.deepl.com/your-account/usage). It shows total character and STT minute usage for the current monthly usage period, and if you're on a yearly plan, usage to date for your yearly billing period, too. ## CSV export Generate a CSV report of your usage for a chosen time period. Two report types are available: * **API key-level report**: characters translated per API key, including a column that breaks out text improvement (Write API) characters separately. Key-level usage is not broken out on invoices; retrieve it via CSV export, the "API Keys & Limits" tab, or the [Admin API analytics endpoints](#admin-api-analytics). * **Custom tag-level report**: usage broken down by [custom reporting tag](/docs/learning-how-tos/examples-and-guides/how-to-use-custom-reporting-tags). Only tagged requests appear in this report, so results reflect a subset of total usage unless every request is tagged. To download a report, click "Download CSV usage report" on the [API Keys & Limits tab](https://www.deepl.com/your-account/keys) or the [API Usage tab](https://www.deepl.com/your-account/usage) and select "API key-level report" or "Custom tag-level report" from the dropdown. Then select a time period and click "Download your report". Select a custom date range of up to 4 months (on UTC calendar days) or one of the presets, which cover rolling windows from the last 24 hours up to the last year as well as calendar months and usage periods. Rolling presets include the current day; calendar-month and usage-period ranges align to UTC day or period boundaries, so they might not start and end on a full calendar day. For any time range, you can also group usage data by UTC calendar day: Document translation data is only included in reports with a time period starting on or after May 16, 2024 00:00 UTC. Text translation data is included for all time periods. ## The usage endpoint The [`/usage` endpoint](/api-reference/usage-and-quota/check-usage-and-limits) returns a near-real-time snapshot of character and minute usage in the current monthly usage period, typically up to date within a few minutes of the usage being generated. Use it for programmatic quota checks, for example before submitting a large translation batch. ## Admin API analytics The [Admin API](/docs/admin/overview#the-admin-api) provides programmatic access to usage data over a custom date range, with optional grouping by day. It requires an [admin key](/docs/admin/managing-api-keys#manage-admin-api-keys) for authentication. Two endpoints are available: * [Get usage analytics](/api-reference/admin-api/get-usage-analytics) returns usage statistics across all services (text translation, document translation, text improvement, and speech-to-text), in total or grouped by API key * [Get custom tag usage analytics](/api-reference/admin-api/get-custom-tag-usage-analytics) returns usage broken down by custom tag Custom tags are an optional dimension for cost attribution: attach a tag to individual API requests with the `X-DeepL-Reporting-Tag` header to track consumption by team, project, or any other category. Only tagged requests appear in custom-tag analytics, so results reflect a subset of total usage unless every request is tagged. See [How to use custom reporting tags](/docs/learning-how-tos/examples-and-guides/how-to-use-custom-reporting-tags) for setup and naming guidance. If you prefer a download over an API call, the same per-tag data is available as a [custom tag-level CSV export](#csv-export). For a worked example of pulling analytics data into a dashboard, see the [usage analytics dashboard cookbook](/docs/learning-how-tos/cookbook/usage-analytics-dashboard). # CORS requests Source: https://developers.deepl.com/docs/best-practices/cors-requests If you try to send requests to the DeepL API from the browser, your requests will fail with the [HTTP 403 Forbidden status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403) with a message stating “blocked by CORS policy”. The DeepL API does not allow calls directly from browser-based applications. Requests to third-party APIs from front-end applications would expose your credentials on the web, leaving your account vulnerable to fraud and abuse. You should never reveal your API authentication key in publicly accessible code. If you realize your API authentication key has been compromised, log in to your DeepL account immediately. Under "Account Details", you have the option to deactivate any of your active API keys. Deactivate any compromised key to prevent others from using it with the DeepL API. You can also generate new keys from the same page. To safely use the DeepL API on your website or application, you can route your requests through your own backend servers. This keeps your credentials hidden and allows you to specify CORS policies and rate limits as required by your use case. DeepL's official open-source [client libraries](/docs/getting-started/client-libraries) can help you create these backend implementations. For prototyping and frontend testing, you can use the [DeepL API Node.js Proxy](https://github.com/DeepL/deepl-api-nodejs-proxy/), a lightweight ready-to-use proxy server that handles CORS and keeps your API key secure. # Subscription-level cost control Source: https://developers.deepl.com/docs/best-practices/cost-control As a DeepL API Pro user, you have the option to set a limit on the number of characters that can be translated in your entire subscription per month, thereby limiting your maximum costs. You only need to activate *Cost Control* in your DeepL Pro Account on the "API Keys & Limits" tab. To do so: 1. Navigate to the ["API Keys & Limits" tab](https://www.deepl.com/your-account/keys) 2. Click *Manage cost control* 3. Tick the checkbox *Activate cost control* to enable 4. Enter the maximum cost amount 5. Click "Submit" to save the changes Your new cost control limit is applied immediately. Once you have reached your limit, DeepL will not process any further translation requests until the end of the usage period, in order to not exceed your set maximum cost. If you would like to translate more, you can make changes to the cost control limit at any time. The limit is enforced against the sum of Translate API and Write API characters; it's not possible to set separate limits for each. #### *I have been notified that my Cost Control was raised, but I didn't raise it. What should I do?* Every time your *Cost Control* limit is raised, you will receive a notification email for security reasons. If you receive such a notification, but you did not raise your *Cost Control* limit, we highly suggest you reset your authentication key and the password to your DeepL Pro account as soon as possible. # Document translations Source: https://developers.deepl.com/docs/best-practices/document-translations Below you will find general guidance on how to handle status codes and error details to ensure a smooth document translation experience. ### Document File Size Limits We impose [size limits](/docs/resources/usage-limits) per file type. Limits vary by format and API plan. For DOCX and PPTX, our .NET, PHP and NodeJS client libraries offer functionality to minify files by temporarily extracting large media formats before sending them to the DeepL API. Stripped media are reinserted after document translation is completed. This allows users to translate files that might hit the size limit. ### Billing minimums Every submitted document of type `.pptx`, `.docx`, `.doc`, `.xlsx`, or `.pdf` is billed a minimum of 50,000 characters on DeepL API plans, no matter how many characters the document contains. ### One source/target language pair per upload The `source_lang` and `target_lang` values on the request apply to the entire uploaded file. For most formats, keep each upload to a single source language for consistent results — behavior on content that isn't in the selected source language is not guaranteed. **XLIFF** is the exception: `` elements are translated as independent segments, so XLIFF files containing segments in different languages are handled segment-by-segment. Note: the per-`` `source-language` attribute inside an XLIFF is ignored — DeepL uses the request's `source_lang` value for every segment. All content in the uploaded file counts toward billed characters, including content that was not actually translated. ### Same-language source and target are rejected Requests where the source and target languages are equal — including regional variants of the same language, e.g. `EN` → `EN-US` or `EN-US` → `EN-GB` — are rejected with HTTP 400 (`Source and target language are equal.`). To adapt regional spelling, post-process the translated output yourself. ### Format-specific gotchas Each supported format has behaviors and constraints worth knowing before you upload. The most common surprises: **XML** * Only text between element tags is translated. Attribute values and processing instructions are left alone. * XML `` may be picked up as translatable text and their delimiters escaped in the output — strip comments before uploading if downstream tooling relies on them. * `CDATA` section content **is** translated — the markers are stripped and the inner text is sent to the engine. Move code, regex, and other non-translatable content out of `CDATA` blocks first. * `translate="no"` on any XML element excludes its content from translation. * Files using ITS 2.0 external rules (`its:rules/@xlink:href`) currently return HTTP 500 — inline the rules or remove the reference. * Malformed XML currently returns HTTP 500 instead of a 4xx. Validate with a standard XML parser before uploading. **XLIFF** * Only `` content is translated; results are written to the corresponding ``. **Existing `` values are overwritten** — remove or split out already-translated units if you need to preserve them. * The per-`` `source-language` / `target-language` attributes are ignored — DeepL uses the API's `source_lang` and `target_lang` for the entire upload. * `translate="no"` on `` (1.2) or `` (2.0) is fully honored. * The `state` attribute is preserved as-is; if you use `state="needs-translation"`, update it yourself after translation. * An unsupported `trgLang` value on the root `` element (e.g. `arb-MOD`) is rejected as "Invalid target language" even if the API `target_lang` is valid — remove or fix the attribute. **DITA** * Only DITA topic files (`.dita`) are supported. `.ditamap` uploads are rejected. * `translate="no"` is fully honored on inline and block-level elements. * Content references (`conref`, `conkeyref`) are not resolved — translate each referenced source topic independently. * Newlines inside ``, `
`, ``, and `` may be collapsed to single spaces. Move code samples out of the file before translation if line breaks matter.
* Topics with dense inline-element markup (combinations of ``, ``, `