top of page
Search

Runway API Integration using Java

  • Writer: OrgLance Technologies LLP
    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<StringObject> 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<StringObject> 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.

 
 
 

Recent Posts

See All

Comments


Services

Explore our software solutions tailored to your needs. Our team of experts at OrgLance Technologies offers top-notch services at a competitive rate of $30 per hour. Let us help you bring your ideas to life.

bottom of page