THE WORLD IS NOT STANDING STILL.RSS
BIG CHANGE.
BIG CHANGE, one tap away
BIG CHANGE, one tap away

Make room for the big picture.

Add BIG CHANGE to your home screen or desktop. It opens in its own window, with the latest stories when you’re online.

iPhone & iPad
Open bigchange.ai in Safari. Tap Share, then Add to Home Screen. Turn on Open as Web App if shown, then tap Add.
Android
Open bigchange.ai in Chrome. In the browser menu, choose Install app or Add to Home screen.
Desktop
In Chrome or Edge, use the install icon in the address bar or the browser’s app menu. In Safari on Mac, choose File → Add to Dock.

Markdown edition

# Generate and check one authorized Higgsfield video from Codex

> Use Codex and a local script to approve one bounded Seedance 2.0 request, recover its ID, download the resulting video and inspect the actual file and charge.

By BIG CHANGE Editorial

Published: 2026-10-21T22:26:38.762Z
Updated: 2026-10-21T22:26:38.762Z
Canonical: https://bigchange.ai/blog/codex-higgsfield-one-authorized-video-guide

![Conceptual charcoal illustration of one person watching an abstract frame projected onto a wall from a computer-connected projector.](https://bigchange.ai/api/media/file/higgsfield-local-video-viewing-hero-v3.png)
AI-generated conceptual illustration by BIG CHANGE.

Codex can run a local script that submits one Higgsfield video request, keeps its identity through a timeout, and saves the resulting file for you to watch. The useful result is the checked video on disk. A key, a successful POST, or a media link is only a step toward it.

This guide is for a reader with local Codex access, a permitted text prompt, Higgsfield API access to Seedance 2.0, and authority to approve one paid generation. It uses the [documented text-to-video endpoint](https://open.higgsfield.ai/models/bytedance/seedance-2.0/text-to-video/api-reference) with five seconds, 720p, 16:9, and audio disabled. BIG CHANGE checked the primary documentation on 21 October 2026. We did not log into an account, submit a request, generate a video, inspect media, or measure a charge. The code below has only been checked offline.

## The big change

- **What changed:** Higgsfield exposes Seedance 2.0 text-to-video through a submit, status, and media-download API. Codex can run a local script around that documented lifecycle.
- **Why it matters:** One saved request identity connects your approval to the eventual file. A lost response or failed download need not become a second paid generation.
- **What to watch:** Model access, the price for your exact account and settings, prompt rights, the terminal status, and the quality of the actual video all need your own checks. Documentation cannot establish any of those outcomes for your account.

## 1. Prepare the account and the one request

In the [Higgsfield API console](https://open.higgsfield.ai/api-keys), check that your account can use **Seedance 2.0 text-to-video**. Read the current API price for **five seconds, 720p, 16:9, audio off**, your available balance, and any account-specific terms. [Higgsfield's public pricing page](https://open.higgsfield.ai/pricing) shows changing model and promotional rates; a “from” rate is not a quote for your request. API billing is separate from any creator subscription. Set a maximum acceptable charge for exactly one generation before continuing.

Use a prompt that you have permission to submit and a result you are permitted to use. Higgsfield's [privacy policy](https://higgsfield.ai/privacy-policy) describes processing of prompts, inputs, and outputs for service operation and possible improvement; running the command locally does not keep the prompt local. Review the [terms](https://higgsfield.ai/terms-of-use-agreement) for input rights, likenesses, output responsibility, and any terms specific to your account. Do not assume exclusive output rights or automatic clearance for downstream use.

Create a new private local directory, for example `video-job`, outside a synced or shared repository. Put the approved text prompt in `prompt.txt` there. Create an empty `video_job.py` beside it and paste the complete script in step 2. Use Python 3.10 or newer; the script uses only the standard library. The directory will contain the prompt and a status record that may contain a signed media URL. Limit its filesystem access and do not commit or share it. In Codex, open that directory as the local project, inspect the script, and check the actual command's file and network permissions. Codex permission to run a command does **not** itself approve a paid generation.

Create an API credential in the console. Supply the **complete copied value** as `HF_KEY` in the local process environment. The REST header is `Authorization: Key <complete copied value>`; do not add `Key` inside the variable or split the value for this script. Do not paste the credential into Codex chat, `prompt.txt`, source code, logs, a committed `.env` file, or browser JavaScript. In PowerShell, a private session can prompt for it without echoing:

```shell
$env:HF_KEY = Read-Host 'Complete Higgsfield API key' -AsSecureString | ForEach-Object { [System.Net.NetworkCredential]::new('', $_).Password }
```

That environment variable remains in the current shell and child processes until you remove it or close the shell. If your Codex command runs in a different shell, supply the secret through that environment's approved secret mechanism. Avoid letting the assistant read or print it.

## 2. Save the local one-request script

Save this as `video_job.py`. It writes the exact endpoint, body, and a UUID idempotency key **before** any POST. `init` has no network side effect. `submit` is the paid boundary and requires a separate, literal authorization flag. `replay` is only for an uncertain submission: it resends the same saved endpoint, body, and key. `resume` polls a saved request, and `download` retrieves its completed media without an API credential on the media request.

```python
import argparse
import hashlib
import json
import os
import pathlib
import sys
import time
import urllib.error
import urllib.parse
import urllib.request
import uuid
from datetime import datetime, timezone

STATE = pathlib.Path("request-private.json")
ENDPOINT = "https://api.higgsfield.ai/bytedance/seedance-2.0/text-to-video"
TERMINAL = {"completed", "failed", "nsfw", "canceled"}
STATUSES = TERMINAL | {"queued", "in_progress"}

class APIHTTPError(RuntimeError):
    def __init__(self, status):
        self.status = status
        super().__init__(f"API HTTP {status}; inspect account/request before retry")

def now():
    return datetime.now(timezone.utc).isoformat()

def save(state):
    # Exclusive first write; atomic replacement thereafter. Keep this directory private.
    temp = STATE.with_name(STATE.name + ".tmp")
    with open(temp, "x", encoding="utf-8") as f:
        json.dump(state, f, indent=2, ensure_ascii=False)
        f.flush()
        os.fsync(f.fileno())
    if os.name != "nt":
        os.chmod(temp, 0o600)
    os.replace(temp, STATE)

def load():
    if STATE.with_name(STATE.name + ".tmp").exists():
        raise ValueError("Interrupted save: reconcile request-private.json and its .tmp file locally before proceeding")
    with open(STATE, encoding="utf-8") as f:
        state = json.load(f)
    if state["endpoint"] != ENDPOINT:
        raise ValueError("Saved endpoint differs from this recipe; stop and inspect")
    return state

def key():
    value = os.environ.get("HF_KEY", "")
    if not value or value.startswith("Key "):
        raise ValueError("Set HF_KEY to the complete copied credential only")
    return value

def api_request(method, url, body=None, idem=None):
    headers = {"Authorization": "Key " + key(), "Accept": "application/json"}
    if body is not None:
        headers["Content-Type"] = "application/json"
    if idem:
        headers["Idempotency-Key"] = idem
    request = urllib.request.Request(url, data=body, headers=headers, method=method)
    # A redirect must not carry the Authorization header to another host.
    class NoRedirect(urllib.request.HTTPRedirectHandler):
        def redirect_request(self, req, fp, code, msg, newurl):
            raise ValueError("API redirected; stop before forwarding credential")
    opener = urllib.request.build_opener(NoRedirect)
    try:
        with opener.open(request, timeout=30) as response:
            raw = response.read()
            correlation = response.headers.get("X-Request-ID") or response.headers.get("X-Correlation-ID")
            result = json.loads(raw)
            if not isinstance(result, dict):
                raise ValueError("API response was not an object")
            return result, correlation
    except urllib.error.HTTPError as e:
        # Do not log response bodies or the credential. Preserve state for diagnosis.
        raise APIHTTPError(e.code) from None

def checked_status_url(state):
    url = state["status_url"]
    parsed = urllib.parse.urlparse(url)
    expected = "/requests/" + urllib.parse.quote(state["request_id"], safe="") + "/status"
    if (parsed.scheme, parsed.netloc, parsed.path, parsed.query, parsed.fragment) != (
        "https", "api.higgsfield.ai", expected, "", ""
    ):
        raise ValueError("Unexpected status URL; stop before sending the credential")
    return url

def init():
    if STATE.exists() or STATE.with_name(STATE.name + ".tmp").exists():
        raise ValueError("Existing request state; never initialize over it")
    prompt = pathlib.Path("prompt.txt").read_text(encoding="utf-8").strip()
    if not prompt:
        raise ValueError("prompt.txt is empty")
    body = {"prompt": prompt, "duration": 5, "resolution": "720p",
            "aspect_ratio": "16:9", "generate_audio": False}
    state = {"endpoint": ENDPOINT, "body": body, "idempotency_key": str(uuid.uuid4()),
             "prepared_at_utc": now(), "phase": "prepared", "request_id": None,
             "status_url": None, "status": None, "correlation_id": None}
    save(state)
    print("Prepared one request in request-private.json. Inspect it and the account price.")

def post(state, replay=False):
    if replay:
        if state["request_id"] or state["phase"] not in {"transmitting", "uncertain"}:
            raise ValueError("Replay only an uncertain submission with no saved request ID")
    elif state["phase"] != "prepared":
        raise ValueError("Already submitted or uncertain; use resume or replay")
    if not replay:
        state["phase"] = "transmitting"
        state["first_post_at_utc"] = now()
        save(state)
    wire = json.dumps(state["body"], separators=(",", ":"), ensure_ascii=False).encode("utf-8")
    try:
        result, correlation = api_request("POST", state["endpoint"], wire,
                                          state["idempotency_key"])
        request_id = result.get("request_id")
        status_url = result.get("status_url")
        if not isinstance(request_id, str) or not request_id or not isinstance(status_url, str):
            raise ValueError("Acceptance lacked request_id or status_url")
        state["request_id"] = request_id
        state["status_url"] = status_url
        checked_status_url(state)
        state["correlation_id"] = correlation
        state["status"] = result.get("status")
        state["phase"] = "accepted"
        state["accepted_at_utc"] = now()
        save(state)
        print("Accepted request_id:", request_id, "— run resume")
    except Exception as e:
        state["phase"] = "rejected" if isinstance(e, APIHTTPError) and e.status < 500 else "uncertain"
        state["last_uncertain_at_utc"] = now()
        save(state)
        raise

def resume(state):
    if not state["request_id"]:
        raise ValueError("No request ID. Inspect state; replay the identical intent only if POST may have reached the API")
    url = checked_status_url(state)
    deadline = time.monotonic() + 15 * 60
    delay = 2.0
    while time.monotonic() < deadline:
        try:
            result, correlation = api_request("GET", url)
        except APIHTTPError as e:
            if e.status < 500:
                raise
            print("Status read failed:", str(e), "— request identity retained")
            time.sleep(delay)
            delay = min(delay * 1.5, 10.0)
            continue
        except (urllib.error.URLError, TimeoutError) as e:
            print("Status read failed:", str(e), "— request identity retained")
            time.sleep(delay)
            delay = min(delay * 1.5, 10.0)
            continue
        status = result.get("status")
        if result.get("request_id") != state["request_id"] or status not in STATUSES:
            raise ValueError("Malformed or mismatched status response; retained request ID, stop and inspect")
        state["status"] = status
        state["last_status_at_utc"] = now()
        state["correlation_id"] = correlation or state.get("correlation_id")
        if status == "completed":
            video = result.get("video")
            media_url = video.get("url") if isinstance(video, dict) else None
            state["video_url"] = media_url if isinstance(media_url, str) and media_url else None
        save(state)
        print("Status:", status)
        if status in TERMINAL:
            if status == "completed" and not state.get("video_url"):
                print("Completed without video.url; inspect the request before any new generation")
            return
        time.sleep(delay)
        delay = min(delay * 1.5, 10.0)
    print("Polling deadline reached; generation may still be running. Run resume later.")

def download(state):
    if state.get("status") != "completed" or not state.get("video_url"):
        raise ValueError("Need completed status and video.url; use resume")
    url = state["video_url"]
    parsed = urllib.parse.urlparse(url)
    if parsed.scheme != "https" or not parsed.hostname:
        raise ValueError("Unexpected media URL; stop")
    target = pathlib.Path("video-output.mp4")
    part = pathlib.Path("video-output.mp4.part")
    if target.exists() or part.exists():
        raise ValueError("Output or partial file exists; inspect it and retain any confirmed incomplete .part copy before retrying download")
    # Deliberately no Authorization header on this request, including redirects.
    class HTTPSRedirect(urllib.request.HTTPRedirectHandler):
        def redirect_request(self, req, fp, code, msg, newurl):
            if urllib.parse.urlparse(newurl).scheme != "https":
                raise ValueError("Media redirected outside HTTPS")
            return super().redirect_request(req, fp, code, msg, newurl)
    opener = urllib.request.build_opener(HTTPSRedirect)
    request = urllib.request.Request(url, headers={"User-Agent": "video-job/1"})
    try:
        with opener.open(request, timeout=60) as response, open(part, "xb") as out:
            final_url = urllib.parse.urlparse(response.geturl())
            if final_url.scheme != "https":
                raise ValueError("Media redirected outside HTTPS")
            while True:
                chunk = response.read(1024 * 1024)
                if not chunk:
                    break
                out.write(chunk)
            out.flush()
            os.fsync(out.fileno())
    except Exception:
        print("Download interrupted; inspect the partial file. Do not generate again.")
        raise
    if part.stat().st_size == 0:
        raise ValueError("Downloaded zero bytes; inspect media URL and retry download only")
    os.replace(part, target)
    hasher = hashlib.sha256()
    with open(target, "rb") as saved:
        for chunk in iter(lambda: saved.read(1024 * 1024), b""):
            hasher.update(chunk)
    digest = hasher.hexdigest()
    state["download"] = {"file": str(target), "bytes": target.stat().st_size,
                         "sha256": digest, "url_host": parsed.hostname,
                         "downloaded_at_utc": now()}
    save(state)
    print("Saved:", target, "bytes:", target.stat().st_size, "SHA-256:", digest)

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("action", choices=["init", "submit", "replay", "resume", "download"])
    parser.add_argument("--authorize-paid-one", action="store_true")
    args = parser.parse_args()
    if args.action == "init":
        init()
        return
    state = load()
    if args.action == "submit":
        if not args.authorize_paid_one:
            raise ValueError("One paid generation needs --authorize-paid-one")
        post(state)
    elif args.action == "replay":
        post(state, replay=True)
    elif args.action == "resume":
        resume(state)
    else:
        download(state)

if __name__ == "__main__":
    try:
        main()
    except Exception as e:
        print(type(e).__name__ + ": " + str(e), file=sys.stderr)
        sys.exit(1)
```

The status record is a recovery journal, not a public artifact. Review it without exposing the prompt or URL. A `request-private.json.tmp` file signals an interrupted local save. Compare both JSON files privately; if the temp file has the latest valid request ID and matching endpoint/body/key, preserve it as the main record. Otherwise preserve the main record. Remove the stale temp only after that reconciliation. The script stops while the temp file exists so a later command cannot silently discard an accepted ID. Do not delete state and begin a new intent because the process exited.

## 3. Authorize and submit once

Run these commands from the private project directory in the same shell that holds `HF_KEY`:

```shell
python video_job.py init
```

Inspect `request-private.json` locally in a private editor: verify the saved endpoint, **your actual prompt**, five-second body, and idempotency key without printing the prompt into a shared Codex transcript. Check the account's live model entitlement, exact current price and balance again. Record your one-request budget and explicit approval outside the script's logs. The next command is the paid boundary. Ask Codex to show the command and obtain the human's authorization for **one** Seedance 2.0 generation with the inspected body before running it; command permission alone does not supply that decision.

```shell
python video_job.py submit --authorize-paid-one
```

The script prints the saved `request_id` after an acceptance response. Keep the private JSON until the work is resolved. The [quick start](https://open.higgsfield.ai/quick-start) describes the POST response as `request_id`, `status_url`, and `cancel_url`; this recipe does not call cancel. Stopping the local script also does not cancel a generation.

## 4. Recover the same request and wait for status

If `submit` reports a timeout, network failure, server error, or an acceptance body without the required fields, inspect `request-private.json` and your account's Requests view first. A failed local write or lost response does not show whether the service accepted the generation. If the saved phase is `uncertain` and there is no `request_id`, use:

```shell
python video_job.py replay
```

This replays the **same endpoint, saved JSON body, and Idempotency-Key**. Higgsfield's [idempotency rules](https://docs.higgsfield.ai/docs/concepts/idempotency) say that a repeat of the same intent returns the original request identity. The page does not state how long that mapping is retained. For delayed recovery, first reconcile the saved key and request with your account's Requests view or Higgsfield support; if you cannot establish that the same intent is still protected, stop before replay rather than treating the key as an indefinite guarantee. Do not edit the body, switch models, or generate a new idempotency key to work around a timeout or `422` parameter mismatch. A replay response can carry an earlier acceptance status; fetch the current status next. An authentication or validation rejection needs diagnosis and a separate decision before any changed paid request. If the account shows a request ID that the local record missed, keep that ID and reconcile it with the saved key and body before any replay.

For an accepted ID, run:

```shell
python video_job.py resume
```

The script checks that the status URL belongs to the Higgsfield API before sending the secret. It polls from two seconds up to ten, stops at `completed`, `failed`, `nsfw`, or `canceled`, and returns after a 15-minute local deadline. That deadline is **not** cancellation and says nothing about whether the service will later complete. Run `resume` again against the saved ID if it is still nonterminal. If a status read cannot be decoded, returns a missing ID, or repeatedly fails, inspect the saved ID, account, and [request status documentation](https://docs.higgsfield.ai/docs/api-reference/requests/get-request-status). Do not submit a replacement to solve a status-read error. The [polling guidance](https://docs.higgsfield.ai/docs/concepts/polling) distinguishes retrying a GET from starting another generation.

## 5. Download and judge the actual video

After `completed` includes `video.url`, run:

```shell
python video_job.py download
```

This writes to an unused `video-output.mp4` path and records bytes, SHA-256, and media URL host in the private journal. The download sends **no Higgsfield API key** to the media host. If `video-output.mp4.part` remains after an interrupted download, inspect it locally. Once you confirm it is incomplete, rename **only that partial file** to an unused private name in the same directory, such as `video-output.incomplete-review.part`. Keep `request-private.json` and its request ID where they are, then run `python video_job.py download` again. An expired or inaccessible media URL calls for a fresh status read or account support, not a second POST.

Now reopen `video-output.mp4` in a local video player and watch it from start to end. On Windows PowerShell, `Invoke-Item .\video-output.mp4` opens the saved file with its associated player. Check whether the depicted content meets the approved prompt and whether artifacts make it unusable. Inspect the actual duration, frame dimensions/aspect ratio, and audio track with your player. If `ffprobe` is installed locally, this command prints the technical fields:

```shell
ffprobe -v error -show_entries format=duration:stream=codec_type,width,height -of json video-output.mp4
```

Compare the observed properties with the requested five seconds, 16:9, and **no audio**. Record the player/probe observations, the file hash from the journal, the request ID, and your human decision: accept, revise, or reject. A nonempty file, a `.mp4` extension, or `completed` alone cannot establish playable, acceptable media. If you decide to revise, that is a **new** paid generation requiring a revised approved prompt, a **new idempotency key**, a fresh price check, and explicit paid authorization. The API credential need not be rotated merely because you request a new video. This script deliberately has no automatic revision path.

Finally, read the charge or usage for that request in your actual Higgsfield account and record the amount and currency, or mark it **unknown** until billing catches up. Do not infer a charge from a public “from” rate or claim that a creator plan pays for API use. Your completed record is the accepted or rejected playable file, technical observations, SHA-256, request ID, and observed charge or unresolved billing status.

## Sources & further reading

- [Seedance 2.0 text-to-video API reference](https://open.higgsfield.ai/models/bytedance/seedance-2.0/text-to-video/api-reference) — model path and allowed request settings.
- [Higgsfield quick start](https://open.higgsfield.ai/quick-start), [authentication](https://docs.higgsfield.ai/docs/authentication), [idempotency](https://docs.higgsfield.ai/docs/concepts/idempotency), [polling](https://docs.higgsfield.ai/docs/concepts/polling), [errors](https://docs.higgsfield.ai/docs/concepts/errors), and [status response](https://docs.higgsfield.ai/docs/api-reference/requests/get-request-status) — the request lifecycle and recovery rules.
- [API pricing](https://open.higgsfield.ai/pricing), [privacy policy](https://higgsfield.ai/privacy-policy), and [terms](https://higgsfield.ai/terms-of-use-agreement) — account, data, and use conditions to check before submission.
- [Codex permissions](https://learn.chatgpt.com/docs/permissions) — local command access varies by client and configuration.
- BIG CHANGE's [Codex skill guide](https://bigchange.ai/blog/codex-skill-from-accepted-work-guide) describes turning **already accepted** work into a reusable skill. This task first requires one checked video; a skill is optional later.

## Sources

- [Seedance 2.0 text-to-video API reference](https://open.higgsfield.ai/models/bytedance/seedance-2.0/text-to-video/api-reference) — Documents exact REST model path, required prompt and valid 5s, 720p, 16:9 and generate_audio false body. It does not establish reader entitlement or output.
- [Higgsfield quick start](https://open.higgsfield.ai/quick-start) — Documents complete copied credential under Authorization: Key, POST acceptance fields, status lifecycle and completed video.url; examples are not observed results.
- [Authentication](https://docs.higgsfield.ai/docs/authentication) — Documents server-side credential use and account/model access distinction. Credential validity alone does not prove Seedance entitlement.
- [Idempotent requests](https://docs.higgsfield.ai/docs/concepts/idempotency) — Documents same endpoint/body/key replay for one generation intent and stale acceptance possibility. No retention window is stated; delayed recovery requires account/support reconciliation.
- [Polling](https://docs.higgsfield.ai/docs/concepts/polling) — Documents 2-to-10-second status backoff, terminal states and application deadline; a local deadline is not cancellation.
- [Errors](https://docs.higgsfield.ai/docs/concepts/errors) — Documents retry/error distinctions and X-Correlation-ID; auth/validation errors need diagnosis and no automatic new generation.
- [Get request status](https://docs.higgsfield.ai/docs/api-reference/requests/get-request-status) — Documents required request_id and status enum plus completed video.url. Actual media properties and playback remain reader checks.
- [API pricing](https://open.higgsfield.ai/pricing) — Displays variable model API prices and promotional from-rates; exact selected-settings charge and account balance need live reader readback.
- [Privacy policy](https://higgsfield.ai/privacy-policy) — Describes service processing and possible improvement use of prompts, inputs and outputs; local command execution is not local-only processing.
- [Terms of use](https://higgsfield.ai/terms-of-use-agreement) — Documents rights, content responsibility and developer terms; no automatic exclusivity or downstream clearance is inferred.
- [Permissions](https://learn.chatgpt.com/docs/permissions) — Explains that Codex command file and network permissions depend on client settings; command permission is separate from one paid-request approval.