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.

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.
| Status | Spring throws | Sume code and meaning |
|---|---|---|
400 | HttpClientErrorException.BadRequest | invalid_request: body, query, path or headers invalid |
401 | HttpClientErrorException.Unauthorized | unauthorized: key missing or invalid, or both Authorization and x-api-key sent |
402 | HttpClientErrorException | insufficient_credits: the balance can't cover the generation |
415 | HttpClientErrorException.UnsupportedMediaType | unsupported_media_type: the body wasn't application/json |
429 | HttpClientErrorException.TooManyRequests | rate_limited or queue_full: back off, honor retry-after |
503 | HttpServerErrorException.ServiceUnavailable | provider_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
- Authentication
- Video Generation
- Errors and rate limits
- Jobs and results
- Spring Framework Javadoc: RestTemplate (read 2026-09-29)
- Spring Framework Javadoc: HttpHeaders (read 2026-09-29)
- Spring Framework Javadoc: DefaultResponseErrorHandler (read 2026-09-29)
- Spring Framework Javadoc: RestClientResponseException (read 2026-09-29)
- Spring Framework reference: REST Clients (read 2026-09-29)
- Spring Boot reference: Calling REST Services (read 2026-09-29)
Related posts
More in Integrations
- Salesforce Apex HTTP callout: POST JSON to an external API
An Apex HTTP callout builds an HttpRequest, sends it with Http.send and reads the HttpResponse. From a trigger or after DML, it must run asynchronously.
- Stripe checkout webhook: start a paid job once per payment
Listen for checkout.session.completed, verify the signature on the raw body, answer 2xx fast, then start the job keyed by the Checkout Session id.
- Tenacity retry in Python: safe settings for a paid API POST
Bare @retry in tenacity retries forever with no wait. For a paid POST, cap attempts, add jittered backoff, retry only transient errors, and reuse one key.
- Workato HTTP connector: call an API with a key and JSON
Workato's HTTP connector calls any HTTP API: a Header auth connection holds the key, and Send request via HTTP POSTs a JSON body and maps the reply.
Written by Sume