What to check

  • ✓Match custom_id in the result file to successful lines, and the error file to failures. Failed lines, a job that died in validation, and rows still unprocessed at expiry should not be in this batch’s token charge. Lines that succeeded before a cancel still bill.
  • ✓Read enable_thinking on the outer body of each JSONL line, not inside extra_body. qwen3.5 through qwen3.8 default it on. The result file has no reasoning_content, so the length of content will not match output tokens.
  • ✓Every line in the file needs the same body.model and the same thinking mode. Mixed models fail validation. The job does not run, and it does not bill inference.
  • ✓The bill’s instance id should show channel batch. The new-user free quota and context cache do not explain that column. Tier price jumps for the whole input, then the 50% applies.
  • ✓Usage for a running job is not on the usage page yet. Batch stats follow task end time, and the bill is hourly. An empty page a few minutes after submit is not proof of a free run.

1. Half price lands only on successful lines

The batch inference guide prices successful requests at 50% of that model’s realtime input price and 50% of its realtime output price. The discount is per successful line. Uploading the file does not put the whole file on sale. Parse failures, task failures, and line-level errors generate no charge. A job that stays in validating and then becomes failed is usually a file-level error. The docs say no inference runs and no result file is written. Mixing models in one file takes this path. The error names the line whose model does not match the rest of the batch.

Cancel and expiry are different cuts. The OpenAI-compatible Batch API says that if the job does not finish inside completion_window, status becomes expired, unprocessed requests do not run, and they do not bill. On a cancelled job, requests that had already succeeded still bill. The console lets you set a wait of 1 to 14 days. The compatible API describes completion_window as a minimum of 24h and a maximum of 336h, integers only. When the window ends, finished lines stay in the result file and bill. Lines the queue never reached have no result and no charge. A terminal status on the job is not a free file.

A created task is not a completed line. Multimodal rows must use public URLs for images and video. A local path or an intranet address can submit, and the docs say that task will not finish processing. A line that never finishes is not a successful request, so it should not produce the half-price token charge. Sort three piles before you argue with the invoice: custom_id values in the result file, custom_id values in the error file, and rows that appear in neither because the window expired first. Only the first pile is the 50% input and output.

2. The discount multiplies thinking, not the visible answer

The 50% multiplies the input and output the line actually generated. It does not multiply the length of the sentence you can read. The batch guide says the qwen3.8, qwen3.7, qwen3.6, and qwen3.5 families default thinking on. Thinking tokens bill at the output price. Half price is therefore half the realtime output price, times an output that includes thinking. A short answer can sit next to a long chain. The docs tell you to set enable_thinking explicitly on mixed-thinking models: true on, false off. Omit the field and you get the model default. For these families, checked on 23 Oct 2026, that default is on.

The field has to sit in the right object. The guide says enable_thinking and thinking_budget belong on the outer body of each JSONL line, beside model. Putting them in extra_body does nothing. That key is how the OpenAI Python SDK forwards nonstandard parameters on a live call. A batch file ignores it. Every line in the file must also share one model and one thinking mode. To turn thinking off, every body needs enable_thinking: false. thinking_budget caps how many thinking tokens that line may spend when thinking is on. It is not a refund for tokens already generated.

The result file hides the column you are trying to reconcile. The guide says the batch result does not include reasoning_content. Even with enable_thinking true, choices[0].message has only content and role. Seeing the chain means calling the realtime API instead. Output tokens on the batch bill can be much larger than the answer in the download. A local tokenizer on content will miss the gap, and that gap is the thinking you paid for. DeepSeek putting the chain in a visible reasoning_content field is a different response shape. Here the output price includes thinking, and the file you download does not contain that text.

3. The discount does not stack with cache, and the tier covers the whole input

Batch is its own billing item. The guide says it supports the “AI general savings plan,” and that it does not support the prepaid savings plan, the new-user free quota, or context cache. The two savings-plan names are not interchangeable. A balance on one does not explain a charge on the other. The new-user free quota does not explain a line whose channel is batch. Quota that can offset a realtime call does not turn the batch line into zero.

The pricing page says the two discounts do not apply together. Where a model supports Batch, input and output are both 50% of realtime. Where it supports context cache, only input is discounted. Do not expect a cache hit to discount the input of a batch line a second time. The same page prices tiers by the input-token total of that single request, and every token on the request uses that tier. Its example is two brackets, up to 32K and from 32K to 128K: 100K of input is the second bracket for the whole request, not a cheap first slice plus a dearer remainder. Batch’s 50% is half of that bracket’s realtime price. Cross the bracket and both input and output on the successful line move up, then the half applies.

Do not copy the realtime context length onto the batch line. The batch guide caps a single request at 256K for qwen3.8-max, qwen3.8-flash, the listed qwen3.7, qwen3.6, and qwen3.5 models, and the listed omni models. The realtime price table can show a longer ladder for the same family. A line past the batch cap is not a half-price long-context call. Regions differ too. China (Beijing) lists many more models than Singapore. A model missing from that region’s table fails validation, the job does not run, and there is no half-price result. A failed validation also means no inference charge.

4. The bill arrives late, and the channel says batch

The billing guide splits the clock. Model inference usually posts in 2 to 10 minutes. Batch, training, and knowledge bases post hourly, and peaks can be later. The usage page is slower again. The batch guide says call stats use the task end time, a running task is invisible until it finishes, and monitoring lags 1 to 2 hours. An empty cost page a few minutes after submit is the posting delay, not a free job.

Once the line exists, read the channel segment of the instance id. The billing guide splits that id on semicolons. One segment is the channel, and batch means batch inference. A realtime call on the same model has an empty channel, or a different code for the console, an app, or RAG. Adding realtime rows and batch rows under one model name mixes full price and half price into a single total. The free-quota stop flag is a different segment, 0 or 1. When the channel is already batch, that flag is the wrong explanation for the amount.

The result file has a second clock. The batch guide says result and error files are deleted 30 days after the task ends. The monthly bill remains. After those 30 days you can still see a batch total and may no longer have the custom_id list that separates successes from zeros. Save both files if you need a line-level reconcile. custom_id itself stops at 256 characters. Longer identifiers go in a custom metadata field when you create the job. Stuffing them into custom_id fails validation, and the job does not run.

5. A top-up does not create the discount

Reconcile five columns. First, status: failed during validation usually means nothing ran; expired keeps only the lines that were processed; cancelled still bills lines that succeeded before the cancel. Second, whether enable_thinking is on the outer body, and that the result file has no reasoning_content under the current docs. Third, which input tier the line landed in, because the 50% applies to that tier. Fourth, whether the instance id says batch, and whether anyone tried to subtract free quota or context cache from it. Fifth, whether the result file is still inside the 30 days. Model names and the thinking default change. Use body.model on the line and the support table in the batch guide.

Funding the Bailian account happens before the debit. Batch is pay-as-you-go from the balance. Opening Model Studio is enough. There is no separate batch package to buy. Moving stablecoins on-chain into assets that can top the account up does not change the 50% on a successful line, does not remove thinking tokens from output, and does not start billing the failed lines. When you need that conversion, a no-signup, non-custodial swap on AllSwap is one place to do it. A completed swap only means the asset changed hands. Thinking tokens on a successful line still show up at half the realtime output price.