3

Since DeepL updated their API which also their website (https://www.deepl.com/translator) uses, an error appears when you're requesting a translation via Java or Python. It says "Too many requests." A year ago the answer here (Using DeepL API to translate text) from EmilioK worked.

Because of their update, the API URL changed to "https://www2.deepl.com/jsonrpc". But the response is {"jsonrpc": "2.0","error":{"code":1042901,"message":"Too many requests."}}. Translating via the website works, so they seem to have implemented a background check. I already tried to debug their JavaScript code, but I don't understand what I'm doing wrong. At last I tried to re-build the requests by analysing the network traffic from the homepage. Didn't work either. Besides, it seems like others are having the same problem (https://github.com/EmilioK97/pydeepl/issues/12, https://github.com/vsetka/deepl-translator/issues/9).

I used Java 8 with the Apache HttpClient 4.5.8:

private static final String BASE_URL = "https://www2.deepl.com/jsonrpc";
private static AtomicInteger requestId = new AtomicInteger(10000 * (int) Math.round(10000 * Math.random()));
private static final CloseableHttpClient HTTP_CLIENT = HttpClients.createDefault();

public void example() {
    HttpResponse httpResponse = HTTP_CLIENT.execute(generateRequest("Hello", "EN", "DE"), getContext());
    JSONObject response = new JSONObject(EntityUtils.toString(httpResponse.getEntity()));
    System.out.println(response.toString());
}

HttpContext getContext() {
    CookieStore cookieStore = new BasicCookieStore();
    BasicClientCookie cookie = new BasicClientCookie("LMTBID", "3389513b-e369-4810-a2f3-73b9405e0b0d|403992a1240e1eca6c6e98b428849a3c");

    cookie.setDomain(".deepl.com");
    cookie.setPath("/");
    cookie.setAttribute(ClientCookie.DOMAIN_ATTR, "true");

    cookieStore.addCookie(cookie);
    HttpContext localContext = new BasicHttpContext();
    localContext.setAttribute(HttpClientContext.COOKIE_STORE, cookieStore);

    return localContext;
}

HttpPost generateRequest(String text, String from, String to) {
    HttpPost request = new HttpPost(BASE_URL);
    StringEntity params = new StringEntity(getJsonRequest(text, from, to).toString(), StandardCharsets.UTF_8);

    request.addHeader(HttpHeaders.HOST, "www2.deepl.com");
    request.addHeader(HttpHeaders.USER_AGENT, "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0");
    request.addHeader(HttpHeaders.ACCEPT, "*/*");
    request.addHeader(HttpHeaders.ACCEPT_LANGUAGE, "de,en-US;q=0.7,en;q=0.3");
    request.addHeader(HttpHeaders.ACCEPT_ENCODING, "gzip, deflate, br");
    request.addHeader(HttpHeaders.REFERER, "https://www.deepl.com/translator");
    request.addHeader(HttpHeaders.CONTENT_TYPE, "text/plain");
    request.addHeader(HttpHeaders.CONNECTION, "keep-alive");
    request.addHeader("X-Requested-With", "XMLHttpRequest");
    request.addHeader("Origin", "https://www.deepl.com");
    request.addHeader("DNT", "1");
    request.addHeader("TE", "Trailers");

    request.setEntity(params);
    request.setConfig(requestConfig);

    return request;
}

private static JSONObject getJsonRequest(String text, String from, String to) {
    JSONObject send = new JSONObject();
    send.put("id", requestId.incrementAndGet());
    send.put("jsonrpc", "2.0");
    send.put("method", "LMT_handle_jobs");

    JSONObject paramsObject = new JSONObject();

    JSONArray jobsArray = new JSONArray();

    JSONObject jobsObject = new JSONObject();
    jobsObject.put("kind", "default");
    jobsObject.put("quality", "fast");
    jobsObject.put("raw_en_context_after", new JSONArray());
    jobsObject.put("raw_en_context_before", new JSONArray());
    jobsObject.put("raw_en_sentence", text);

    jobsArray.put(jobsObject);

    JSONObject langObject = new JSONObject();

    JSONArray userPreferredLangsArray = new JSONArray();
    userPreferredLangsArray.put(to);
    userPreferredLangsArray.put(from);

    langObject.put("source_lang_user_selected", from.getLanguageCode());
    langObject.put("target_lang", to);
    langObject.put("user_preferred_langs", userPreferredLangsArray);

    paramsObject.put("jobs", jobsArray);
    paramsObject.put("lang", langObject);
    paramsObject.put("priority", -1);
    paramsObject.put("timestamp", System.currentTimeMillis());

    send.put("params", paramsObject);

    return send;
}

The result should look like this, but I don't remember exactly:

{"result":{"source_lang":"EN","target_lang":"DE","translations":[{"beams":[{"postprocessed_sentence":"Hallo","score":0.5,"totalLogProb":0.3,"num_symbols":1},{"postprocessed_sentence":"Guten Tag","score":0.3,"totalLogProb":0.7,"num_symbols":2}]}]}}

Dharman
  • 30,962
  • 25
  • 85
  • 135
Celsius
  • 104
  • 1
  • 9
  • 2
    Have you signed up and gotten an authentication key? According to their docs, you'll need to send the key with each request: https://www.deepl.com/docs-api.html?part=accessing – nvioli May 13 '19 at 14:37
  • No, I wanna use the free version, like it's working on their website (https://www.deepl.com/translator). Before, you could just request it like Emilio says here: https://stackoverflow.com/questions/45937616/using-deepl-api-to-translate-text – Celsius May 13 '19 at 14:39
  • @Celsius someone commented on that answer over a year ago to say it had stopped working....tbh it makes sense if they released a proper API and commercial version that older ways would stop working. Maybe you should contact their support to see if this jsonrpc service is actually still supported or not, or whether you need an auth key. See also https://www.deepl.com/docs-api.html?part=accessing – ADyson May 13 '19 at 14:47
  • I'm asking here because I found no solution, just like the person who wrote the comment. And it has to work somehow, else their website (deepl.com/translator) wouldn't work. But I'm not sure what does the trick. – Celsius May 13 '19 at 15:05
  • 2
    I agree it's unclear what the difference is. I tried to make an identical request via PostMan and got the same 429 response. I would guess that the issue is something to do with the session cookies perhaps, but it's unclear how exactly it knows that the request is coming from its own page and not from an imitator. But what I would imagine is that, now they have a paid-for API, it will hurt their business to have everyone freeloading off this URL, and therefore they will have worked very hard on preventing unauthorised access to it. – ADyson May 13 '19 at 21:04
  • 2
    I used Google Chrome to inspect the request and I noticed that an "id" parameter is sent by your navigator. If I use Chrome's debugger to create a cURL command line request and execute it in a shell, it works. If I change the sent "id" and put a random value then I get the "Too many requests" response too. So I imagine that the JS that makes the request recieves a valid "id" value from the web page. This is quite normal because they don't want people to bypass their API. By the way, I have now been blacklisted by deepl for a while :-/ – Patrick Janser Sep 06 '19 at 11:54

1 Answers1

0

I can't tell the length of the text items you are trying to translate, but I had to send in groups of text of max 1000 at a time. Also I have a subscription and not using the free side.

Jeff Blumenthal
  • 442
  • 6
  • 8