The official Java client for the API2Convert file-conversion API. Convert, compress and transform images, documents, audio, video, ebooks, archives and CAD — and run operations like OCR, merge, thumbnail and website capture — in one line of code.
Api2Convert client = new Api2Convert("YOUR_API_KEY");
client.convert("invoice.docx", "pdf").save("invoice.pdf");That single call creates a job, uploads your file, starts it, waits for it to finish and gives you back a result you can save. No polling loops, no manual upload handling.
- Java 17+
- One runtime dependency: Jackson (
jackson-databind). The HTTP layer uses the JDK's built-injava.net.http.HttpClient— no extra HTTP dependency.
Maven:
<dependency>
<groupId>com.api2convert</groupId>
<artifactId>api2convert-java</artifactId>
<version>10.2.1</version>
</dependency>Gradle:
implementation("com.api2convert:api2convert-java:10.2.1")Get an API key from the API2Convert dashboard / documentation.
import com.api2convert.Api2Convert;
import java.util.Map;
// Reads the API2CONVERT_API_KEY environment variable when no key is passed.
Api2Convert client = new Api2Convert("YOUR_API_KEY");
// 1) From a local file
client.convert("photo.png", "jpg").save("photo.jpg");
// 2) From a URL
client.convert("https://example.com/photo.png", "jpg").save("photo.jpg");
// 3) With conversion options (discover them via client.options("jpg"))
client.convert("photo.png", "jpg", Map.of("quality", 85, "width", 1280, "height", 720))
.save("out/"); // the processed-file directoryconvert(input, to, options) — input is a local path String, a public URL, a Path, a
byte[] or an InputStream; to is the target format; options are the conversion
options for that target. Less-common controls live on ConvertOptions (so they can never collide
with an open-ended API option): category, timeout, outputIndex, filename, downloadPassword.
The returned ConversionResult lets you:
ConversionResult result = client.convert("report.docx", "pdf");
result.save("report.pdf"); // stream to a file
result.save("downloads/"); // ...or a directory (keeps the server filename)
byte[] content = result.contents(); // ...or get the raw bytes
String url = result.url(); // ...or just the download URLPass a downloadPassword and the output is locked behind it. The SDK remembers the password and
sends it automatically when you download — you don't pass it again:
import com.api2convert.ConvertOptions;
ConversionResult result = client.convert("statement.docx", "pdf", null,
new ConvertOptions().downloadPassword("hunter2"));
result.save("statement.pdf"); // the password is applied for youThe download URL still needs the password from anywhere else (a browser, cURL, another process),
via the X-Api2convert-Download-Password header. When you already hold an OutputFile — e.g. from the Jobs
API — hand the password to download():
client.download(output, "hunter2").save("out/");For long-running jobs, start the conversion and get notified via a webhook instead of waiting:
import com.api2convert.AsyncOptions;
Job job = client.convertAsync("movie.mov", "mp4", null,
new AsyncOptions().callback("https://your-app.example.com/webhooks/api2convert"));In your webhook handler, verify and parse the callback:
import com.api2convert.Api2Convert;
import com.api2convert.exception.SignatureVerificationException;
import com.api2convert.webhook.WebhookEvent;
byte[] payload = request.rawBody(); // the RAW body
String signature = request.header("X-Oc-Signature");
try {
WebhookEvent event = Api2Convert.webhooks().constructEvent(payload, signature, "YOUR_WEBHOOK_SECRET");
Job job = event.job();
// ... react to job.status().code() ...
} catch (SignatureVerificationException e) {
// respond 400
}Signed webhooks are being rolled out. Until they are enabled for your account no signature is sent — call
Api2Convert.webhooks().parse(payload)(or pass an empty secret) to deserialize the callback without verifying.
Read an input straight from your own S3, Azure Blob, FTP or Google Cloud Storage, and/or deliver the
converted output back into a bucket. Use the per-provider CloudInput factory for inputs (its
arguments are the provider's flat/lowercase keys, exactly as the API expects), and the generic
OutputTarget for outputs:
import com.api2convert.ConvertOptions;
import com.api2convert.enums.CloudProvider;
import com.api2convert.model.CloudInput;
import com.api2convert.model.OutputTarget;
import java.util.Map;
// Input from S3: fetch the file from your bucket and save the result locally.
CloudInput input = CloudInput.amazonS3("my-bucket", "invoices/invoice.docx",
"AKIA...", "s3-secret-access-key");
client.convert(input, "pdf").save("invoice.pdf");
// Output to S3: convert a local/URL input and deliver the result into a bucket.
OutputTarget target = OutputTarget.of(CloudProvider.AMAZON_S3,
Map.of("bucket", "my-bucket", "file", "results/invoice.pdf"),
Map.of("accesskeyid", "AKIA...", "secretaccesskey", "s3-secret-access-key"));
client.convert("invoice.docx", "pdf", null, new ConvertOptions().outputTargets(target));azure(...), ftp(...) and googleCloud(...) inputs work the same way. When an output target is
set the conversion has no downloadable output — convert() returns the completed job (via
result.job()) without downloading. Cloud credentials are redacted ([REDACTED]) in exceptions
and object inspection, and the SDK never logs them.
Every failure is an unchecked exception extending com.api2convert.exception.Api2ConvertException:
import com.api2convert.exception.*;
try {
client.convert("photo.png", "jpg").save("photo.jpg");
} catch (ValidationException e) {
// bad target / option — e.getMessage() explains
} catch (AuthenticationException e) {
// bad or missing API key
} catch (RateLimitException e) {
// too many requests — retry after e.getRetryAfter() seconds
} catch (ConversionFailedException e) {
// the job failed — inspect e.errors()
}| Exception | When |
|---|---|
AuthenticationException |
401 / 403 — bad or missing key |
PaymentRequiredException |
402 — no remaining quota |
ValidationException |
400 / 422 — invalid request (e.g. unknown target) |
NotFoundException |
404 — resource doesn't exist |
RateLimitException |
429 — exposes getRetryAfter() |
ServerException |
5xx |
ConversionFailedException |
the job reached failed; exposes getJob() and errors() |
TimeoutException |
the job didn't finish within the poll timeout |
SignatureVerificationException |
a webhook payload failed verification |
Transient failures (429, 5xx, network errors) are retried automatically with jittered
exponential backoff. A non-idempotent POST (e.g. creating a job) is never blindly retried, so a
transient error can't create a duplicate job — pass an idempotency key to make it retry-safe:
client.jobs().create(payload, "my-idempotency-key").
convert() is sugar over the Jobs API. Drop down to it for compound jobs, merges, presets, custom
polling or job chaining:
Job job = client.jobs().create(Map.of(
"process", false,
"conversion", List.of(Map.of("target", "pdf", "options", Map.of("pdf_a", true)))));
client.jobs().upload(job, "contract.docx"); // local file
client.jobs().addInput(job.id(), Map.of( // ...or a URL
"type", "remote", "source", "https://example.com/appendix.docx"));
client.jobs().start(job.id());
Job done = client.jobs().await(job.id(), 120); // poll to completion (120s timeout)
for (OutputFile output : done.output()) {
client.download(output).save("out/");
}Available resources: jobs(), conversions() (the catalog + option discovery), presets(),
stats(), contracts().
Discover the valid options for any target:
Map<String, Object> options = client.options("jpg"); // -> { quality: {...}, width: {...}, ... }Note: the poll-to-completion method is
jobs().await(...)(notwait, which is reserved byjava.lang.Object). This is the only public name adapted to Java idiom.
import com.api2convert.http.Config;
Api2Convert client = new Api2Convert("YOUR_API_KEY", Config.builder()
.timeout(30) // per-request network timeout (seconds)
.maxRetries(2) // automatic retries for transient failures
.pollInterval(1.0) // first poll interval when waiting (seconds)
.pollMaxInterval(5.0) // backoff cap (seconds)
.pollTimeout(300) // give up waiting after this many seconds
.build());Bring your own HTTP transport by implementing com.api2convert.http.HttpSender and passing it as
the third constructor argument.
- Never hard-code or commit your API key. Load it from the environment (
API2CONVERT_API_KEY) or a secrets manager. - In CI, store it as a masked & protected variable and never print it to logs.
- Treat the per-job upload token and your webhook signing secret with the same care.
- The SDK never logs your key/token and never puts them in exception messages. Authenticated requests never follow a redirect (a redirect could otherwise forward the key to another host); only the self-contained, no-auth download path follows redirects.
- If a key is ever exposed, revoke and rotate it in the API2Convert dashboard immediately.
See SECURITY.md.
mvn verify # compile + run the offline unit testsLive conformance tests run against the real API when API2CONVERT_API_KEY is set (they auto-skip
otherwise):
API2CONVERT_API_KEY=... mvn verify -DexcludedGroups=The live conformance suite doubles
as an executable, end-to-end tour of the SDK: it runs the same 20 documented examples end to end
(plus two negative scenarios — an invalid target is a typed validation error, and a bad key is a
typed auth error that never leaks the credential). Each test mirrors one runnable file in
examples/ and one guide on api2convert.com.
It runs automatically against the real API on every release tag (see
.github/workflows/live-conformance.yml), so a published
version is always verified end to end.
Each file in examples/ is a self-contained program that reads the key from
API2CONVERT_API_KEY (and honors API2CONVERT_BASE_URL). Build the jar (mvn -B package), then run
one with the SDK + Jackson on the classpath, e.g.:
API2CONVERT_API_KEY=your-key java -cp "target/classes:libs/*" Quickstart| Guide | Example |
|---|---|
| Quick start | Quickstart.java |
| Convert files | ConvertFiles.java |
| Uploading files | UploadingFiles.java |
| The job lifecycle | JobLifecycle.java |
| Add a watermark | AddWatermark.java |
| Create thumbnails | CreateThumbnails.java |
| Compress files | CompressFiles.java |
| Create archives | CreateArchives.java |
| Create hashes | CreateHashes.java |
| Extract assets | ExtractAssets.java |
| File analysis | FileAnalysis.java |
| Compare files | CompareFiles.java |
| Capture a website | CaptureWebsite.java |
| Audio operations | AudioOperations.java |
| Image operations | ImageOperations.java |
| Webhooks | Webhooks.java |
| Presets | Presets.java |
| Statistics | Statistics.java |
| Rate limits & contracts | RateLimits.java |
| Authentication | Authentication.java |
This SDK is hand-written and kept in sync with the API by an AI agent — see AGENTS.md
and docs/SDK_CONTRACT.md. Notable changes are recorded in
docs/CHANGELOG.md.
MIT — see LICENSE.