GuidesAI APIS
Runway API from browser JavaScript
Browsers cannot call api.dev.runwayml.com: on 28 October 2026 it answered the CORS preflight with 401 and no Access-Control-Allow-Origin header, so the POST is never sent. The free cors.dev proxy does not help, because it allows only GET and HEAD, refuses Authorization with 400 and drops X-Runway-Version; cors.dev Pro ($5 a month for 500,000 requests, 7-day trial with 1,000 requests, no card) forwards all three from origins you register. A gen4_image task costs 5 credits ($0.05) at 720p, and you poll GET /v1/tasks/{id} no more than every 5 seconds.
Why the browser cannot reach api.dev.runwayml.com
Probes sent on 28 October 2026 from Origin: https://example.com:
- The
OPTIONSpreflight forPOST https://api.dev.runwayml.com/v1/text_to_imageanswered401No API key was provided, with noAccess-Control-Allow-Originheader. - A direct
POSTto/v1/text_to_imagewith a test key returned401and no CORS headers. - A
GETto/v1/tasks/{id}also answered401with no CORS headers.
A call to Runway sets Authorization, Content-Type and X-Runway-Version, so the browser sends an OPTIONS preflight first, and a preflight never carries credentials. Runway rejects it with 401 and no Access-Control-Allow-Origin, so the browser stops there and your POST is never sent. DevTools reports a failed preflight, and fetch rejects with TypeError: Failed to fetch, with no status or body to read.
What the free proxy does with Runway
The free tier at https://proxy.cors.dev/ needs no signup or key and allows about 600 requests a minute per IP, but only GET and HEAD. A POST to create a task answers 405 method_not_allowed, and an Authorization header answers 400 header_not_allowed.
The free tier also drops unsupported custom headers. A task GET through it returned Runway's 401 with CORS headers and x-cors-dropped-headers: x-runway-version, while Runway requires both Authorization: Bearer <key> and X-Runway-Version: 2024-11-06 on every call.
Create a text_to_image task through cors.dev Pro
- Start a cors.dev trial or Pro
sign in with Google at cors.dev. The 7-day trial needs no card and includes 1,000 requests at up to 600 a minute and 10 in flight; cors.dev Pro is $5 a month for 500,000 requests, with 3,000 requests a minute per account and 100 in flight.
- Create a Connection
list the exact page origins your app runs on and add
api.dev.runwayml.comas an API host. Its key, sent asX-Cors-Key, is a publishable identifier that only works from those origins. - Send the proxied request
prefix the Runway URL with
https://proxy.cors.dev/and sendX-Cors-Key,Authorization: Bearer <key>,Content-TypeandX-Runway-Version: 2024-11-06. The preflight tohttps://proxy.cors.dev/https://api.dev.runwayml.com/v1/text_to_imageanswered204and allows all four headers; a wrongX-Cors-Keyanswers401invalid_key.
const PROXY = 'https://proxy.cors.dev/';
const CORS_KEY = 'YOUR_CONNECTION_KEY'; // publishable, works only from your registered origins
async function runway(path, apiKey, init = {}) {
const res = await fetch(PROXY + 'https://api.dev.runwayml.com/v1/' + path, {
...init,
headers: {
'X-Cors-Key': CORS_KEY,
Authorization: `Bearer ${apiKey}`,
'X-Runway-Version': '2024-11-06',
'Content-Type': 'application/json',
},
});
const body = await res.json();
if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.headers.get('X-Cors-Error') ?? body.error}`);
return body;
}
async function textToImage(promptText, apiKey) {
const { id } = await runway('text_to_image', apiKey, {
method: 'POST',
body: JSON.stringify({ model: 'gen4_image', ratio: '1280:720', promptText }),
});
for (;;) {
await new Promise((resolve) => setTimeout(resolve, 5000));
const task = await runway(`tasks/${id}`, apiKey);
if (task.status === 'SUCCEEDED') return task.output;
if (task.status === 'FAILED' || task.status === 'CANCELLED') throw new Error(task.failure ?? task.status);
}
}
const userKey = prompt('Your Runway API key');
const [imageUrl] = await textToImage('a red bicycle at dusk', userKey);The POST /v1/text_to_image JSON body takes model (gen4_image), promptText (1 to 1,000 characters), and ratio, one of 16 values: 1024:1024, 1080:1080, 1168:880, 1360:768, 1440:1080, 1080:1440, 1808:768, 1920:1080, 1080:1920, 2112:912, 1280:720, 720:1280, 720:720, 960:720, 720:960 and 1680:720. It also takes an optional seed from 0 to 4,294,967,295 (the same seed with the same request gives similar results) and up to 3 referenceImages, each with a uri and a tag you can mention in the prompt as @tag. gen4_image_turbo requires referenceImages. A successful call returns the task id and estimatedCost.credits.
Proxy errors arrive in the X-Cors-Error header:
invalid_keyororigin_not_allowed: checkX-Cors-Keyand the Connection's origins; scheme, host and port must match exactly.target_not_allowed: enableapi.dev.runwayml.comon the Connection.request_too_large: the body is over 1 MiB.upstream_timeout: Runway took longer than the 30-second deadline.rate_limitedorquota_exceeded: a429; wait forRetry-After, which your code can read.
Poll the task and use the output
Poll GET /v1/tasks/{id} through the proxy no more than once every 5 seconds; Runway says not to expect updates more often than that.
PENDING: waiting to run; poll again in 5 seconds.THROTTLED: you are over your concurrency limit; Runway stored the task and queues it in submission order. Keep polling.RUNNING: generating;progressruns from 0 to 1.SUCCEEDED:outputholds an array of result URLs.FAILED: stop; readfailureand the machine-readablefailureCode.CANCELLED: stop polling.
The URLs in output expire within 24 to 48 hours; fetch the task again for fresh URLs, and download anything you want to keep. Put them straight into <img> or <video> tags, which need no CORS and no proxy.
What a generation costs
Credits cost $0.01 each, bought in the Runway developer portal. Prices from Runway's API pricing page, checked 28 October 2026:
| Model | Output | Credits | Cost |
|---|---|---|---|
| gen4_image at 720p | Image | 5 | $0.05 |
| gen4_image at 1080p | Image | 8 | $0.08 |
| gen4_image_turbo | Image, any resolution | 2 | $0.02 |
| gen4_turbo | Video, per second | 5 | $0.05 |
| gen4.5 | Video, per second | 12 | $0.12 |
| veo3.1_fast without audio | Video, per second | 10 | $0.10 |
| veo3.1 with audio | Video, per second | 40 | $0.40 |
Video is billed per second of output: veo3.1 without audio is 20 credits a second and veo3.1_fast with audio 15. Every create response includes estimatedCost.credits, so log it per task to track spend.
Keys, bodies and time limits
- Keys: Runway keys are
key_followed by 128 hex characters; a shorter test key was rejected with a message saying so. Any key in browser code is visible in DevTools, and cors.dev does not hide it. Keep your own production key on a backend; browser calls fit internal tools, prototypes and bring-your-own-key apps where each user pastes their own key. - Bodies: Runway accepts HTTPS URLs or base64 data URIs up to 5MB in
referenceImages, but cors.dev Pro caps request bodies at 1 MiB. Pass HTTPS URLs through the proxy. - Time: cors.dev ends every upstream request after 30 seconds, on cors.dev Pro too. Creating a task and each poll return quickly, so polling fits; do not hold one request open while a video renders.
- Runway limits: no requests-per-minute limit while you stay within your maximum daily generations. Concurrency, daily generations and monthly spend are capped per model and per project by your usage tier; tasks past the concurrency cap wait as
THROTTLED. On Tier 3, for example, a project can run 5 Gen-4.5 and 5 Veo 3.1 generations at the same time, and a project moves up a tier automatically once it meets the next tier's spend criteria.
Good questions.
Does the Runway API support CORS?
No, api.dev.runwayml.com doesn't return Access-Control-Allow-Origin on its responses. In our 28 October 2026 probes, the browser's OPTIONS preflight to /v1/text_to_image came back 401 No API key was provided without CORS headers because browsers don't send credentials on preflights. Both POST /v1/text_to_image and GET /v1/tasks/{id} also omit CORS headers.
Can I call Runway with the free cors.dev proxy?
No, the free proxy only supports GET and HEAD, so POST requests return 405 method_not_allowed. It also blocks the Authorization header with 400 header_not_allowed and strips X-Runway-Version (listing it in X-Cors-Dropped-Headers), both of which Runway requires on every request. cors.dev Pro ($5 a month for 500,000 requests, 7-day trial with 1,000 requests, no card) forwards all three from origins registered on a Connection, identified by its X-Cors-Key header.
How often should I poll a Runway task?
Runway states that you shouldn't expect task updates more than once every 5 seconds per task. Poll GET /v1/tasks/{id} on a 5-second timer while the status is PENDING, THROTTLED or RUNNING. Stop polling once the status changes to SUCCEEDED, FAILED or CANCELLED.
How long do Runway output URLs last?
The file URLs in the output array expire within 24 to 48 hours. If you need to display the image again after the link expires, call GET /v1/tasks/{id} again to get fresh URLs, or download the file to your own storage if you want to keep it permanently. While the URL is live, you can put it directly into an <img> or <video> tag without routing it through cors.dev.
How much does a Runway image cost through the API?
Runway sells credits in the developer portal at $0.01 per credit. On gen4_image, a 720p image costs 5 credits ($0.05) and a 1080p image costs 8 credits ($0.08). On gen4_image_turbo, which requires referenceImages, every image costs 2 credits ($0.02) at any resolution.