# Orders

> The Markdown form of https://openfill.ai/docs/orders. The index of every docs page is https://openfill.ai/llms.txt, and every page in one file is https://openfill.ai/llms-full.txt.

A request is an order. It queues until it clears, and it is never rejected on price. This page is what happens to it from submission to result.

Set shell variables such as ORDER_ID, FILE_ID and BATCH_ID to ids returned by the API before running the corresponding command.

## Lifecycle

`queued → executing → done`. An order executes when it reaches the top of the book and the model has room; the level at that moment is what it is charged, up to its price. Until then it waits in the book, highest price first, and first-come-first-served within a price. While queued the HTTP connection stays open, and a streaming request receives keep-alive comments naming its place in the book.

A request to a fixed-price model waits in its model's book without a price to rank it by. The requests waiting for one model take turns across accounts, the account served least recently first; within an account, interactive requests go first, with one batch line after every 4. It is charged that model's prices.

| Status | Meaning | Charged |
| --- | --- | --- |
| queued | In the book, ranked by price, waiting for room on the model. | Nothing yet. |
| executing | Admitted and running on the model. A run that fails before any output goes back to queued. | At completion. |
| done | Finished. usage.x_market says what it cost. | The level at execution, up to the bid, or a fixed-price model's prices. |
| error | The backend failed, or the response was produced but could not be settled. | Nothing, except the settlement case, which the message names. |
| canceled | You cancelled it while it was queued, or the client disconnected while it waited. | Nothing. |
| expired | It waited past your timeout, or a locked order missed its execute window. | Nothing. |

```bash
GET  https://api.openfill.ai/v1/orders              # ?status=queued,executing to narrow
GET  https://api.openfill.ai/v1/orders/{id}         # one order's state, no result body
POST https://api.openfill.ai/v1/orders/{id}/cancel  # cancel a still-queued order
```

## Every way an order ends

One row per case, with what the request answers and what is charged. A blocking request gets the status code; a streaming request that was already answered 200 gets the same error inside the stream.

| Case | Answer | Charged |
| --- | --- | --- |
| The model has room and the order is at the top of the book. | It runs. 200 with the completion. | The level at execution, up to your price limit, or a fixed-price model's prices. |
| The model has no room, or higher prices are ahead of it. | It waits in the book, highest price first, until a slot frees, until max_wait, or until you cancel. | Nothing while it waits. |
| max_wait passes while it is still queued on a market model. | 408 order_expired. | Nothing. |
| max_wait passes while it is still queued on a fixed-price model. | 429 capacity_unavailable, with Retry-After. | Nothing. |
| It is queued on a fixed-price model when that model's prices change. | It expires unrun, with a message saying so. | Nothing. |
| You cancel it while it is queued. | The cancel answers 200; the waiting request answers 409 order_canceled. | Nothing. |
| It is price-locked and does not start within 10 seconds. | 429 capacity_unavailable, with Retry-After. | Nothing. |
| Your balance cannot cover it at the current level, or at a fixed-price model's prices, when you send it. | 402 insufficient_balance. It never enters the book. | Nothing. |
| The backend fails before any output, and retries are exhausted. | It goes back into the book and waits, or, past its deadline, answers 408 or 429. | Nothing. |
| The backend fails after streaming has begun. | 502 inside the stream, after what was delivered. | Nothing. |
| The client disconnects while it is queued. | The order is cancelled. | Nothing. |
| The client disconnects while it is executing. | It runs to completion. | The level at execution, up to its bid. |

## Waiting and timeouts

An order on a market model whose price is not locked waits for a slot, ranked by price against every other order on its model. A very low price waits for a trough in demand, and at sustained full utilization it can wait a long time. It waits at most your order timeout, you can cancel a queued order at any time, and raising the price moves it up the book.

The timeout is your account's order timeout, set on [Bidding](https://openfill.ai/bidding), unless the request carries its own. A price-locked order ignores it: it has its own window (10 seconds), timed from submission, in which to start. One that starts runs to completion; one that does not expires with a `429`. An order on a fixed-price model keeps the timeout whatever its lock setting, and one that reaches it answers `429` too.

```python
import os
from openai import OpenAI
client = OpenAI(base_url="https://api.openfill.ai/v1", api_key=os.environ["OPENFILL_API_KEY"])

r = client.chat.completions.create(
    model="market-preview",
    messages=[{"role": "user", "content": "hello"}],
    extra_body={
        "price_limit": 0.10,      # a waiting limit order, timeouts apply
        "price_lock": False,   # unless your account locks by default
        "expires_after": 600,   # give up if still queued after 10 minutes
    },
)
```

**Overriding the timeout per request**

Use `max_wait`, its alias `expires_after`, or the `X-Expires-After` header, in seconds. The range is 10 seconds to 30 days. The body beats the header, and both beat the account setting. A value below the floor is raised to it, silently and with no warning in the response; a value at or below zero or a fractional one is a `400`, from the header exactly as from the body, and a value above the configured ceiling is lowered to it. The schema accepts at most2592000; a larger value is refused before clamping. max_wait wins over expires_after, and a nonempty header is validated even when a body field wins.

An order on a market model that hits your timeout returns `408` with error type `order_expired`. A price-locked order that misses its window returns `429`, meaning no capacity in time rather than a deadline you chose. An order on a fixed-price model that does not start before its timeout returns `429` too, with code `capacity_unavailable`. None of them is charged. OpenAI SDKs retry both automatically and a retry re-queues with a fresh deadline, so set `max_retries` if you want a hard cutoff.

Set the SDK's own request timeout above `max_wait` plus the time a generation takes. A client timeout shorter than the wait abandons a request that is still in the book and would have run.

## A blocking request that sends nothing

The edge in front of the API closes a response that has carried no bytes for about two minutes and answers `524` in its place. A disconnect reaching the gateway cancels an attached request that is still queued; an execution already in progress can finish and be charged up to its bid. A blocking request that may wait or generate longer than that should stream, where the first comment arrives at once, or run detached and collect the result.

## Cancelling

`POST /v1/orders/{id}/cancel` takes a queued order out of the book and answers `200` with `status: canceled`. Cancelled orders are never charged. An order that is already executing or finished cannot be cancelled: the call answers `409 order_not_cancelable`.

A blocking request that is cancelled from elsewhere answers `409` with type `order_canceled`. Disconnecting a blocking or streaming request while it is queued cancels it too; a detached order survives the disconnect.

## Correcting the price on a queued order

A queued order's price limit can be corrected, which is how you rescue requests submitted under a market that has since moved away from them. `POST /v1/orders/price` takes `ids` (1 to 500 request ids) and `price_limit`, and answers per id.

```bash
curl https://api.openfill.ai/v1/orders/price \
  -H "Authorization: Bearer $OPENFILL_API_KEY" -H "Content-Type: application/json" \
  -d '{"ids": ["'"$ORDER_ID"'"], "price_limit": 0.05}'
```

**Response**

```json
{"object": "list", "price_limit": 0.05, "changed": 1, "data": [
  {"id": "...", "changed": true, "code": null, "reason": null}]}
```

The market engine applies the change, so the call returns once the new price limit is really in the book: within one clearing tick. Raising a price limit does not charge you it. You pay the clearing level at the moment your request runs, up to your price limit, so a higher price limit buys a place further up the book. A repriced order gives up its place in the queue among orders at its new price. A price-locked order is refused: its bid was set at submission to the quote then standing, capped by your price limit, and is not yours to set. An order on a fixed-price model is refused as well, since it is charged its published prices. An item that changed carries a `code` of null. One that did not says why in `reason` and names the case in `code`: `not_queued`, `price_locked`, `too_large`, `fixed_price_model`, or `unavailable` when the market could not apply the change just now, as during a deploy; that order is still queued, and a retry can apply it.

## Listing and paging

The list is filterable by `?status=`, `?model=`, `?kind=`, `?batch=`, `?id=` (a prefix of at least four characters) and `?from=`/`?to=`, and paginates newest first: pass the `next_cursor` from one response as `?cursor=` on the next, until `has_more` is false.

```bash
curl "https://api.openfill.ai/v1/orders?status=done&include=usage,totals&limit=50" \
  -H "Authorization: Bearer $OPENFILL_API_KEY"
```

### From a completion to its order

A completion's `id` is the model server's, so it is not the order id. The order id is in `usage.x_market.order_id` on every completion, and on the response as the `x-openfill-order-id` header, which a stream carries in its head before the first token. A blocking caller that times out before receiving headers has no order id from that response; resend with the same Idempotency-Key. Every route on this page takes that id.

### What ?include= adds

It takes a comma list. usage, storage, batch and pricing add a field to every order; totals and counts add to the response beside data.

| Value | Adds | Notes |
| --- | --- | --- |
| usage | What the order was billed, on the order itself | Null when no usage record exists. A settlement failure may have a record; charged_usd includes refunds. |
| totals | total_orders, total_failed, total_charged_usd, total_at_bid_usd and total_saved_usd for the whole filtered set | First page only, since they do not change as you page. total_at_bid_usd and total_saved_usd cover the market orders in the set, and are null when it holds orders and none is a market order; a set with no orders answers 0. |
| counts | How many orders in the filtered set are queued and executing right now, and oldest_queued_at | On every page, filtered like the rows, so ?status=done gives zeros. |
| storage | result_stored and log_stored | Scoped to that order: a request sent with store: false keeps nothing even on an account that keeps everything. |
| batch | batch_id | Null for a request sent outside a batch. |
| pricing | price_lock and pricing, and on a fixed-price order usd_per_million | Whether the order bid the quote, and whether its model prices it on the market or at a fixed price. usd_per_million holds the three prices a fixed-price order was submitted at, per 1M tokens of cache-hit input, cache-miss input and output, and stays on the order after the model's prices change. |

### Following an order without its body

`GET /v1/orders/{id}` reports status, queue position, what the order cleared at and any error, and works whatever your storage setting is. `queue_position` is null for an order on a fixed-price model, where accounts take turns and a place in the book is no rank.

```bash
curl "https://api.openfill.ai/v1/orders/$ORDER_ID" \
  -H "Authorization: Bearer $OPENFILL_API_KEY"
```

**Response**

```json
{"id": "...", "status": "queued", "queue_position": 3, "bid": 0.05, "kind": "chat", "model": "...", "level_at_execution": null,
 "created_at": "...", "expires_at": "...", "admitted_at": null, "finished_at": null, "error": null}
```

`GET /v1/usage` lists the same charges keyed by usage record rather than by order, with the same cursor pagination and the account balance alongside. Reach for it when you want billing history and do not care what each request was doing.

> A request that should run in the background, with the result collected later, is a [detached order](https://openfill.ai/docs/detached). It has the same lifecycle and, apart from surviving a disconnect, the same outcomes. The answer to the submit changes, and a terminal error is read from the result URL rather than answered with a status code.

Previous: [Rate limits](https://openfill.ai/docs/rate-limits). Next: [Detached orders](https://openfill.ai/docs/detached).
