Quick answer
Use Game.market.createOrder() only from an explicit one-time request. Validate the order type, resource, positive price, positive integer amount, owned active Terminal, 5% fee, Credit reserve, and equivalent existing orders. Disable the request before the API call, save the exact snapshot and return code, then confirm the order in Game.market.orders on a later tick.
Calculate the 5% creation fee
The official market charges a fee when an order is placed:
creation fee = price × totalAmount × 0.05
function calculateOrderFee(price, totalAmount) {
if (
!Number.isFinite(price)
|| price <= 0
|| !Number.isInteger(totalAmount)
|| totalAmount <= 0
) {
return null;
}
return price * totalAmount * 0.05;
}
A sell order does not prepay the full resource value. A buy order also does not spend the full future purchase value at creation. Both pay the 5% placement fee, while later activation and deals depend on available resources or Credits.
Use a one-time request
Memory.market ??= {};
Memory.market.createOrderRequest = {
enabled: true,
requestId: 'manual-order-2026-07-26-01',
type: ORDER_SELL,
resourceType: RESOURCE_UTRIUM,
price: 1,
totalAmount: 10000,
roomName: 'W1N1',
creditReserve: 1000000
};
Every number is an example, not a market recommendation. Review the current market, inventory, Terminal, fee, account budget, and intended order direction before enabling the request.
Validate values and Terminal state
function validateOrderRequest(request) {
if (!request || request.enabled !== true) {
return { valid: false, reason: 'disabled' };
}
if (
request.type !== ORDER_BUY
&& request.type !== ORDER_SELL
) {
return { valid: false, reason: 'order-type-invalid' };
}
if (
typeof request.resourceType !== 'string'
|| typeof request.roomName !== 'string'
|| request.roomName.length === 0
|| !Number.isFinite(request.price)
|| request.price <= 0
|| !Number.isInteger(request.totalAmount)
|| request.totalAmount <= 0
) {
return { valid: false, reason: 'arguments-invalid' };
}
return { valid: true, reason: 'valid' };
}
For ordinary Terminal resources, verify that the requested room is currently visible and contains your active Terminal. Account-bound resources use different room rules and are outside this guide.
Reject equivalent duplicate orders
function findEquivalentOrder(request) {
return Object.values(Game.market.orders).find(order =>
order.type === request.type
&& order.resourceType === request.resourceType
&& order.roomName === request.roomName
) || null;
}
Equivalent does not necessarily mean identical price or remaining amount. The safer default is to stop and review the existing order, then use the dedicated maintenance APIs rather than creating another order every tick.
Build a testable order plan
function evaluateOrderPlan(input) {
const validation = validateOrderRequest(input.request);
if (!validation.valid) {
return { ready: false, reason: validation.reason };
}
if (!input.terminalReady) {
return { ready: false, reason: 'terminal-not-ready' };
}
if (input.duplicateFound) {
return { ready: false, reason: 'equivalent-order-exists' };
}
const fee = calculateOrderFee(
input.request.price,
input.request.totalAmount
);
if (!Number.isFinite(fee)) {
return { ready: false, reason: 'fee-invalid' };
}
const reserve = Number.isFinite(
input.request.creditReserve
)
? input.request.creditReserve
: 0;
if (input.credits - fee < reserve) {
return {
ready: false,
reason: 'credit-reserve',
fee
};
}
return {
ready: true,
reason: 'ready',
fee,
creditsAfterFee: input.credits - fee
};
}
This function does not call the market. It can be tested with snapshots before any irreversible operation.
Complete one-time createOrder example
State impact: this code writes request status and may schedule one real market order. It intentionally does not auto-retry.
module.exports.loop = function () {
const request = Memory.market?.createOrderRequest;
const validation = validateOrderRequest(request);
if (!validation.valid) {
return;
}
const room = Game.rooms[request.roomName];
const terminal = room?.terminal ?? null;
const terminalReady = Boolean(
room
&& terminal
&& terminal.my === true
&& terminal.isActive() === true
);
const duplicate = findEquivalentOrder(request);
const plan = evaluateOrderPlan({
request,
credits: Game.market.credits,
terminalReady,
duplicateFound: Boolean(duplicate)
});
request.lastCheckedAt = Game.time;
request.lastStatus = plan.reason;
if (!plan.ready) {
request.preview = {
fee: plan.fee ?? null,
duplicateOrderId: duplicate?.id ?? null,
currentOrderCount:
Object.keys(Game.market.orders).length
};
return;
}
request.enabled = false;
request.status = 'submitted';
request.submittedAt = Game.time;
request.snapshot = {
requestId: request.requestId ?? null,
type: request.type,
resourceType: request.resourceType,
price: request.price,
totalAmount: request.totalAmount,
roomName: request.roomName,
fee: plan.fee,
creditsBefore: Game.market.credits,
currentOrderCount:
Object.keys(Game.market.orders).length
};
const result = Game.market.createOrder({
type: request.type,
resourceType: request.resourceType,
price: request.price,
totalAmount: request.totalAmount,
roomName: request.roomName
});
request.result = result;
request.resultAt = Game.time;
request.status = result === OK
? 'accepted-pending-verification'
: 'failed-review-required';
console.log(JSON.stringify({
type: 'market-create-order-result',
requestId: request.requestId ?? null,
roomName: request.roomName,
resourceType: request.resourceType,
orderType: request.type,
price: request.price,
totalAmount: request.totalAmount,
fee: plan.fee,
result
}));
};
Disable before the irreversible call
The request is disabled before createOrder(). A transient error, changed Terminal, invalid arguments, or order-cap failure must not cause another automatic order on the next tick. Review the snapshot and return code, then create a new request ID if another attempt is justified.
Do not hard-code the disputed order limit
As of the verification date, the official API page says the maximum order count is 300 in the method description, while the ERR_FULL table still says more than 50 cannot be created. Because those statements conflict, this guide logs:
const currentOrderCount = Object.keys(
Game.market.orders
).length;
It does not reject at 50 or 300. The actual API result remains authoritative, and ERR_FULL requires manual review rather than an automatic loop.
Verify the order after OK
function findOrderAfterRequest(snapshot) {
return Object.values(Game.market.orders).find(order =>
order.type === snapshot.type
&& order.resourceType === snapshot.resourceType
&& order.roomName === snapshot.roomName
&& order.price === snapshot.price
&& order.totalAmount === snapshot.totalAmount
) || null;
}
A matching order confirms that the order now exists in your account snapshot. Check its active, amount, remainingAmount, and totalAmount separately. Existence does not prove a trade has occurred.
Change, extend, or cancel the existing order
changeOrderPrice()changes price; raising it charges 5% on the price difference multiplied by remaining amount.extendOrder()adds capacity and charges 5% on the added value.cancelOrder()removes the order; the original 5% fee is not returned.
Frequent cancel-and-recreate cycles can lose Credits unnecessarily.
Handle return codes
| Code | Interpretation | Review |
|---|---|---|
OK | Operation scheduled | Find the order afterward |
ERR_NOT_OWNER | Terminal or room ownership condition failed | Room and Terminal |
ERR_NOT_ENOUGH_RESOURCES | Insufficient Credits for the fee | Fee and reserve |
ERR_FULL | No more orders accepted | Current orders and current API behavior |
ERR_INVALID_ARGS | Invalid request arguments | Type, resource, price, amount, room |
Debugging checklist
- Require an explicit one-time request ID.
- Validate type, resource, room, price, and integer amount.
- Check an owned active Terminal for ordinary resources.
- Calculate the 5% fee.
- Preserve a Credit reserve.
- Reject an equivalent existing order.
- Disable before calling the API.
- Save the exact snapshot and return code.
- Handle
ERR_FULLwithout hard-coded 50 or 300 checks. - Verify order existence and activity later.
- Never describe order creation as a completed trade.
Scope and next steps
This guide does not implement automatic pricing, market forecasting, profit calculations, account-bound resources, multi-shard strategy, automatic retries, or fill prediction. Continue with a reviewed one-time deal.
Frequently asked questions
Is price 1 a recommended Utrium price?
No. It is an example input.
Why check duplicates before calling?
Repeated main-loop calls can create several equivalent orders and pay several fees.
Does cancellation refund the fee?
No. The official documentation says the 5% fee is not returned.
Can I trust one documented order-limit number?
Not while the current API page contains conflicting 300 and 50 statements. Handle the return code.