Spring Boot RestTemplate POST JSON with a Bearer token

Set JSON content type and setBearerAuth on HttpHeaders, wrap them in an HttpEntity, call postForEntity, and catch the 4xx exception to read the body.

5 min readSume
All posts

To POST JSON with Spring Boot's RestTemplate, create HttpHeaders, call setContentType(MediaType.APPLICATION_JSON) and setBearerAuth(token), wrap the body and headers in an HttpEntity, and pass it to restTemplate.postForEntity(url, entity, String.class). A 4xx reply doesn't come back as a response: RestTemplate throws HttpClientErrorException, so catch it and read getResponseBodyAsString() for the API's error.

Spring facts come from the Javadoc for RestTemplate, HttpHeaders, DefaultResponseErrorHandler and RestClientResponseException, the REST Clients reference and Spring Boot's Calling REST Services. The example API is Sume's, from Authentication, Video Generation, Errors and rate limits and Jobs and results. All were read on 2026-09-29. Sume's only documented client library is the TypeScript SDK, so from Java this is a plain HTTPS call.

How do I POST a JSON body with a Bearer token in RestTemplate?

Spring Boot doesn't provide a single auto-configured RestTemplate bean; it auto-configures a RestTemplateBuilder you inject and build from, where you can also set connect and read timeouts. This service starts a Sume video job with the key from the server environment.

@Service
public class VideoClient {
  private final RestTemplate rest;

  public VideoClient(RestTemplateBuilder builder) {
    this.rest = builder.connectTimeout(Duration.ofSeconds(5))
        .readTimeout(Duration.ofSeconds(30)).build();
  }

  public String startVideo(String idempotencyKey) {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    headers.setBearerAuth(System.getenv("SUME_API_KEY"));
    headers.set("Idempotency-Key", idempotencyKey);
    Map<String, Object> body = Map.of("model", "sume/auto",
        "prompt", "A vertical product clip on a desk",
        "aspect_ratio", "9:16", "duration", 5);
    try {
      ResponseEntity<String> res = rest.postForEntity(
          "https://api.sume.com/v1/videos", new HttpEntity<>(body, headers), String.class);
      return res.getBody(); // 202: id, polling_url, status "pending"
    } catch (HttpStatusCodeException e) { // 4xx and 5xx
      throw new IllegalStateException(e.getStatusCode() + " " + e.getResponseBodyAsString(), e);
    }
  }
}

Why does postForEntity throw on a 400 or 401?

By default RestTemplate uses DefaultResponseErrorHandler, which treats any 4xx or 5xx status as an error and throws. Catch HttpStatusCodeException, the base class of both, or the specific one you care about. getResponseBodyAsString() returns the body, which for a JSON API holds the reason.

Sume's errors share one envelope, {"error": {"code", "message", "request_id", "details"}}, and the request id is safe to share with Sume support.

Spring behavior from DefaultResponseErrorHandler; Sume codes from Errors and rate limits and Authentication, read 2026-09-29.
StatusSpring throwsSume code and meaning
400HttpClientErrorException.BadRequestinvalid_request: body, query, path or headers invalid
401HttpClientErrorException.Unauthorizedunauthorized: key missing or invalid, or both Authorization and x-api-key sent
402HttpClientErrorExceptioninsufficient_credits: the balance can't cover the generation
415HttpClientErrorException.UnsupportedMediaTypeunsupported_media_type: the body wasn't application/json
429HttpClientErrorException.TooManyRequestsrate_limited or queue_full: back off, honor retry-after
503HttpServerErrorException.ServiceUnavailableprovider_not_configured or provider_capacity_exceeded: a runtime dependency is unavailable or at capacity

Is RestTemplate deprecated?

Yes, as of Spring Framework 7.0: the REST Clients reference says RestTemplate is deprecated in favor of RestClient and will be removed in a future version. Spring Boot's docs still cover RestTemplate for existing code you don't want to migrate. The migration guidance is to create a RestClient from an existing template with RestClient.create(restTemplate) and replace RestTemplate usage component by component. In RestClient the same call is restClient.post().uri(url).contentType(APPLICATION_JSON).body(body).retrieve(), and it also throws on 4xx and 5xx unless you override that with onStatus.

Why does the POST return before the video is ready?

Video generation is asynchronous. POST /v1/videos answers 202 Accepted with id, polling_url and status: "pending", and you poll GET /v1/videos/{id} with backoff until it's completed. Keep the read timeout short, since a client-side timeout doesn't cancel the job: it keeps running and still bills.

The Idempotency-Key header is what makes a retry safe. On /v1/videos, a replay with the key returns the original job, and Sume's docs say not to retry unsafe submit requests without one. Derive the key from the order or record, not a random UUID per attempt. Spring Retry for paid API calls covers the retry side, and Spring Boot webhook HMAC verification covers receiving the result.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume