About Runway ML
Runway AI, Inc. was founded in 2018 in New York, USA. They build generative AI tools for creative professionals — video, image, and audio generation. Their flagship product is the Gen-series video generation model, exposed via a REST API at api.dev.runwayml.com/v1.
The API is production-ready and used by studios, startups, and indie developers globally. It authenticates via a Bearer token in the Authorization header and requires the X-Runway-Version header (e.g. 2024-11-06) on every request.
Available models
Runway provides three current model variants under the seedance2 family. The older gen3a_turbo is deprecated — it returns 403 errors on /text_to_video and 400 errors on /image_to_video. Do not use it in new projects.
| Model | Speed | Quality | Cost | Best for |
|---|---|---|---|---|
seedance2 |
Slow | Highest | Most | Ad production, portfolio, final renders |
seedance2_fast |
Balanced | High | Medium | Most applications — recommended default |
seedance2_mini |
Fastest | Good | Lowest | Previews, prototyping, high-volume workflows |
All three models support:
- Text to video and image to video
- Multi-image input (up to 9 images)
- Optional audio generation
- Duration: 5–15 seconds
- Aspect ratios: 21:9, 16:9, 4:3, 1:1, 3:4, 9:16
- Output resolutions: 480p, 720p, 1080p
Aspect ratios and pixel dimensions
Runway does not accept generic ratio strings like 16:9 directly. You must pass exact pixel dimension strings (e.g. 1280:720). The resolution determines which dimensions are valid.
| Ratio | 480p | 720p | 1080p |
|---|---|---|---|
| 21:9 | 992:432 | 1470:630 | 2206:946 |
| 16:9 | 864:496 | 1280:720 | 1920:1080 |
| 4:3 | 752:560 | 1112:834 | 1664:1248 |
| 1:1 | 640:640 | 960:960 | 1440:1440 |
| 3:4 | 560:752 | 834:1112 | 1248:1664 |
| 9:16 | 496:864 | 720:1280 | 1080:1920 |
In your code, implement a mapping function that takes (aspectRatio, outputResolution) and returns the correct pixel string. Passing an invalid or unrecognized value results in a 400 error from the API.
Pricing and credits
Runway charges in credits. You purchase credit packs, and each generation consumes credits based on model, duration, resolution, and whether audio is enabled.
| Plan | Monthly credits | Price | Best for |
|---|---|---|---|
| Starter | ~125 credits | ~$15/mo | Hobby / testing |
| Standard | ~625 credits | ~$35/mo | Small apps |
| Pro | ~2,250 credits | ~$95/mo | Production apps |
| Unlimited | Pooled team | ~$195/mo | Teams / studios |
Always verify current pricing at runwayml.com/pricing before estimating production costs. Runway adjusts rates as models evolve.
Approximate credit cost per video
| Generation | Approx. credits |
|---|---|
| seedance2_mini — 5 sec, 720p, no audio | ~5 credits |
| seedance2_fast — 5 sec, 720p, no audio | ~10 credits |
| seedance2_fast — 10 sec, 720p, with audio | ~25 credits |
| seedance2 — 15 sec, 1080p, with audio | ~75 credits |
| 1 credit | ~$0.04–$0.07 USD |
Audio generation adds roughly 1 credit per second of video. A 10-second video with audio costs approximately 10 additional credits on top of the base generation cost. If cost is a concern, offer users an audio toggle and default it to false.
How the API works
Runway's video generation API is asynchronous. You do not receive a video file immediately. Instead:
- POST to
/text_to_videoor/image_to_videowith your parameters - Receive a
task_idin the response - Poll
GET /tasks/{task_id}repeatedly until status changes - When status is
SUCCEEDED, download the video from the output URL - Re-host the video in your own storage — Runway's output URLs are temporary
Task status values
| Status | Meaning | What to do |
|---|---|---|
PENDING |
Queued, not yet started | Keep polling |
RUNNING |
Generation in progress | Track progress percentage, keep polling |
SUCCEEDED |
Video is ready | Download from output URL |
FAILED |
Generation failed | Read error message, surface to user |
Key request fields
| Field | Type | Required | Notes |
|---|---|---|---|
model |
string | Yes | seedance2 / seedance2_fast / seedance2_mini |
promptText |
string | Yes | Describe the video content |
promptImage |
string or array | Image-to-video only | Single = base64 string. Multiple = array of {"uri":"data:..."} objects |
ratio |
string | Yes | Pixel dimensions, e.g. 1280:720 |
duration |
int | Yes | 5–15 seconds |
audio |
bool | No | Default false. Adds background audio; increases cost |
Important: For image-to-video with a single image, pass promptImage as a plain base64 data URI string. For multiple images (up to 9), pass an array of objects: [{"uri":"data:image/jpeg;base64,..."}]. Passing plain strings inside an array returns a 400 error.
Integration architecture in .NET
In an ASP.NET Core application, use a layered approach:
1. Service layer — IRunwayMLService
Encapsulate all Runway HTTP calls behind an interface. Register it in DI. Methods needed:
CreateTaskAsync(request, endpoint, cancellationToken)— POST to Runway, returns task IDGetTaskStatusAsync(taskId, cancellationToken)— GET task status and progressDownloadVideoAsync(videoUrl, cancellationToken)— download completed video bytes
2. Controller layer — VideoGenerationController
Accept user input via [FromForm], validate parameters, call the service, and stream progress back to the client using Server-Sent Events (SSE).
3. Storage layer — Cloudflare R2 or S3
Once generation completes, download the video from Runway's temporary URL and store it in your own object storage. Never return Runway's output URL directly to clients — it expires.
4. Database layer — EF Core
Persist the task ID, status, and final video URL. Poll and update the record as the task progresses. Index on RunwayTaskId (unique) and UserId for fast lookups.
SSE polling strategy
Video generation takes 30 seconds to several minutes. A simple HTTP response will time out. Use Server-Sent Events to stream live progress to the client without holding a blocking connection.
Adaptive polling is the key to avoiding the most common failure — socket timeout when progress stalls at ~90–95%:
| Setting | Value | Why |
|---|---|---|
| Normal poll interval | 2,000 ms | Avoids rate limits during slow early progress |
| Fast poll interval | 800 ms | Reduces perceived lag near completion |
| Fast poll threshold | ≥ 80% progress | Progress near the end stalls if polled slowly |
| Max poll attempts | 300 | 10-minute safety ceiling |
| HttpClient timeout | 10 minutes | Must exceed max poll duration |
Only write to the database when status or progress actually changes — avoid unnecessary writes on every poll iteration.
Environment variables you need
Never hardcode secrets. Store all credentials as environment variables and read them at runtime.
| Variable | Where to get it |
|---|---|
RUNWAY_API_KEY |
Runway dashboard → API Keys → Create new key (needs video generation permissions) |
R2_ACCESS_KEY_ID |
Cloudflare dashboard → R2 → API Tokens → Create token with Object Read/Write |
R2_SECRET_ACCESS_KEY |
Generated alongside the access key — copy immediately, shown only once |
R2_BUCKET_NAME |
Cloudflare R2 → Buckets → your bucket name |
R2_ACCOUNT_ID |
Cloudflare dashboard → right sidebar → Account ID |
ConnectionStrings__DefaultConnection |
Your SQL Server / Azure SQL connection string |
In development, use appsettings.Development.json or dotnet user-secrets. In production (Azure App Service, etc.), set these as Application Settings environment variables. Never commit secrets to source control.
Common errors and how to fix them
| Error | Cause | Fix |
|---|---|---|
| 400 — promptImage undefined | Calling /image_to_video without sending promptImage |
Always include at least one base64 image for image-to-video |
| 400 — promptImage array items must be objects | Passing ["data:image/..."] strings instead of objects |
Use [{"uri":"data:image/..."}] format |
| 400 — ratio not recognized | Passing a human-readable ratio like 16:9 |
Use exact pixel strings from the dimension table (e.g. 1280:720) |
| 400 — resolution unrecognized key | Sending a resolution field in the request body |
Remove it — resolution is encoded in the ratio pixel dimensions |
| 403 — model not available | Using deprecated gen3a_turbo |
Switch to the seedance2 model family |
| SSE socket timeout at ~95% | HttpClient timeout too short; polling too slow near completion | Raise HttpClient timeout to 10 min; use adaptive fast polling at ≥80% |
Implementation checklist
- Register
HttpClientwith a 10-minute timeout inProgram.csfor the Runway service - Read the API key from environment variables — never from committed config files
- Validate duration is between 5 and 15 seconds — Runway rejects values outside this range
- Map aspect ratio + resolution to the correct pixel dimension string before calling the API
- Implement adaptive SSE polling — 2s normally, 0.8s when progress ≥ 80%
- Download and re-host the video in your own R2/S3 bucket — Runway output URLs expire
- Persist task state in your database — only write when status or progress actually changes
- Handle multi-image input correctly — 1 image = string, 2–9 images = array of
{"uri":"..."}objects - Offer an audio toggle and clearly communicate the added credit cost to users
- Index your database on
RunwayTaskId(unique) andUserIdfor efficient polling queries
Which model should you use?
For most applications, start with seedance2_fast. It strikes the right balance between speed, quality, and cost. Upgrade to seedance2 when users need maximum quality (ad production, portfolio exports). Use seedance2_mini for draft previews or high-volume non-critical workflows.
Recommended default configuration:
seedance2_fast · 720p · 5 seconds · audio: false
This gives a solid user experience at roughly 10 credits per video (~$0.50–$0.70 USD), and users can opt into longer duration, higher resolution, or audio when they need it.
Runway ML's API evolves quickly. Always verify model names, pricing, and endpoint paths against the official documentation at docs.dev.runwayml.com before shipping to production.
0 Comments
Leave a Comment