---
title: "Why is my Wireflow API request failing?"
description: "A Wireflow API request fails for the reason its status code names, such as 401 for the key, 402 for credits or 429 for too many requests. Fix each one here."
updated: 2026-10-20
---

Your Wireflow API request is failing for the reason its status code and `error` message name: a missing or wrong key (`401`), too few credits (`402`), no permission (`403`), an id that doesn't exist (`404`), a malformed request (`400`), too many requests (`429`) or a failure on Wireflow's side (`500`). Find your status below. Each section gives the cause, the fix and a check that the fix worked.

| Status | Meaning |
| --- | --- |
| `400` | Missing required fields, invalid node structure, empty workflow |
| `401` | Missing or invalid API key, expired key |
| `402` | Insufficient credits to run the workflow |
| `403` | No permission to access the resource |
| `404` | Workflow or execution does not exist |
| `429` | Rate limit exceeded |
| `500` | Unexpected server-side failure |

## How do I see the full error the API sent back?

Read three things from the response: the HTTP status, the `error` field in the body and the `X-Request-Id` header. The `error` field comes in two shapes, so handle both: a flat `{ "error": "message" }`, sometimes with extra fields, or an object with `type`, `message` and `code`.

```json
{
  "error": {
    "type": "invalid_request_error",
    "message": "Workflow not found",
    "code": "not_found"
  }
}
```

`curl -i` prints the status line and the headers, `X-Request-Id` included, above the body:

```bash
curl -i https://www.wireflow.ai/api/v1/me \
  -H "Authorization: Bearer $WIREFLOW_API_KEY"
```

```javascript
const res = await fetch('https://www.wireflow.ai/api/v1/me', {
  headers: { Authorization: `Bearer ${process.env.WIREFLOW_API_KEY}` },
});
if (!res.ok) {
  const body = await res.json().catch(() => ({}));
  const message = typeof body.error === 'string' ? body.error : body.error?.message;
  console.error(res.status, message, res.headers.get('X-Request-Id'));
}
```

```python
import os
import requests

res = requests.get(
    "https://www.wireflow.ai/api/v1/me",
    headers={"Authorization": f"Bearer {os.environ['WIREFLOW_API_KEY']}"},
)
if not res.ok:
    try:
        error = res.json().get("error")
    except ValueError:
        error = None
    message = error.get("message") if isinstance(error, dict) else error
    print(res.status_code, message, res.headers.get("X-Request-Id"))
```

If an error persists, contact support with the `X-Request-Id` value from the response.

## Why do I get 401 "Invalid API key" or "Missing Authorization header"?

- **Cause:** Wireflow couldn't match the request to a valid key. The message says which part failed:

| Message | Cause | Fix |
| --- | --- | --- |
| "Missing Authorization header" | The request has no `Authorization` header | Send `Authorization: Bearer YOUR_API_KEY` on every request |
| "Invalid API key format. Expected: Bearer sk-..." | The header isn't `Bearer`, a space, then a key that starts with `sk-` | Send the full key after `Bearer` and a space |
| "Invalid API key" | The value is not a valid key | Create a new key in **Settings > API Keys** and copy all of it. A key is shown only once |
| "API key has expired" | The key had an expiration date, and it has passed | Create a new key, with a later expiration date or none |
| "API key missing required scope: apps:execute" | The key lacks the scope the route needs (this message is listed for running a published app) | Create a key with the scope the route needs: `workflows:execute` or **Full access** to start runs, `workflows:write` to upload files, `apps:execute` to run published apps. A new key starts read-only |

- **Fix:** Follow the row for your message.
- **Check:** `GET /api/v1/me` with the same header returns your account. It works with any valid key and needs no extra scope.

## Why does a run fail with 402 "Insufficient credits"?

- **Cause:** Your credit balance is lower than the run needs. The reply says how many credits the run needs, how many you have, and what each node costs:

```json
{
  "error": "Insufficient credits. Required: 10, Available: 3",
  "insufficientCredits": true,
  "requiredCredits": 10,
  "availableCredits": 3,
  "breakdown": [
    { "nodeId": "node-1", "nodeLabel": "Text to Image", "credits": 10 }
  ]
}
```

- **Fix:** Add credits, or move to a plan with more credits (see the [pricing page](https://www.wireflow.ai/pricing)), or lower the cost of the most expensive node in `breakdown`, for example with a lower resolution or a shorter length. Runs started with an API key are paid by the key owner.
- **Check:** `GET /api/v1/me` shows the key's account and its `credits`. Start the run again once `credits` covers `requiredCredits`.

## Why do I get 403 "Forbidden"?

- **Cause:** The key has no permission for the resource it asked for. "Forbidden: Cannot resume execution you do not own" means the execution belongs to another account.
- **Fix:** Use a key from the account that owns the workflow or the execution. `/run` works on workflows you own.
- **Check:** `GET /api/v1/me` shows which account the key acts as.

## Why do I get 404 "Workflow not found" or "Execution not found"?

- **Cause:** No workflow or execution exists with the id in the request.
- **Fix:** Copy the workflow id from `GET /api/v1/workflows`, which lists your workflows with their ids, and the execution id from the `executionId` in the `202` reply. To poll, put `https://www.wireflow.ai` in front of the `poll` path: the path already starts with `/api/v1`, so joining it to the base URL repeats that part.
- **Check:** `GET /api/v1/workflows/{id}/run` returns the workflow's inputs.

## Why do I get 400 when I save or run a workflow?

- **Cause:** The request body is missing something or has the wrong structure. The message names it:

| Message or code | Cause | Fix |
| --- | --- | --- |
| "Missing required fields: name, nodes, edges" | The workflow in the request lacks a `name`, `nodes` or `edges` | Send all three |
| "At least one node must be provided" | `nodes` is empty | Send at least one node |
| "All nodes must have an id" | A node has no `id` | Give every node an `id` |
| "Node node-1 is missing model.name" | That node has no `model.name` | Add `model.name` to the node the message names |
| "Workflow is not active" | The errors reference lists this message without more detail | Contact support with the `X-Request-Id` |
| `invalid_webhook` | The `webhook` block on a run is invalid. Its `url` must be `https`, with plain `http` only for `localhost`, `127.0.0.1` and `*.localhost`, and at most 2048 characters | Fix the URL and run again. Nothing ran |

- **Fix:** Follow the row for your message.
- **Check:** Send the request again. A valid run request returns `202 Accepted` with an `executionId`.

## Why do I get 429 "Too many workflow executions. Please try again later."?

- **Cause:** You went over a rate limit. The `Retry-After` header says how many seconds to wait, and the `retryAfter` field in the body is a Unix timestamp that matches the `X-RateLimit-Reset` header.
- **Fix:** Wait the number of seconds in `Retry-After`, then retry with backoff. Some routes have their own caps, such as 20 requests per minute per API key for running a published app. The numbers are in [limits for uploads, runs and the API](/docs/limits).
- **Check:** `X-RateLimit-Remaining` on each response shows how many requests are left in the current window.

```json
{
  "error": "Too many workflow executions. Please try again later.",
  "retryAfter": 1711036800
}
```

## Why do I get 500 "An unexpected error occurred"?

- **Cause:** Something failed on Wireflow's side. A run can start even when the call that started it returns `500`.
- **Fix:** Before you retry a call that starts a run, check `GET /api/v1/workflows/{id}/executions` for that run. Retry `/run` with the same `Idempotency-Key` and it replays the original run, while a retry of `/execute`, or of `/run` without the same key, bills a second run. Other calls can be retried after a short delay.
- **Check:** If the error persists, contact support with the `X-Request-Id` from the response.

## How do I retry a failed request without paying for the run twice?

Send an `Idempotency-Key` header with every `/run` request and reuse the same key on each retry: `/run` then replays the original run instead of billing a new one. Wait the `Retry-After` seconds on a `429`, and back off between retries on a `500`.

```bash
curl -X POST https://www.wireflow.ai/api/v1/workflows/YOUR_WORKFLOW_ID/run \
  -H "Authorization: Bearer $WIREFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1042" \
  -d '{"inputs": {"node-prompt": "A cat wearing a top hat"}}'
```

Send the same command, with the same key, to retry.

```javascript
import { randomUUID } from 'node:crypto';

const url = 'https://www.wireflow.ai/api/v1/workflows/YOUR_WORKFLOW_ID/run';
const headers = {
  Authorization: `Bearer ${process.env.WIREFLOW_API_KEY}`,
  'Content-Type': 'application/json',
  'Idempotency-Key': randomUUID(), // one key per run, reused on every retry
};
const body = JSON.stringify({ inputs: { 'node-prompt': 'A cat wearing a top hat' } });
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

let res;
for (let attempt = 0; attempt < 4; attempt++) {
  res = await fetch(url, { method: 'POST', headers, body });
  if (res.status === 429) await sleep(Number(res.headers.get('Retry-After') || 5) * 1000);
  else if (res.status >= 500) await sleep(2 ** attempt * 1000);
  else break;
}
if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
const { data } = await res.json();
console.log(data.executionId);
```

```python
import os
import time
import uuid
import requests

URL = "https://www.wireflow.ai/api/v1/workflows/YOUR_WORKFLOW_ID/run"
HEADERS = {
    "Authorization": f"Bearer {os.environ['WIREFLOW_API_KEY']}",
    "Idempotency-Key": str(uuid.uuid4()),  # one key per run, reused on every retry
}
BODY = {"inputs": {"node-prompt": "A cat wearing a top hat"}}

for attempt in range(4):
    res = requests.post(URL, headers=HEADERS, json=BODY)
    if res.status_code == 429:
        time.sleep(int(res.headers.get("Retry-After", "5")))
    elif res.status_code >= 500:
        time.sleep(2 ** attempt)
    else:
        break
res.raise_for_status()
print(res.json()["data"]["executionId"])
```

## Why is my file upload refused?

- **Cause:** Uploads return their own codes. The common ones are `file_too_large` (over the limit for the upload method), `unsupported_type` (not an accepted file type), `SignatureDoesNotMatch` (the PUT headers differ from the signed ones) and `upload_not_found` (unknown `mediaId`, the PUT never happened, or its 24 hours ran out).
- **Fix:** Follow the row for your code in the [upload error codes](/docs/api/upload-a-file-and-use-it-in-a-run) table. Uploads also need a key with the `workflows:write` scope.
- **Check:** A finished upload appears in `GET /api/v1/media`.

## Why did my run end as FAILED, CANCELLED or TIMEOUT?

- **Cause:** The request worked and returned `202`, but the run itself stopped. `FAILED` comes with an `error` message for a person to read. `CANCELLED` and `TIMEOUT` mean the run stopped before it finished.
- **Fix:** Read `error`, correct the input or the workflow, and start a new run. Branch your code on `status`, never on the wording of `error`, which can change. Files that finished generating before a run stopped are still charged.
- **Check:** Poll the new run until its `status` is `COMPLETED`. How to poll, or get a webhook instead, is in [check when a run is finished](/docs/check-when-a-run-is-finished).

Last updated: 2026-10-20

---

Documentation index: fetch https://www.wireflow.ai/llms.txt for the full list of Wireflow docs.
