{"id":15125,"date":"2023-12-04T11:52:27","date_gmt":"2023-12-04T19:52:27","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=15125"},"modified":"2023-12-12T12:52:52","modified_gmt":"2023-12-12T20:52:52","slug":"spring-webclient-429-ratelimit-errors","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/es\/spring-webclient-429-ratelimit-errors\/","title":{"rendered":"Gesti\u00f3n de errores 429 RateLimit con Spring WebClient"},"content":{"rendered":"<p><span style=\"font-weight: 400;\">Recientemente me he hecho cargo del mantenimiento de un <a href=\"https:\/\/spring.io\/projects\/spring-boot\/\">Spring Boot<\/a> proyecto. Este proyecto tiene algunas <em>L\u00edmite de tarifa <\/em>errores en los registros cuando la aplicaci\u00f3n se pon\u00eda en contacto con una API REST remota. Resulta que esta aplicaci\u00f3n tambi\u00e9n utilizaba el m\u00e9todo s\u00edncrono y bloqueante <em>Plantilla RestTemplate<\/em> para realizar las llamadas a la API, en lugar del m\u00e1s reciente Spring <em>WebClient<\/em>que resulta que utiliza la API Reactor bajo el cap\u00f3.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00bfY sabes qu\u00e9 es lo mejor de Reactor y del uso de APIs Reactivas en general? Hace que programar con Data Stream sea muy f\u00e1cil. Lo que tambi\u00e9n significa que facilita la implementaci\u00f3n de estrategias de reintento.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Hablemos primero del error en el registro. Lo que obtuve se parec\u00eda a esto:<\/span><\/p>\n<pre class=\"nums:false nums-toggle:false lang:default decode:true\">org.springframework.web.client.HttpClientErrorException$TooManyRequests: 429 Too Many Requests: [{\"response_type\":\"ERROR\",\"message\":\"Number of requests has exceeded the 1 minute limit\"}]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:137) ~[spring-web-5.2.9.RELEASE.jar!\/:5.2.9.RELEASE]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:184) ~[spring-web-5.2.9.RELEASE.jar!\/:5.2.9.RELEASE]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:125) ~[spring-web-5.2.9.RELEASE.jar!\/:5.2.9.RELEASE]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63) ~[spring-web-5.2.9.RELEASE.jar!\/:5.2.9.RELEASE]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:782) ~[spring-web-5.2.9.RELEASE.jar!\/:5.2.9.RELEASE]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:740) ~[spring-web-5.2.9.RELEASE.jar!\/:5.2.9.RELEASE]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:674) ~[spring-web-5.2.9.RELEASE.jar!\/:5.2.9.RELEASE]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:315) ~[spring-web-5.2.9.RELEASE.jar!\/:5.2.9.RELEASE]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at com.couchbase.training.couchlms.repository.LmsRepository.getCourseModules(LmsRepository.java:102) ~[classes!\/:0.0.40-SNAPSHOT]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at com.couchbase.training.couchlms.services.LmsProcessor.processCourseModules(LmsProcessor.java:147) [classes!\/:0.0.40-SNAPSHOT]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at com.couchbase.training.couchlms.services.LmsProcessor.processCourses(LmsProcessor.java:91) [classes!\/:0.0.40-SNAPSHOT]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at com.couchbase.training.couchlms.config.SchedulingConfig.scheduledCoursesPuller(SchedulingConfig.java:45) [classes!\/:0.0.40-SNAPSHOT]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_252]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_252]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_252]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_252]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84) [spring-context-5.2.9.RELEASE.jar!\/:5.2.9.RELEASE]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) [spring-context-5.2.9.RELEASE.jar!\/:5.2.9.RELEASE]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_252]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308) [na:1.8.0_252]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_252]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294) [na:1.8.0_252]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_252]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_252]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0at java.lang.Thread.run(Thread.java:748) [na:1.8.0_252]<\/pre>\n<p><span style=\"font-weight: 400;\">Como de costumbre, la parte interesante de las trazas de pila est\u00e1 en la parte superior. El error es <\/span><strong>429 Demasiadas solicitudes<\/strong><span style=\"font-weight: 400;\">y el mensaje dice que hay un <strong>L\u00edmite de velocidad de 1 minuto<\/strong>. Descomponiendo esto, el estado HTTP<\/span>\u00a0<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Status\/429\"><span style=\"font-weight: 400;\">el c\u00f3digo devuelto es 429<\/span><\/a><span style=\"font-weight: 400;\">. Se trata de un error de l\u00edmite de velocidad, lo que significa que la API indica a la persona que llama que ha enviado demasiadas peticiones. Por lo general, esto se puede resolver esperando un poco, e incluso podr\u00eda tener un <em>Reintentar despu\u00e9s de<\/em>\u00a0en la respuesta que le indica cu\u00e1nto tiempo tiene que esperar. <\/span><\/p>\n<p><span style=\"font-weight: 400;\">Veamos c\u00f3mo podemos obtener esa informaci\u00f3n con WebClient de Spring:<\/span><\/p>\n<pre class=\"nums:false lang:java decode:true\">.uri(ub -&gt; ub.pathSegment(uri).queryParams(queryParams).build())\r\n.retrieve()\r\n.onStatus(\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0HttpStatus.TOO_MANY_REQUESTS::equals,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0response -&gt; {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0List&lt;String&gt; header = response.headers().header(\"Retry-After\");\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Integer delayInSeconds;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (!header.isEmpty()) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0delayInSeconds = Integer.valueOf(header.get(0));\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0} else {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0delayInSeconds = 60;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return response.bodyToMono(String.class).map(msg -&gt; new RateLimitException(msg, delayInSeconds));\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0})\r\n.bodyToMono(String.class)<\/pre>\n<p>Este c\u00f3digo est\u00e1 enviando un <em>GET<\/em> petici\u00f3n. El WebClient nos permite echar un vistazo a la respuesta de la petici\u00f3n y reaccionar adecuadamente gracias a la funci\u00f3n <em>onStatus<\/em> m\u00e9todo. El primer par\u00e1metro es un booleano utilizado para filtrar el c\u00f3digo de estado HTTP devuelto. Aqu\u00ed, cuando el c\u00f3digo de estado es 429, hacemos algo.<\/p>\n<p><span style=\"font-weight: 400;\">Echamos un vistazo a la <em>Respuesta<\/em> mirar si hay un <em>Reintentar despu\u00e9s de<\/em> si es as\u00ed, inicializamos la cabecera <em>delayInSeconds<\/em> si no, establecemos un valor por defecto de 60. Entonces podemos devolver el <em>Respuesta<\/em> asignado a un <em>RateLimitException<\/em>. El contenido del cuerpo se utilizar\u00e1 como mensaje de error y el delayInSeconds estar\u00e1 disponible en un campo separado. Echa un vistazo al c\u00f3digo de Excepci\u00f3n para m\u00e1s detalles:<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true\">import java.time.Duration;\r\npublic class RateLimitException extends Throwable {\r\n\u00a0\u00a0\u00a0\u00a0private int retryAfterDelay = 60;\r\n\r\n\u00a0\u00a0\u00a0\u00a0public RateLimitException(String message) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0super(message);\r\n\u00a0\u00a0\u00a0\u00a0}\r\n\r\n\u00a0\u00a0\u00a0\u00a0public RateLimitException(String message, int retryAfterDelay) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0super(message); this.retryAfterDelay = retryAfterDelay;\r\n\u00a0\u00a0\u00a0\u00a0}\r\n\r\n\u00a0\u00a0\u00a0\u00a0public int getRetryAfterDelay() {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return retryAfterDelay;\r\n\u00a0\u00a0\u00a0\u00a0}\r\n\r\n\u00a0\u00a0\u00a0\u00a0public Duration getRetryAfterDelayDuration() {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return Duration.ofSeconds(retryAfterDelay);\r\n\u00a0\u00a0\u00a0\u00a0}\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">As\u00ed que lo que hay que hacer es capturar este error espec\u00edfico y luego volver a intentarlo despu\u00e9s de la duraci\u00f3n dada. Reactor lo hace f\u00e1cil proporcionando <em>Reintentos<\/em> estrategias. Todo lo que tiene que hacer es llamar al <em>reintentarCuando<\/em> m\u00e9todo:<\/span><\/p>\n<pre class=\"nums:false lang:java decode:true\">.bodyToMono(String.class)\r\n.retryWhen(Retry.withThrowable(throwableFlux -&gt; {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return throwableFlux.filter(t -&gt; t instanceof RateLimitException).map(t -&gt; {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0RateLimitException rle = (RateLimitException) t;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return Retry.fixedDelay(1, rle.getRetryAfterDelayDuration());\r\n\u00a0\u00a0\u00a0\u00a0});<\/pre>\n<p><span style=\"font-weight: 400;\">Existen diferentes <em>Reintentar<\/em> podemos utilizar el m\u00e9todo <em>withThrowable()<\/em> constructor. Da un Flux que debe contener el <em>RateLimitException<\/em>. As\u00ed que empezamos aplicando un filtro para asegurarnos de ello. Luego mapeamos esa excepci\u00f3n al objeto Retry real. Aqu\u00ed es el <em>Retry.fixedDelay<\/em> tomando como par\u00e1metros un m\u00e1ximo de intentos y una duraci\u00f3n. La duraci\u00f3n procede del <em>RateLimitException<\/em> que se lanz\u00f3 antes.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Con eso, cada vez que una petici\u00f3n devuelva un 429, el cliente esperar\u00e1 el tiempo apropiado hasta que reintente. Y fue mucho m\u00e1s f\u00e1cil de implementar con Reactor que usando <em>try\/catch<\/em> con Spring's <em>Plantilla RestTemplate<\/em>. S\u00e9 que la programaci\u00f3n reactiva puede ser un poco intimidante al principio, pero es una gran manera de gestionar flujos de datos como solicitudes y respuestas HTTP, o para gestionar las conexiones a bases de datos que soportan la programaci\u00f3n reactiva, como <a href=\"https:\/\/www.couchbase.com\/blog\/es\/\">Couchbase<\/a>.<\/span><\/p>\n<p>\u00bfQuieres m\u00e1s ayuda e ideas?<\/p>\n<ul>\n<li><a href=\"https:\/\/www.couchbase.com\/blog\/es\/couchbase-on-discord\/\">\u00danete a nuestro servidor Discord para chatear<\/a><\/li>\n<li><a href=\"https:\/\/www.couchbase.com\/blog\/es\/introducing-the-couchbase-community-hub\/\">\u00danase a nuestro Community Hub para conectarse<\/a><\/li>\n<\/ul>","protected":false},"excerpt":{"rendered":"<p>I have recently taken over the maintenance of a Spring Boot project. This project has some RateLimit errors in the logs when the app was contacting a remote REST API. Turns out that this app was also using the synchronous, [&hellip;]<\/p>","protected":false},"author":49,"featured_media":15126,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1814,6343,2201],"tags":[1316,1630],"ppma_author":[9023],"class_list":["post-15125","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-application-design","category-spring-boot","category-tools-sdks","tag-error-handling","tag-spring-boot"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.8 (Yoast SEO v25.8) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Manage 429 RateLimit Errors with Spring WebClient - The Couchbase Blog<\/title>\n<meta name=\"description\" content=\"As usual, the interesting bit in stack traces is at the top. The error is 429 Too Many Requests, and the message says there is a 1 minute rate limit.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.couchbase.com\/blog\/es\/spring-webclient-429-ratelimit-errors\/\" \/>\n<meta property=\"og:locale\" content=\"es_MX\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Manage 429 RateLimit Errors with Spring WebClient\" \/>\n<meta property=\"og:description\" content=\"As usual, the interesting bit in stack traces is at the top. The error is 429 Too Many Requests, and the message says there is a 1 minute rate limit.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/es\/spring-webclient-429-ratelimit-errors\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2023-12-04T19:52:27+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-12-12T20:52:52+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/12\/spring-boot-ratelimit-error.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"628\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Laurent Doguin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@ldoguin\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"unstructured.io\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"3 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/\"},\"author\":{\"name\":\"Laurent Doguin\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/c0aa9b8f1ed51b7a9e2f7cb755994a5e\"},\"headline\":\"Manage 429 RateLimit Errors with Spring WebClient\",\"datePublished\":\"2023-12-04T19:52:27+00:00\",\"dateModified\":\"2023-12-12T20:52:52+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/\"},\"wordCount\":532,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/12\/spring-boot-ratelimit-error.jpg\",\"keywords\":[\"Error handling\",\"spring-boot\"],\"articleSection\":[\"Application Design\",\"Spring Boot\",\"Tools &amp; SDKs\"],\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/\",\"name\":\"Manage 429 RateLimit Errors with Spring WebClient - The Couchbase Blog\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/12\/spring-boot-ratelimit-error.jpg\",\"datePublished\":\"2023-12-04T19:52:27+00:00\",\"dateModified\":\"2023-12-12T20:52:52+00:00\",\"description\":\"As usual, the interesting bit in stack traces is at the top. The error is 429 Too Many Requests, and the message says there is a 1 minute rate limit.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/#breadcrumb\"},\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/12\/spring-boot-ratelimit-error.jpg\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/12\/spring-boot-ratelimit-error.jpg\",\"width\":1200,\"height\":628},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Manage 429 RateLimit Errors with Spring WebClient\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"name\":\"The Couchbase Blog\",\"description\":\"Couchbase, the NoSQL Database\",\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"es\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"width\":218,\"height\":34,\"caption\":\"The Couchbase Blog\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/c0aa9b8f1ed51b7a9e2f7cb755994a5e\",\"name\":\"Laurent Doguin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/12929ce99397769f362b7a90d6b85071\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/b8c466908092b46634af916b6921f30187a051e4367ded7ac9b1a3f2c5692fd2?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/b8c466908092b46634af916b6921f30187a051e4367ded7ac9b1a3f2c5692fd2?s=96&d=mm&r=g\",\"caption\":\"Laurent Doguin\"},\"description\":\"Laurent is a nerdy metal head who lives in Paris. He mostly writes code in Java and structured text in AsciiDoc, and often talks about data, reactive programming and other buzzwordy stuff. He is also a former Developer Advocate for Clever Cloud and Nuxeo where he devoted his time and expertise to helping those communities grow bigger and stronger. He now runs Developer Relations at Couchbase.\",\"sameAs\":[\"https:\/\/x.com\/ldoguin\"],\"honorificPrefix\":\"Mr\",\"birthDate\":\"1985-06-07\",\"gender\":\"male\",\"award\":[\"Devoxx Champion\",\"Couchbase Legend\"],\"knowsAbout\":[\"Java\"],\"knowsLanguage\":[\"English\",\"French\"],\"jobTitle\":\"Director Developer Relation & Strategy\",\"worksFor\":\"Couchbase\",\"url\":\"https:\/\/www.couchbase.com\/blog\/es\/author\/laurent-doguin\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Manage 429 RateLimit Errors with Spring WebClient - The Couchbase Blog","description":"Como de costumbre, la parte interesante de las trazas de pila est\u00e1 en la parte superior. El error es 429 Too Many Requests, y el mensaje dice que hay un l\u00edmite de velocidad de 1 minuto.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.couchbase.com\/blog\/es\/spring-webclient-429-ratelimit-errors\/","og_locale":"es_MX","og_type":"article","og_title":"Manage 429 RateLimit Errors with Spring WebClient","og_description":"As usual, the interesting bit in stack traces is at the top. The error is 429 Too Many Requests, and the message says there is a 1 minute rate limit.","og_url":"https:\/\/www.couchbase.com\/blog\/es\/spring-webclient-429-ratelimit-errors\/","og_site_name":"The Couchbase Blog","article_published_time":"2023-12-04T19:52:27+00:00","article_modified_time":"2023-12-12T20:52:52+00:00","og_image":[{"width":1200,"height":628,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/12\/spring-boot-ratelimit-error.jpg","type":"image\/jpeg"}],"author":"Laurent Doguin","twitter_card":"summary_large_image","twitter_creator":"@ldoguin","twitter_misc":{"Written by":"unstructured.io","Est. reading time":"3 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/"},"author":{"name":"Laurent Doguin","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/c0aa9b8f1ed51b7a9e2f7cb755994a5e"},"headline":"Manage 429 RateLimit Errors with Spring WebClient","datePublished":"2023-12-04T19:52:27+00:00","dateModified":"2023-12-12T20:52:52+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/"},"wordCount":532,"commentCount":0,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/12\/spring-boot-ratelimit-error.jpg","keywords":["Error handling","spring-boot"],"articleSection":["Application Design","Spring Boot","Tools &amp; SDKs"],"inLanguage":"es","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/","url":"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/","name":"Manage 429 RateLimit Errors with Spring WebClient - The Couchbase Blog","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/12\/spring-boot-ratelimit-error.jpg","datePublished":"2023-12-04T19:52:27+00:00","dateModified":"2023-12-12T20:52:52+00:00","description":"Como de costumbre, la parte interesante de las trazas de pila est\u00e1 en la parte superior. El error es 429 Too Many Requests, y el mensaje dice que hay un l\u00edmite de velocidad de 1 minuto.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/#breadcrumb"},"inLanguage":"es","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/"]}]},{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/12\/spring-boot-ratelimit-error.jpg","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/12\/spring-boot-ratelimit-error.jpg","width":1200,"height":628},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/spring-webclient-429-ratelimit-errors\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Manage 429 RateLimit Errors with Spring WebClient"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"El blog de Couchbase","description":"Couchbase, la base de datos NoSQL","publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"es"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"El blog de Couchbase","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","width":218,"height":34,"caption":"The Couchbase Blog"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/c0aa9b8f1ed51b7a9e2f7cb755994a5e","name":"Laurent Doguin","image":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/12929ce99397769f362b7a90d6b85071","url":"https:\/\/secure.gravatar.com\/avatar\/b8c466908092b46634af916b6921f30187a051e4367ded7ac9b1a3f2c5692fd2?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/b8c466908092b46634af916b6921f30187a051e4367ded7ac9b1a3f2c5692fd2?s=96&d=mm&r=g","caption":"Laurent Doguin"},"description":"Laurent es un metalero empoll\u00f3n que vive en Par\u00eds. Principalmente escribe c\u00f3digo en Java y texto estructurado en AsciiDoc, y a menudo habla sobre datos, programaci\u00f3n reactiva y otras cosas de moda. Tambi\u00e9n fue Developer Advocate de Clever Cloud y Nuxeo, donde dedic\u00f3 su tiempo y experiencia a ayudar a esas comunidades a crecer y fortalecerse. Ahora dirige las relaciones con los desarrolladores en Couchbase.","sameAs":["https:\/\/x.com\/ldoguin"],"honorificPrefix":"Mr","birthDate":"1985-06-07","gender":"male","award":["Devoxx Champion","Couchbase Legend"],"knowsAbout":["Java"],"knowsLanguage":["English","French"],"jobTitle":"Director Developer Relation & Strategy","worksFor":"Couchbase","url":"https:\/\/www.couchbase.com\/blog\/es\/author\/laurent-doguin\/"}]}},"authors":[{"term_id":9023,"user_id":49,"is_guest":0,"slug":"laurent-doguin","display_name":"Laurent Doguin","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/b8c466908092b46634af916b6921f30187a051e4367ded7ac9b1a3f2c5692fd2?s=96&d=mm&r=g","author_category":"","last_name":"Doguin","first_name":"Laurent","job_title":"","user_url":"","description":"Laurent es un metalero empoll\u00f3n que vive en Par\u00eds. Principalmente escribe c\u00f3digo en Java y texto estructurado en AsciiDoc, y a menudo habla sobre datos, programaci\u00f3n reactiva y otras cosas de moda. Tambi\u00e9n fue Developer Advocate de Clever Cloud y Nuxeo, donde dedic\u00f3 su tiempo y experiencia a ayudar a esas comunidades a crecer y fortalecerse. Ahora dirige las relaciones con los desarrolladores en Couchbase."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts\/15125","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/users\/49"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/comments?post=15125"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts\/15125\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/media\/15126"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/media?parent=15125"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/categories?post=15125"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/tags?post=15125"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/ppma_author?post=15125"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}