Remove Image Backgrounds in Java and Spring Boot with an API (HttpClient, RestClient)
Java has no dependable in-process background remover; the ONNX route works but drags a runtime and a model into your service. An HTTP call to a background-removal API is one class. Below: the standard-library HttpClient using a JSON body with base64 (which sidesteps hand-rolled multipart), Spring Boot's RestClient with proper multipart, error handling that maps the API's stable error codes, and an @Async service for catalogue jobs. The request format is remove.bg-compatible, which matters because remove.bg's API ends on 1 December 2026.
In this guide
java.net.http with a JSON body
The API accepts image_file_b64 in a JSON body, so Java 11's HttpClient needs no multipart code at all. Ask for JSON back and you get a hosted result URL; ask for the default and you get the PNG bytes.
import java.net.URI;
import java.net.http.*;
import java.nio.file.*;
import java.util.Base64;
public class BgClear {
private static final String API = "https://www.bgclear.ai/api/v1/remove";
private static final String KEY = System.getenv("BGCLEAR_API_KEY");
private static final HttpClient http = HttpClient.newHttpClient();
public static byte[] removeBackground(Path in, String size) throws Exception {
String b64 = Base64.getEncoder().encodeToString(Files.readAllBytes(in));
String body = "{\"image_file_b64\":\"" + b64 + "\",\"size\":\"" + size + "\",\"format\":\"png\"}";
HttpRequest req = HttpRequest.newBuilder(URI.create(API))
.header("Authorization", "Bearer " + KEY)
.header("Content-Type", "application/json")
.timeout(java.time.Duration.ofSeconds(60))
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<byte[]> res = http.send(req, HttpResponse.BodyHandlers.ofByteArray());
if (res.statusCode() != 200) {
throw new RuntimeException("bgclear " + res.statusCode() + ": " + new String(res.body()));
}
System.out.println("credits remaining: " + res.headers().firstValue("X-Credits-Remaining").orElse("?"));
return res.body();
}
public static void main(String[] a) throws Exception {
Files.write(Path.of("photo-no-bg.png"), removeBackground(Path.of("photo.jpg"), "auto"));
}
}size is preview (free, ≤800 px), full (one credit, ≤4 MP synchronously) or auto. Base64 adds a third to the payload; the 25 MB limit applies to the decoded image, so stay under about 18 MB of file.
Spring Boot: RestClient with real multipart
Spring 6.1's RestClient plus MultipartBodyBuilder gives you the multipart form without base64 overhead, and reads like the curl example in the docs.
@Service
public class BackgroundRemovalService {
private final RestClient client;
public BackgroundRemovalService(@Value("${bgclear.api-key}") String apiKey) {
this.client = RestClient.builder()
.baseUrl("https://www.bgclear.ai/api/v1")
.defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + apiKey)
.build();
}
public byte[] cutout(Resource image, String idempotencyKey) {
MultipartBodyBuilder form = new MultipartBodyBuilder();
form.part("image_file", image);
form.part("size", "auto");
form.part("format", "webp");
return client.post().uri("/remove")
.header("Idempotency-Key", idempotencyKey)
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(form.build())
.retrieve()
.onStatus(HttpStatusCode::isError, (req, res) -> {
String msg = new String(res.getBody().readAllBytes());
if (res.getStatusCode().value() == 402) throw new OutOfCreditsException(msg);
if (res.getStatusCode().value() == 429) throw new RateLimitedException(msg);
throw new BgClearException(res.getStatusCode().value(), msg);
})
.body(byte[].class);
}
}Put bgclear.api-key=${BGCLEAR_API_KEY} in application.properties. In a controller, a MultipartFile becomes a Resource via file.getResource(); use the upload's SHA-256 as the idempotency key so a double-submit never charges twice.
Retries, rate limits and async processing
The API allows 60 requests per minute per key and answers 429 with an X-RateLimit-Reset header in seconds; 5xx and 503 gpu_unavailable (with Retry-After) are worth retrying, 4xx are not. Spring Retry expresses that in an annotation, and @Async keeps catalogue work off request threads.
@Retryable(retryFor = { RateLimitedException.class, BgClearServerException.class },
maxAttempts = 4, backoff = @Backoff(delay = 5000, multiplier = 2))
@Async
public CompletableFuture<Path> processProductImage(Path source, long productId) {
byte[] bytes = service.cutout(new FileSystemResource(source), "product-" + productId);
Path out = Path.of("cutouts", productId + ".webp");
Files.write(out, bytes);
return CompletableFuture.completedFuture(out);
}For images above 4 megapixels, post to /api/v1/jobs (same body), keep the returned job_id, and either poll GET /api/v1/jobs/{id} or expose a callback endpoint and send its URL in the X-Callback-Url header — the bulk guide shows both.
Cost and testing
Preview-size results are free and unlimited — use size=preview in tests and CI. Full-resolution results are one credit each: $9 for 100, $39 for 500, $129 for 2,000; credits never expire and failed requests are never charged (pricing). If you are replacing a remove.bg integration, the field names above are the same ones; only the URL and the auth header differ — see the migration page.
Frequently asked questions
Is there a Java SDK?
No; the API is plain HTTP and both snippets above are complete. If you want a typed client, generate one from the OpenAPI document at https://www.bgclear.ai/api/v1/openapi.json with openapi-generator.
Multipart or base64 JSON — which should I use?
Multipart (RestClient example) for large files and lowest overhead; base64 JSON when you want zero dependencies or already have the bytes in memory. Both return the same result.
Can I run this on Android?
Call it from your backend, not the app — a key shipped in an APK is public. Expose a small endpoint on your server that forwards to the API.
How much does a full-resolution image cost?
One credit, from $0.065 to $0.09 depending on pack size; previews are free.