# DeepL Documentation > Developer documentation for the DeepL API: translate text and documents, transcribe and translate speech in real time, improve text, and customize output with glossaries, style rules, and translation memories. > ## Agent Instructions > Use the DeepL API when a task needs machine translation or text improvement, including translating text strings, whole documents with formatting preservation, or transcribing and translating live speech. Preferred terminology and phrasing may be enforced using customizations (glossaries, style rules, and translation memories). Retrieve supported languages for each product from the `/v3/languages` endpoints. > Read the machine-readable API surface instead of inferring request shapes from prose: the REST spec is at https://developers.deepl.com/api-reference/openapi.yaml (also served as openapi.json) and the Voice WebSocket protocol is at https://developers.deepl.com/api-reference/voice/voice.asyncapi.yaml. These docs also expose an MCP server at https://developers.deepl.com/mcp (Streamable HTTP, no authentication). > Use https://api.deepl.com for Pro plans and https://api-free.deepl.com for the Free plan. Authenticate every request with the header `Authorization: DeepL-Auth-Key `. Never fabricate an API key: ask the user for one, or point them at https://developers.deepl.com/docs/getting-started/quickstart. > Errors use standard HTTP status codes with a JSON body containing a `message` field, plus a `code` field where available, and an `X-Trace-ID` response header that identifies the request in DeepL's logs. Log `X-Trace-ID` by default. Retry 429 and 5xx with exponential backoff. Do not retry 456, which means the account quota is exhausted, or 400, which means the request itself is invalid. # 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 DeepL API usage broken down by custom tag for a date range, optionally aggregated by day, with paginated results. ## 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. > [!WARNING] > > 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 organization-wide DeepL API usage for a date range, optionally grouped 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 Create a new DeepL API key for your organization as an admin, using the Admin API. # Deactivate a developer key Source: https://developers.deepl.com/api-reference/admin-api/managing-developer-keys/deactivate-key put /v2/admin/developer-keys/deactivate Deactivate a DeepL API key as an admin so it can no longer be used to authenticate requests. # Get developer keys Source: https://developers.deepl.com/api-reference/admin-api/managing-developer-keys/get-keys get /v2/admin/developer-keys List every DeepL API key in your organization as an admin, including each key's label and usage limits. # Rename a developer key Source: https://developers.deepl.com/api-reference/admin-api/managing-developer-keys/rename-key put /v2/admin/developer-keys/label Change the label on an existing DeepL API key as an admin, using the Admin API. # 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 Set or update the usage limit on an individual DeepL API key as an admin. # 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 > [!WARNING] > > **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 **JSON Request** ```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?" ] }' ``` **Form-Encoded Request** ```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 Request** ```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 **text** `string[]` *required* 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 **200 Success** ```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" } ] } ``` **400 Bad Request** ```json theme={null} { "message": "Missing parameter text." } ``` This error occurs when: * The inputs in the request are malformed **403 Forbidden** ```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 **415 Unsupported Media Type** ```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 **500 Internal Server Error** ```json theme={null} { "message": "Internal server error." } ``` This error occurs when there are issues with the internal language detection service. ### Response Fields **results** `array` *required* Array of detection results, one for each input text. Each result object contains a `detected_language` and a `detection_status` field. **detected\_language** `string` 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. **detection\_status** `string` *required* 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} Check whether an uploaded document has finished translating, along with its billed character count and any error. # Download translated document Source: https://developers.deepl.com/api-reference/document/download-translated-document post /v2/document/{document_id}/result Download the translated file once a document translation job has finished. Results are available for a limited time. # Upload and translate a document Source: https://developers.deepl.com/api-reference/document/upload-and-translate-a-document post /v2/document Upload a document for translation and receive the document ID and key needed to check status and download the result. # Create a glossary Source: https://developers.deepl.com/api-reference/glossaries/create-a-glossary post /v2/glossaries Create a v2 glossary from a list of source and target term pairs for a single language pair. # Delete a glossary Source: https://developers.deepl.com/api-reference/glossaries/delete-a-glossary delete /v2/glossaries/{glossary_id} Delete a v2 glossary and all of its entries. Deleted glossaries cannot be recovered. # List all glossaries Source: https://developers.deepl.com/api-reference/glossaries/list-all-glossaries get /v2/glossaries List every v2 glossary on your account with its metadata, without returning the glossary entries. # Retrieve glossary details Source: https://developers.deepl.com/api-reference/glossaries/retrieve-glossary-details get /v2/glossaries/{glossary_id} Retrieve the metadata for a single v2 glossary, including its language pair and entry count, without the entries. # Retrieve glossary entries Source: https://developers.deepl.com/api-reference/glossaries/retrieve-glossary-entries get /v2/glossaries/{glossary_id}/entries Retrieve the term pairs stored in a single v2 glossary, in the format requested through 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 Retrieve the languages a DeepL API resource supports, including source and target roles and per-resource features. # 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 Reference for the deprecated v2/languages endpoint. New integrations should use the v3/languages endpoints instead. > [!WARNING] > > **`/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 Create a v3 multilingual glossary holding one or more dictionaries, each covering a source and target language pair. # Delete a glossary Source: https://developers.deepl.com/api-reference/multilingual-glossaries/delete-a-glossary delete /v3/glossaries/{glossary_id} Delete a v3 multilingual glossary along with all of its dictionaries and entries. This cannot be undone. # 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 Delete a single dictionary from a v3 multilingual glossary by its source and target language pair. # Edit glossary details Source: https://developers.deepl.com/api-reference/multilingual-glossaries/edit-glossary-details patch /v3/glossaries/{glossary_id} Rename a v3 multilingual glossary, or edit the dictionary for one source and target language pair. # List all glossaries Source: https://developers.deepl.com/api-reference/multilingual-glossaries/list-all-glossaries get /v3/glossaries List every v3 multilingual glossary on your account with its metadata, without returning the 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 Reference for the deprecated v2/glossary-language-pairs endpoint. New integrations should use v3/languages instead. > [!WARNING] > > **`/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. > [!NOTE] > > 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 Create a dictionary in a v3 multilingual glossary, or replace the entries in an existing one, for one language pair. # Retrieve glossary details Source: https://developers.deepl.com/api-reference/multilingual-glossaries/retrieve-glossary-details get /v3/glossaries/{glossary_id} Retrieve the metadata for a single v3 multilingual glossary, including its dictionaries, without the entries. # Retrieve glossary entries Source: https://developers.deepl.com/api-reference/multilingual-glossaries/retrieve-glossary-entries get /v3/glossaries/{glossary_id}/entries Retrieve the term pairs stored in a v3 multilingual glossary dictionary, returned in TSV format. # Poll for the evaluation result Source: https://developers.deepl.com/api-reference/quality-evaluations/poll get /v1/quality-evaluations/{job_id} Poll a document's quality evaluation for its status and retrieve the per-segment report of translation issues once it completes. > [!WARNING] > > Quality evaluation is in [beta](/docs/resources/alpha-and-beta-features) and available to select DeepL customers only. To request access, contact your customer success manager. Evaluations are requested with `enable_quality_evaluation` on [`POST /v2/document`](/api-reference/document/upload-and-translate-a-document), which returns a `quality_evaluation_job_id`. That is the unique ID of the evaluation, and the value this endpoint takes. There is no public endpoint for starting an evaluation. For the end-to-end flow, see [Requesting a quality evaluation](/docs/best-practices/document-translations#requesting-a-quality-evaluation). Evaluations resolve only within the account that requested them. A job ID belonging to another account returns `404`, the same response as an unknown ID. ## Status values | **Value** | **HTTP status** | **Response body** | | - | - | - | | `processing` | `202` | `job_id` and `status` only. The `Retry-After` header carries the suggested number of seconds before the next poll. | | `done` | `200` | The full report in `segments`. | | `error` | `200` | An `error` object. The evaluation produced no report and will not produce one later. | A completed evaluation and a failed one both return `200`, so `status` rather than the HTTP status code distinguishes them. A malformed `job_id` returns `400` without reaching an evaluation. `error.code` is currently always `evaluation_failed`, so the response does not distinguish one cause of failure from another. ## Segment text A report carries one entry in `segments` per evaluated segment of the document, in reading order, including segments where no issue was found. On those, `errors` is an empty array. `segment_index` is that reading-order position. It is not a durable handle: re-translating the same document can produce a different segmentation. `segment_score` rates the translation quality of that segment from 0 (lowest) to 100 (highest). It is always present in a completed report. `source` and `target` hold the segment text as the evaluation read it, which is DeepL's own segmentation of the document with inline formatting tags removed. This text does not byte-match the text in the uploaded or translated file, and segment boundaries do not necessarily correspond to the paragraphs or sentences in the document. Span offsets index these two strings, not the uploaded or translated file. Both fields are omitted when the text is unavailable for a segment. An issue on such a segment still carries its spans, but there is no text in the response to resolve them against. `applied_glossary_term_pairs` lists the glossary term pairs applied to a segment when the document was translated. It is omitted for segments where no glossary term pair was applied. Each item gives the `source_term`, `target_term`, and the spans in `target` where the target term was applied. ## Span ranges Each span describes a half-open interval over the segment's source or target text. `start` is the index of the first Unicode code point in the span; `end` is one past the last. The span covers the substring `text[start:end]` in Python-style slicing, and its length is `end - start`. For example: ```text theme={null} target: Unser Q3-Umsatz lag 5 % unter der Prognose. ``` A target span of `{ "start": 20, "end": 29 }` covers `target[20:29]`, the 9 code points `"5 % unter"`. Offsets count code points, not UTF-16 code units, so a language whose characters fall outside the Basic Multilingual Plane indexes differently than a UTF-16 string length would suggest. * `source_spans` and `target_spans` are always present on an issue. If no span applies to that side, the value is an empty array * Within a single issue, the ranges in `source_spans` (and in `target_spans`) are sorted by `start` and do not overlap * Different issues may cover the same part of a segment * `ranges` on an applied glossary term pair follows the same convention and indexes into `target` ## Severity values The `severity` field on an issue takes one of three values. | **Value** | **Description** | | - | - | | `Critical` | Renders the content unusable or significantly changes its meaning. For example, the meaning is reversed, important information is missing, or the content is unsuitable for the intended audience. | | `Major` | Misleads the reader or significantly alters the meaning. The general idea is clear, but specific details are wrong, terminology is incorrect, or grammar issues make the text hard to understand. | | `Minor` | Does not make the text unreadable, but can make it harder to understand and less appealing. | ## Type and sub-type values Every issue carries a `type`, the top-level category, and a `sub_type`, which narrows it within that type. The five types and their sub-types are listed below. > [!NOTE] > > New values for `type`, `sub_type`, and `severity` may be added without warning. | **type** | **sub\_type** | **Description** | | - | - | - | | `Accuracy` | `Addition` | The target includes text that was not part of the source. | | `Accuracy` | `Omission` | Content from the source is missing from the target. | | `Accuracy` | `Mistranslation` | The content from the target doesn't match the content of the source. | | `Accuracy` | `Over-translation` | The target is more specific than the source. | | `Accuracy` | `Under-translation` | The target is less specific than the source. | | `Accuracy` | `Untranslated text` | Content that should have been translated has been left untranslated. | | `Fluency` | `Punctuation` | Punctuation is used incorrectly. | | `Fluency` | `Spelling` | Incorrect spelling in the target text. | | `Fluency` | `Grammar` | Issues with the grammar or syntax of the text, other than spelling and orthography. | | `Fluency` | `Grammatical register` | The text uses the wrong grammatical register, such as informal forms where formal is required. | | `Fluency` | `Inconsistency` | The text is not internally consistent. For example, using two different abbreviations for the same term. | | `Fluency` | `Character encoding` | Characters are garbled due to incorrect application of an encoding. | | `Style` | `Awkward` | The text is written in an awkward style. | | `Style` | `Inconsistent style` | The text uses inconsistent stylistic conventions across sections. | | `Miscellaneous` | Not enumerated | An error that does not fall into the other categories but is nonetheless clearly identifiable as a mistake. The sub-types of this type are not a fixed set, so read `sub_type` as free text. | | `InternalTQEError` | `SegmentTooLarge` | The segment's source and target exceed 10,000 characters combined, so it was not analyzed. Reported with severity `Critical`. | An issue of this type reports that a segment could not be evaluated rather than a defect in the translation. The rest of the document is evaluated normally. ## Supported language pairs | **Source language** | **Target languages** | | - | - | | German (`DE`) | English (`EN-GB`, `EN-US`) | | English (`EN`) | German (`DE`), Spanish (`ES`, `ES-419`), French (`FR`), Italian (`IT`), Japanese (`JA`), Korean (`KO`) | | Spanish (`ES`) | English (`EN-GB`, `EN-US`) | | French (`FR`) | English (`EN-GB`, `EN-US`) | | Italian (`IT`) | English (`EN-GB`, `EN-US`) | | Japanese (`JA`) | English (`EN-GB`, `EN-US`), Korean (`KO`), Chinese Simplified (`ZH-HANS`), Chinese Traditional (`ZH-HANT`) | | Korean (`KO`) | English (`EN-GB`, `EN-US`), Japanese (`JA`) | | Chinese (`ZH`) | Japanese (`JA`) | Requesting an evaluation for a pair that is not listed is rejected with `400` when the document is uploaded, so an evaluation only ever exists for a supported pair. See [`enable_quality_evaluation`](/api-reference/document/upload-and-translate-a-document). ## Rate limit This endpoint accepts 1,000 requests per minute per API key. Exceeding it returns `429 Too Many Requests`. ## Report retention A report is retained for 24 hours after its evaluation reaches `done` or `error`. After that, this endpoint returns `404 Not Found` for that job ID. # 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 style profile Source: https://developers.deepl.com/api-reference/style-profiles/create-style-profile post /v3/style_profiles Create a style profile that bundles glossaries, style rule lists and translation memories under one ID. # Delete a style profile Source: https://developers.deepl.com/api-reference/style-profiles/delete-style-profile delete /v3/style_profiles/{style_profile_id} Delete a style profile without deleting the components it links. # Retrieve a style profile Source: https://developers.deepl.com/api-reference/style-profiles/get-style-profile get /v3/style_profiles/{style_profile_id} Retrieve a style profile's linked components and metadata by its ID. # List style profiles Source: https://developers.deepl.com/api-reference/style-profiles/list-all-style-profiles get /v3/style_profiles Retrieve all style profiles available to your account, including the ones shared with it. # Update a style profile Source: https://developers.deepl.com/api-reference/style-profiles/update-style-profile patch /v3/style_profiles/{style_profile_id} Update a style profile's name, description, industry or linked components. # Create a custom instruction Source: https://developers.deepl.com/api-reference/style-rules/create-custom-instruction post /v3/style_rules/{style_id}/custom_instructions Add a custom instruction to a style rule list to shape translation output beyond the configured rules. # Create a style rule list Source: https://developers.deepl.com/api-reference/style-rules/create-style-rule post /v3/style_rules Create a style rule list, the container for the configured rules and custom instructions applied to translations. # 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} Remove a single custom instruction from a style rule list, leaving its configured rules in place. # Delete a style rule list Source: https://developers.deepl.com/api-reference/style-rules/delete-style-rule delete /v3/style_rules/{style_id} Delete a style rule list along with its configured rules and custom instructions. # 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} Retrieve a single custom instruction from a style rule list by its 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} Retrieve a single style rule list, including its configured rules and custom instructions. # Get all style rule lists Source: https://developers.deepl.com/api-reference/style-rules/list-all-style-rules get /v3/style_rules List every style rule list on your account, with the configured rules and custom instructions on each. # Replace configured rules Source: https://developers.deepl.com/api-reference/style-rules/update-configured-rules put /v3/style_rules/{style_id}/configured_rules Replace the full set of configured rules on a style rule list. Rules left out of the request are removed. # 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} Replace the text of an existing custom instruction on a style rule list. # 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} Rename an existing style rule list without changing its configured rules or custom instructions. # Translate text Source: https://developers.deepl.com/api-reference/translate/request-translation post /v2/translate Translate text between any supported language pair, with options for formality, glossaries, tag handling, and context. # 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. > [!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. 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 **1. Start the export** ```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" } } ``` **2. 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 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. > [!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. `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 **1. 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. ```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" } ``` **2. 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 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. **3. Poll the job** 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 the translation memories on your account, which store previously created translations for reuse. To apply a translation memory to a translation request, see [Using translation memories](/docs/customize/using-translation-memories). # 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. > [!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. ## 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`. > [!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. # 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 Reconnect to an interrupted real-time voice translation session and resume streaming audio. # Request Session Source: https://developers.deepl.com/api-reference/voice/request-session post /v3/voice/realtime Request a real-time voice translation session and receive the WebSocket streaming URL to connect to. # WebSocket Streaming Source: https://developers.deepl.com/api-reference/voice/websocket-streaming AsyncAPI reference for the real-time voice WebSocket protocol, covering the messages sent in each direction. > [!WARNING] > > Don't hardcode the WebSocket address shown on this page. Always connect to the `streaming_url` from the most recent [Request Session](/api-reference/voice/request-session) or [Reconnect Session](/api-reference/voice/reconnect-session) response, because it can change. # 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 [audio file translation](/api-reference/jobs-voice-translate)). * 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 `message` field of the response lists what's missing: ```json theme={null} { "message": "Missing required scope(s): glossaries:write. You can find more info in our docs: https://developers.deepl.com/docs/getting-started/auth" } ``` 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. This tab has two subtabs: "API Keys" and "Admin API Keys." The sections below — filtering, creating, copying, renaming, deactivating, setting usage limits, and setting permissions — all describe the "API Keys" subtab. See [Manage admin API keys](#manage-admin-api-keys) for how the "Admin API Keys" subtab differs. > [!NOTE] > > To create, deactivate, and limit developer keys programmatically instead, use the [Admin API](/docs/admin/overview#the-admin-api). ![](https://mintcdn.com/deepl-c950b784/V62dEoFj_Y8TzBkJ/_assets/images/limits-and-api-keys-home.png?fit=max\&auto=format\&n=V62dEoFj_Y8TzBkJ\&q=85\&s=96d34779833aefa3b56dbdefe6847bf6) The API Keys & Limits tab ## Filter API keys You can filter API keys by dimensions such as key status, key-level permissions, and consumption. Use this to quickly narrow the key table down to, for example, only active keys or only keys nearing their usage limit. ![](https://mintcdn.com/deepl-c950b784/V62dEoFj_Y8TzBkJ/_assets/images/filter-api-keys.png?fit=max\&auto=format\&n=V62dEoFj_Y8TzBkJ\&q=85\&s=4b4550c82a2a577fa33437b14116a66b) Filtering the API key table ## 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. ![](https://mintcdn.com/deepl-c950b784/V62dEoFj_Y8TzBkJ/_assets/images/create-api-key-dialog.png?fit=max\&auto=format\&n=V62dEoFj_Y8TzBkJ\&q=85\&s=249ab4de38272b0c50be0f5db2da96db) Create a new API key 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. ![](https://mintcdn.com/deepl-c950b784/V62dEoFj_Y8TzBkJ/_assets/images/create-api-key-copy-modal.png?fit=max\&auto=format\&n=V62dEoFj_Y8TzBkJ\&q=85\&s=73c64b5a8fb001e61e6b2d76d345ed6c) Copy an API key immediately after creating it 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. ![](https://mintcdn.com/deepl-c950b784/V62dEoFj_Y8TzBkJ/_assets/images/copy-api-key.png?fit=max\&auto=format\&n=V62dEoFj_Y8TzBkJ\&q=85\&s=b83f3e863f6f261c158dce3632cfcdc2) Copy an API key ## 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. ![](https://mintcdn.com/deepl-c950b784/V62dEoFj_Y8TzBkJ/_assets/images/rename-key.png?fit=max\&auto=format\&n=V62dEoFj_Y8TzBkJ\&q=85\&s=59fdcdf4e83af9a06d92e117aca63980) Renaming a key ## Deactivate a key > [!WARNING] > > 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. ![](https://mintcdn.com/deepl-c950b784/V62dEoFj_Y8TzBkJ/_assets/images/deactivate-key-step-1.png?fit=max\&auto=format\&n=V62dEoFj_Y8TzBkJ\&q=85\&s=96f967c487409b5f8e0b228d7382af7a) Select "Deactivate key" from key options ![](https://mintcdn.com/deepl-c950b784/V62dEoFj_Y8TzBkJ/_assets/images/deactivate-key-step-2.png?fit=max\&auto=format\&n=V62dEoFj_Y8TzBkJ\&q=85\&s=6d2c73d8c4c3a178022b3593aaa47eab) Confirm deactivation of key ## Set a key-level usage limit Key-level limits cap how much a key can consume in a one-month usage period. You can set a limit in characters (across text translation, document translation, and text improvement), STT minutes, or STS minutes. 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, choose the unit (characters, STT minutes, or STS minutes), and enter an amount. Setting the limit to 0 prevents the key from consuming any of that unit. ![](https://mintcdn.com/deepl-c950b784/V62dEoFj_Y8TzBkJ/_assets/images/set-key-level-limit-1.png?fit=max\&auto=format\&n=V62dEoFj_Y8TzBkJ\&q=85\&s=33c3cd55ab7c90c7d917f820adf7689b) Select "Set limit" from key options ![](https://mintcdn.com/deepl-c950b784/V62dEoFj_Y8TzBkJ/_assets/images/set-key-level-limit-2.png?fit=max\&auto=format\&n=V62dEoFj_Y8TzBkJ\&q=85\&s=6cf1517bdde8808a777ae9d4df912487) Activate and specify a limit 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. ![](https://mintcdn.com/deepl-c950b784/V62dEoFj_Y8TzBkJ/_assets/images/api-key-permissions-create.png?fit=max\&auto=format\&n=V62dEoFj_Y8TzBkJ\&q=85\&s=eb2894523cd13ab25197ebe28d2ab031) Creating a scoped API key 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. ![](https://mintcdn.com/deepl-c950b784/V62dEoFj_Y8TzBkJ/_assets/images/api-key-permissions-edit-dialog.png?fit=max\&auto=format\&n=V62dEoFj_Y8TzBkJ\&q=85\&s=cf7a4397fefef23a6aacd96dcc253b3d) Choose all access or custom permissions The "Permissions" column in the key table shows each key's status as a badge. Hover over a badge to see the assigned scopes. ![](https://mintcdn.com/deepl-c950b784/V62dEoFj_Y8TzBkJ/_assets/images/api-key-permissions-badge-hover.png?fit=max\&auto=format\&n=V62dEoFj_Y8TzBkJ\&q=85\&s=1613294881f4083047f61c80e4c0527f) Hover a badge to view 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) on the "Admin API Keys" subtab of the ["API Keys & Limits" tab](https://www.deepl.com/your-account/keys?keyType=admin). ![](https://mintcdn.com/deepl-c950b784/V62dEoFj_Y8TzBkJ/_assets/images/admin-api-tab.png?fit=max\&auto=format\&n=V62dEoFj_Y8TzBkJ\&q=85\&s=f2cd9d95961b3aa05551b2fe86d66268) The Admin Keys subtab Admin keys support the same actions as developer keys: create, copy, rename, and deactivate them from the "Admin API Keys" subtab 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 API Keys" subtab](https://www.deepl.com/your-account/keys?keyType=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 > **[Admin API Quickstart](/docs/admin/quickstart)** > > Create a developer key, cap its usage, and pull a usage report in four API calls. > **[Managing API Keys in the Account UI](/docs/admin/managing-api-keys)** > > Create, rename, deactivate, and set usage limits on developer and admin keys in the account UI. > **[Understanding API Key Permissions](/docs/admin/api-key-permissions)** > > Understand how permission scopes restrict what a developer key can do. > **[Retrieving Usage Data](/docs/admin/retrieving-usage-data)** > > Pick the right usage data source and pull consumption data for your account, keys, or custom tags. > **[Admin API Reference](/api-reference/admin-api/managing-developer-keys/create-key)** > > 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`. > **Translate text (translate:text)** > > | **Method** | **Endpoint** | > | - | - | > | `POST` | [`/v2/translate`](/api-reference/translate/request-translation) | > **Translate document (translate:document)** > > | **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) | > **Translate real-time speech (voice:realtime)** > > | **Method** | **Endpoint** | > | - | - | > | `POST` | [`/v3/voice/realtime`](/api-reference/voice/request-session) | > | `GET` | [`/v3/voice/realtime`](/api-reference/voice/reconnect-session) | > > This scope also covers the [WebSocket stream](/api-reference/voice/websocket-streaming). You open the stream with the streaming URL and token from one of the endpoints above, so a scoped key needs `voice:realtime` to use any part of a real-time session. > **Rephrase or correct text (write:improve)** > > | **Method** | **Endpoint** | > | - | - | > | `POST` | [`/v2/write/rephrase`](/api-reference/improve-text/request-text-improvement) | > | `POST` | [`/v2/write/correct`](/api-reference/improve-text/correct-text) | > **View glossaries and their entries (glossaries:read)** > > | **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) | > **Create, modify, and delete glossaries (glossaries:write)** > > | **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) | > **View style rules and custom instructions (style\_rules:read)** > > | **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) | > **Create, modify, and delete style rules and custom instructions (style\_rules:write)** > > | **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) | > **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`. > **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) | > **Retrieve languages and resources (languages:read)** > > | **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) | > **View subscription usage and limits (usage:read)** > > | **Method** | **Endpoint** | > | - | - | > | `GET` | [`/v2/usage`](/api-reference/usage-and-quota/check-usage-and-limits) | > [!NOTE] > > No scope covers [audio file translation](/api-reference/jobs-voice-translate) yet, so those endpoints are accessible only with unrestricted keys. # 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 > [!TIP] > > 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, "speech_to_speech_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, "speech_to_speech_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, "speech_to_speech_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. > [!NOTE] > > 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, per custom tag, per language pair, or per document | Preset ranges for all reports; custom ranges up to 4 months and optional grouping by day for key-level and tag-level reports | 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, per custom tag, per language pair, or per document, 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 consumption for each API key in a single unit at a time — characters, speech-to-text (STT) minutes, or speech-to-speech (STS) minutes. ![](https://mintcdn.com/deepl-c950b784/V62dEoFj_Y8TzBkJ/_assets/images/api-keys-and-limits-with-usage.png?fit=max\&auto=format\&n=V62dEoFj_Y8TzBkJ\&q=85\&s=46f99154b47d44f470708abaf2351b67) The API Keys & Limits tab Use the unit dropdown above the key table to switch which usage unit the table displays. ![](https://mintcdn.com/deepl-c950b784/V62dEoFj_Y8TzBkJ/_assets/images/api-keys-and-limits-with-usage-unit-dropdown.png?fit=max\&auto=format\&n=V62dEoFj_Y8TzBkJ\&q=85\&s=c8ab84605e386a10463fd97b08f0b865) Switching the displayed usage unit ## 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. ![](https://mintcdn.com/deepl-c950b784/G4w6sAcpHNkksvKb/_assets/images/api-growth-annual-usage-tab.png?fit=max\&auto=format\&n=G4w6sAcpHNkksvKb\&q=85\&s=01cd3997908131a0b7f97d2f4d3802fa) The API Usage tab ## CSV export Generate a CSV report of your usage for a chosen time period. Four 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**: characters translated per [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. * **Language pair-level report**: characters translated per source-target language pair. Generally available on the API Enterprise plan. On the API Pro plan, it's available on request; contact your customer success manager to enable it. * **Document translation report**: one row per translated document, with the document type, language pair, input characters, billed characters (reflecting any [billing minimum](/docs/best-practices/document-translations#billing-minimums) applied), and whether a glossary was used. Use it to trace document translation usage back to individual documents. Generally available on the API Enterprise plan. On the API Pro plan, it's available on request; contact your customer success manager to enable it. 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," "Custom tag-level report," "Language pair-level report," or "Document translation report" from the dropdown. Then select a time period and click "Download your report". ![](https://mintcdn.com/deepl-c950b784/tcrlYvr_Q3YiDP74/_assets/images/download-csv-usage-report.png?fit=max\&auto=format\&n=tcrlYvr_Q3YiDP74\&q=85\&s=123aa3484501c7961e8791354104810c) Select a report type from the "Download CSV usage report" dropdown Select one of the presets or, for the API key-level and custom tag-level reports, a custom date range of up to 4 months (on UTC calendar days). The language pair-level and document translation reports currently support presets only. For the API key-level and custom tag-level reports, presets cover rolling windows from the last 24 hours up to the last year, as well as calendar months and usage periods. The language pair-level and document translation reports offer these presets: * Last 24 hours * Last 7 days * Last 30 days * Current month * Last month * Current usage period * Last usage period 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. In the API key-level and custom tag-level reports, you can also group usage data by UTC calendar day for any time range. The language pair-level and document translation reports currently don't support grouping by day. ![](https://mintcdn.com/deepl-c950b784/5Ui5bPgQu6CdkNqe/_assets/images/group-by-day-option.png?fit=max\&auto=format\&n=5Ui5bPgQu6CdkNqe\&q=85\&s=f0836efccbec267aaa40b4888e29ffc4) Select the "Group by day" option to group usage data by UTC calendar day > [!NOTE] > > 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 Why the DeepL API rejects requests sent directly from a browser, and how to call it from your backend instead. 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 Set a subscription-wide limit on characters, STT minutes, or STS minutes to cap your maximum costs. As a DeepL API Pro user, you have the option to set a limit on the amount of characters, STT minutes, or STS minutes consumed across your entire subscription per month, limiting your maximum costs. You activate "Cost Control" on the ["API Usage" tab](https://www.deepl.com/your-account/usage), the central hub for usage and cost settings. This limit applies subscription-wide; for a limit on a single key instead, see [Set a key-level usage limit](/docs/admin/managing-api-keys#set-a-key-level-usage-limit). ## Set a cost control limit 1. Navigate to the ["API Usage" tab](https://www.deepl.com/your-account/usage) 2. Click to change cost control settings for the relevant unit — a separate link manages the limit for each of characters, STT minutes, and STS minutes 3. Check the box to activate the limit 4. Enter the maximum consumption per month 5. Click "Submit" to save the changes Your new cost control limit is applied immediately. ![](https://mintcdn.com/deepl-c950b784/V62dEoFj_Y8TzBkJ/_assets/images/subscription-level-cost-control.png?fit=max\&auto=format\&n=V62dEoFj_Y8TzBkJ\&q=85\&s=4bce05df078f738e714f3808d949cd1f) Per-unit links for managing subscription-level cost control Once you reach your limit, DeepL stops processing further translation requests until the end of the usage period, to avoid exceeding your set maximum cost. If you want to translate more, you can change the cost control limit at any time. Within the characters unit, the limit is enforced against the sum of Translate API and Write API characters; it's not possible to set separate limits for each. Characters, STT minutes, and STS minutes each have their own independent limit. ## FAQ #### 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 receive a notification email for security reasons. If you receive such a notification but 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 Handle file size limits, status codes, quality evaluation reports, and error details when translating documents with the DeepL API. 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. Formats currently in beta are not billed and are not subject to this minimum. ### 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 ``, ``, `