Runway API Integration using Java
- OrgLance Technologies LLP
- Aug 12, 2025
- 10 min read
Introduction
Runway ML is a powerful AI platform that provides state-of-the-art machine learning models for creative applications, including image generation, video synthesis, and content manipulation. This article demonstrates how to integrate Runway's API into Java applications, enabling developers to harness AI-powered creative tools programmatically.
Prerequisites
Before diving into the integration, ensure you have:
Java Development Kit (JDK) 8 or higher
Maven or Gradle for dependency management
A Runway ML account with API access
Your Runway API key
Setting Up the Project
Maven Dependencies
Add the following dependencies to your pom.xml:
xml
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.2.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.15.2</version>
</dependency>
</dependencies>Gradle Dependencies
For Gradle users, add to your build.gradle:
gradle
dependencies {
implementation 'org.apache.httpcomponents.client5:httpclient5:5.2.1'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2'
implementation 'com.fasterxml.jackson.core:jackson-core:2.15.2'
}Creating the Runway API Client
Basic Configuration Class
java
public class RunwayConfig {
public static final String BASE_URL = "https://api.runwayml.com/v1";
public static final String API_KEY = System.getenv("RUNWAY_API_KEY");
// HTTP headers
public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String CONTENT_TYPE_HEADER = "Content-Type";
public static final String CONTENT_TYPE_JSON = "application/json";
}HTTP Client Wrapper
java
import org.apache.hc.client5.http.classic.methods.*;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
public class RunwayHttpClient {
private final CloseableHttpClient httpClient;
private final ObjectMapper objectMapper;
public RunwayHttpClient() {
this.httpClient = HttpClients.createDefault();
this.objectMapper = new ObjectMapper();
}
public String executeGet(String endpoint) throws Exception {
HttpGet request = new HttpGet(RunwayConfig.BASE_URL + endpoint);
setHeaders(request);
return httpClient.execute(request, response -> {
return EntityUtils.toString(response.getEntity());
});
}
public String executePost(String endpoint, Object payload) throws Exception {
HttpPost request = new HttpPost(RunwayConfig.BASE_URL + endpoint);
setHeaders(request);
String jsonPayload = objectMapper.writeValueAsString(payload);
request.setEntity(new StringEntity(jsonPayload));
return httpClient.execute(request, response -> {
return EntityUtils.toString(response.getEntity());
});
}
private void setHeaders(HttpUriRequest request) {
request.setHeader(RunwayConfig.AUTHORIZATION_HEADER, "Bearer " + RunwayConfig.API_KEY);
request.setHeader(RunwayConfig.CONTENT_TYPE_HEADER, RunwayConfig.CONTENT_TYPE_JSON);
}
public void close() throws Exception {
httpClient.close();
}
}Data Models
Task Request Model
java
import com.fasterxml.jackson.annotation.JsonProperty;
public class TaskRequest {
@JsonProperty("model")
private String model;
@JsonProperty("input")
private Object input;
@JsonProperty("options")
private TaskOptions options;
// Constructors
public TaskRequest() {}
public TaskRequest(String model, Object input, TaskOptions options) {
this.model = model;
this.input = input;
this.options = options;
}
// Getters and setters
public String getModel() { return model; }
public void setModel(String model) { this.model = model; }
public Object getInput() { return input; }
public void setInput(Object input) { this.input = input; }
public TaskOptions getOptions() { return options; }
public void setOptions(TaskOptions options) { this.options = options; }
}Task Response Model
java
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
public class TaskResponse {
@JsonProperty("id")
private String id;
@JsonProperty("status")
private String status;
@JsonProperty("output")
private List<String> output;
@JsonProperty("progress")
private Double progress;
@JsonProperty("eta")
private Integer eta;
// Constructors
public TaskResponse() {}
// Getters and setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public List<String> getOutput() { return output; }
public void setOutput(List<String> output) { this.output = output; }
public Double getProgress() { return progress; }
public void setProgress(Double progress) { this.progress = progress; }
public Integer getEta() { return eta; }
public void setEta(Integer eta) { this.eta = eta; }
}Task Options Model
java
import com.fasterxml.jackson.annotation.JsonProperty;
public class TaskOptions {
@JsonProperty("seed")
private Integer seed;
@JsonProperty("image_ratio")
private String imageRatio;
@JsonProperty("output_format")
private String outputFormat;
public TaskOptions() {}
public TaskOptions(Integer seed, String imageRatio, String outputFormat) {
this.seed = seed;
this.imageRatio = imageRatio;
this.outputFormat = outputFormat;
}
// Getters and setters
public Integer getSeed() { return seed; }
public void setSeed(Integer seed) { this.seed = seed; }
public String getImageRatio() { return imageRatio; }
public void setImageRatio(String imageRatio) { this.imageRatio = imageRatio; }
public String getOutputFormat() { return outputFormat; }
public void setOutputFormat(String outputFormat) { this.outputFormat = outputFormat; }
}Main API Service Class
java
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;
import java.util.HashMap;
public class RunwayAPIService {
private final RunwayHttpClient httpClient;
private final ObjectMapper objectMapper;
public RunwayAPIService() {
this.httpClient = new RunwayHttpClient();
this.objectMapper = new ObjectMapper();
}
/**
* Create a new image generation task
*/
public TaskResponse createImageGenerationTask(String prompt) throws Exception {
Map<String, Object> input = new HashMap<>();
input.put("text_prompt", prompt);
TaskOptions options = new TaskOptions(null, "16:9", "WEBP");
TaskRequest request = new TaskRequest("gen3a_turbo", input, options);
String response = httpClient.executePost("/tasks", request);
return objectMapper.readValue(response, TaskResponse.class);
}
/**
* Get task status and results
*/
public TaskResponse getTaskStatus(String taskId) throws Exception {
String response = httpClient.executeGet("/tasks/" + taskId);
return objectMapper.readValue(response, TaskResponse.class);
}
/**
* Create a video generation task
*/
public TaskResponse createVideoGenerationTask(String prompt, String imageUrl) throws Exception {
Map<String, Object> input = new HashMap<>();
input.put("text_prompt", prompt);
if (imageUrl != null) {
input.put("image_prompt", imageUrl);
}
TaskOptions options = new TaskOptions(42, "16:9", "MP4");
TaskRequest request = new TaskRequest("gen3a_turbo", input, options);
String response = httpClient.executePost("/tasks", request);
return objectMapper.readValue(response, TaskResponse.class);
}
/**
* Wait for task completion with polling
*/
public TaskResponse waitForTaskCompletion(String taskId, int maxWaitSeconds) throws Exception {
int pollInterval = 2; // seconds
int totalWaited = 0;
while (totalWaited < maxWaitSeconds) {
TaskResponse status = getTaskStatus(taskId);
if ("SUCCEEDED".equals(status.getStatus())) {
return status;
} else if ("FAILED".equals(status.getStatus())) {
throw new RuntimeException("Task failed: " + taskId);
}
Thread.sleep(pollInterval * 1000);
totalWaited += pollInterval;
}
throw new RuntimeException("Task timeout: " + taskId);
}
public void close() throws Exception {
httpClient.close();
}
}Usage Examples
Basic Image Generation
java
public class RunwayExample {
public static void main(String[] args) {
RunwayAPIService service = new RunwayAPIService();
try {
// Create image generation task
String prompt = "A serene mountain landscape at sunset with reflections in a crystal-clear lake";
TaskResponse task = service.createImageGenerationTask(prompt);
System.out.println("Task created: " + task.getId());
System.out.println("Status: " + task.getStatus());
// Wait for completion
TaskResponse completedTask = service.waitForTaskCompletion(task.getId(), 120);
System.out.println("Task completed!");
if (completedTask.getOutput() != null && !completedTask.getOutput().isEmpty()) {
System.out.println("Generated image URL: " + completedTask.getOutput().get(0));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
service.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}Video Generation with Image Input
java
public class VideoGenerationExample {
public static void main(String[] args) {
RunwayAPIService service = new RunwayAPIService();
try {
String prompt = "Camera slowly zooms out to reveal the entire landscape, birds flying across the sky";
String inputImageUrl = "https://example.com/your-image.jpg";
// Create video generation task
TaskResponse task = service.createVideoGenerationTask(prompt, inputImageUrl);
System.out.println("Video generation task created: " + task.getId());
// Poll for completion
TaskResponse completedTask = service.waitForTaskCompletion(task.getId(), 300);
if (completedTask.getOutput() != null && !completedTask.getOutput().isEmpty()) {
System.out.println("Generated video URL: " + completedTask.getOutput().get(0));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
service.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}Advanced Features
Batch Processing
java
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class BatchProcessor {
private final RunwayAPIService service;
private final ExecutorService executor;
public BatchProcessor() {
this.service = new RunwayAPIService();
this.executor = Executors.newFixedThreadPool(5);
}
public List<TaskResponse> processBatch(List<String> prompts) throws Exception {
List<CompletableFuture<TaskResponse>> futures = new ArrayList<>();
for (String prompt : prompts) {
CompletableFuture<TaskResponse> future = CompletableFuture.supplyAsync(() -> {
try {
TaskResponse task = service.createImageGenerationTask(prompt);
return service.waitForTaskCompletion(task.getId(), 120);
} catch (Exception e) {
throw new RuntimeException(e);
}
}, executor);
futures.add(future);
}
List<TaskResponse> results = new ArrayList<>();
for (CompletableFuture<TaskResponse> future : futures) {
results.add(future.get());
}
return results;
}
public void shutdown() throws Exception {
executor.shutdown();
service.close();
}
}Custom Error Handling
java
public class RunwayException extends Exception {
private final int statusCode;
private final String errorType;
public RunwayException(String message, int statusCode, String errorType) {
super(message);
this.statusCode = statusCode;
this.errorType = errorType;
}
public int getStatusCode() { return statusCode; }
public String getErrorType() { return errorType; }
}
// Enhanced HTTP client with better error handling
public String executePostWithErrorHandling(String endpoint, Object payload) throws RunwayException {
try {
HttpPost request = new HttpPost(RunwayConfig.BASE_URL + endpoint);
setHeaders(request);
String jsonPayload = objectMapper.writeValueAsString(payload);
request.setEntity(new StringEntity(jsonPayload));
return httpClient.execute(request, response -> {
int statusCode = response.getCode();
String responseBody = EntityUtils.toString(response.getEntity());
if (statusCode >= 400) {
throw new RunwayException(
"API request failed: " + responseBody,
statusCode,
"HTTP_ERROR"
);
}
return responseBody;
});
} catch (Exception e) {
if (e instanceof RunwayException) {
throw (RunwayException) e;
}
throw new RunwayException("Request execution failed: " + e.getMessage(), 0, "EXECUTION_ERROR");
}
}Best Practices
1. API Key Management
Always store your API key securely using environment variables or a secure configuration management system:
java
// Load from environment variable
String apiKey = System.getenv("RUNWAY_API_KEY");
// Or use a properties file
Properties props = new Properties();
props.load(new FileInputStream("config.properties"));
String apiKey = props.getProperty("runway.api.key");2. Rate Limiting and Retry Logic
java
import java.util.concurrent.TimeUnit;
public class RetryableRunwayClient {
private static final int MAX_RETRIES = 3;
private static final int RETRY_DELAY_SECONDS = 5;
public TaskResponse createTaskWithRetry(TaskRequest request) throws Exception {
Exception lastException = null;
for (int i = 0; i < MAX_RETRIES; i++) {
try {
String response = httpClient.executePost("/tasks", request);
return objectMapper.readValue(response, TaskResponse.class);
} catch (Exception e) {
lastException = e;
if (i < MAX_RETRIES - 1) {
TimeUnit.SECONDS.sleep(RETRY_DELAY_SECONDS);
}
}
}
throw new RuntimeException("Failed after " + MAX_RETRIES + " retries", lastException);
}
}3. Resource Management
java
public class RunwayAPIManager implements AutoCloseable {
private final RunwayAPIService service;
public RunwayAPIManager() {
this.service = new RunwayAPIService();
}
// Your API methods here...
@Override
public void close() throws Exception {
service.close();
}
}
// Usage with try-with-resources
try (RunwayAPIManager manager = new RunwayAPIManager()) {
TaskResponse result = manager.createImageGenerationTask("Your prompt here");
// Handle result
}Testing
Unit Test Example
java
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import static org.junit.jupiter.api.Assertions.*;
public class RunwayAPIServiceTest {
private RunwayAPIService service;
@BeforeEach
void setUp() {
service = new RunwayAPIService();
}
@Test
void testCreateImageGenerationTask() throws Exception {
// Mock or use a test API key
String prompt = "Test image generation";
TaskResponse response = service.createImageGenerationTask(prompt);
assertNotNull(response);
assertNotNull(response.getId());
assertEquals("PENDING", response.getStatus());
}
}Conclusion
This comprehensive guide demonstrates how to integrate Runway ML's API into Java applications effectively. The implementation provides a solid foundation for building AI-powered creative applications with proper error handling, resource management, and scalability considerations.
Key takeaways:
Use proper HTTP client configuration for reliable API communication
Implement robust error handling and retry mechanisms
Follow Java best practices for resource management
Consider batch processing for multiple requests
Secure API key management is crucial
The modular design allows for easy extension and customization based on specific application requirements. Whether you're building a content creation platform, automating creative workflows, or exploring AI-generated media, this Java integration provides the tools needed to harness Runway ML's powerful capabilities.





Comments