# API billing

> How API print orders are paid for: the card on file is charged the moment you submit, what the charge covers, and what happens when it is declined.

Source: https://x3dstudios.com/docs/api/billing  ·  Updated: 2026-10-20

An API print order is paid at the moment you submit it. The route quotes the file, writes the order, then charges the card saved on your X3D account for the full amount — print plus postage — and returns the result in the same response. Nothing is invoiced later, and nothing is charged when the part ships.

> **A card is required before a key exists** — POST /api/account/api-key refuses to mint a key while the account has no card, returning 402 with code CARD_REQUIRED. Add one at /profile. The card can be removed afterwards, and the key keeps working — submissions then come back unpaid with the reason "No card on file".

## What the charge covers

- The print itself, at one all-in rate per gram that covers filament and normal machine time.
- Postage to the address in the request, at the speed in shippingSpeed.
- The $25 second-AMS fee, once per order, when a sliced .gcode.3mf uses five or more filaments. It is part of the same single charge and shows in breakdown.secondAmsFee.
- Nothing else. There is no setup fee, no per-order platform fee and no minimum monthly spend.

It is a single payment for the whole order, in USD, taken off-session against the card on file. The payment record carries a description built from the order — material, colour, nozzle, layer height, quantity and file name — so a charge can be traced back to a submission even without the order code, and the order code itself is attached to the payment as metadata.

> **estimatedCost is what you were charged** — The field is named for the quote it came from, but it is the exact amount taken: the charge is that number in cents. On a successful submission the response repeats it in charged. Nothing is trued up afterwards if the print comes off the plate heavier or lighter than quoted.

## The rates

| material | Rate | Density |
| --- | --- | --- |
| pla | $0.12/g | 1.26 g/cm³ |
| petg | $0.12/g | 1.27 g/cm³ |
| abs | $0.12/g | 1.04 g/cm³ |
| asa | $0.14/g | 1.07 g/cm³ |
| tpu | $0.25/g | 1.24 g/cm³ |

- Billable weight rounds up to the whole gram, the way postage does.
- quality=premium (0.1 mm layers) multiplies the per-gram rate by 1.75. quality=standard (0.2 mm) is 1×.
- Minimum $1.00 per part.
- Twelve stock colours bill at the material rate. Any other colour bills at a $0.20/g floor, because the spool has to be bought in.
- Bulk: 5 or more units take 5% off, 10 or more take 10%.
- US postage is $7 standard, free once the print subtotal reaches $49.50, or $15 express, never free. International is $30 flat, free at $500.
- An order that works out below $0.50 is refused with a 400 — that is the floor our payment processor accepts.

> **No colour means a stock colour** — Omit the color field and the order is stored as "Default", our choice of a spool we stock, so it prices at the material rate. Until 29 September 2026 it priced at the $0.20/g custom-colour floor instead; orders placed since are not affected.

## A worked example

A bracket whose mesh measures 100 cm³, submitted as PLA in Black at standard quality with the default 20% infill, quantity 1, shipping inside the US.

| Step | Arithmetic | Result |
| --- | --- | --- |
| Solid volume | 100 cm³ × (30% walls + 70% × 20% infill) | 44 cm³ |
| Weight | 44 cm³ × 1.26 g/cm³, rounded up | 56 g |
| Print | 56 g × $0.12 × 1.0 (standard) | $6.72 |
| Machine surcharge | No colour changes on a mesh | $0.00 |
| Postage | US flat, subtotal under $49.50 | $7.00 |
| Charged | $6.72 + $7.00 | $13.72 |

*The response returns the same figures under breakdown, as print, shipping, grams and printHours.*

Three variations on that same part show where the money actually moves. At quantity 10 the print is $6.72 × 10 × 0.90 = $60.48, which clears the $49.50 threshold, so postage drops to nothing and the total is $60.48. At premium quality the print becomes 56 × $0.12 × 1.75 = $11.76, for $18.76 with postage. In a colour we do not stock, such as Navy, it is 56 × $0.20 = $11.20, for $18.20 — nearly as much as the premium version of the same part, on a standard-quality print.

### Already-sliced files are priced differently

Send an STL or GLB and the weight is estimated from geometry, as above. Send a .gcode or .gcode.3mf and the slicer's own measurements are used instead — its filament weight and its print time — because a volume estimate cannot see purge waste or tool changes. On top of the per-gram charge, hours that the weight cannot explain are billed at $1.50/hour.

| Step | Arithmetic | Result |
| --- | --- | --- |
| Slicer reports | Four-colour plate, PLA | 120 g, 20 h |
| Print | 120 g × $0.12 | $14.40 |
| Hours a 120 g print normally takes | 120 g ÷ (1.26 g/cm³ × 16 cm³/h) | 5.95 h |
| Excess hours, at $1.50/h | (20 − 5.95) × $1.50 | $21.07 |
| Postage | US flat, subtotal under $49.50 | $7.00 |
| Charged | $14.40 + $21.07 + $7.00 | $42.47 |

*A four-colour plate spends most of its run swapping and purging filament, which is why time — not material — dominates the price.*

> **The quote is not a slice** — For a mesh, the API prices from the geometry it can read; it does not run OrcaSlicer on your STL before charging you. If you want the price to come from a real slice, slice it yourself and submit the .gcode.3mf — then the numbers we bill are the numbers your slicer showed you.

## When the charge fails

The order row is written before the card is charged, so a decline does not throw away the work. You get 201, a real order code, and a response that says plainly what went wrong.

**201 — order created, card declined**

```json
{
  "code": "X3D-K7M2QP",
  "estimatedCost": 13.72,
  "currency": "USD",
  "breakdown": { "print": 6.72, "shipping": 7, "shippingSpeed": "standard", "grams": 56, "printHours": 2.75 },
  "status": "PENDING_PAYMENT",
  "paid": false,
  "chargeOutcome": "not-charged",
  "paymentUrl": "https://x3dstudios.com/print/order/X3D-K7M2QP/pay",
  "checkoutUrl": "https://checkout.stripe.com/…",
  "message": "Auto-charge failed (No card on file) and nothing was taken. Update your card on file, or pay via paymentUrl."
}
```

| Reason in the message | What it means |
| --- | --- |
| No card on file | The card was removed after the key was issued. Add one at /profile. |
| Payment requires_action | The card asked for 3-D Secure. The charge runs off-session, so there is nobody to complete the challenge. This comes back with chargeOutcome "unknown" and the could-not-be-confirmed message; once a read of the order shows it still unpaid, pay through paymentUrl, where someone can answer the challenge. |
| Any other text | Passed through verbatim from the payment processor, e.g. a declined or expired card. |

> **When the outcome is unknown, do not pay twice** — chargeOutcome is "not-charged" when the card was provably not charged: a decline, a missing card, or a request the processor refused. It is "unknown" for everything else: the connection failed after the charge was sent, or the payment was left in a state such as processing or requires_action. The message then says the charge may have gone through. In that case read the order back with GET /api/print/orders/{code}, or ask us, before anyone opens paymentUrl.

- The order stays at PENDING_PAYMENT and is never auto-retried.
- The farm will not queue it — an operator trying to put an unpaid order on a printer gets refused.
- paymentUrl is a page on x3dstudios.com that forwards to the hosted payment page; checkoutUrl is that page directly. Either works, and both are null if payments are unavailable at that moment.
- Paying through that link flips the order to RECEIVED and marks it paid, the same end state a successful card charge produces.

> **Do not resubmit a declined order** — Resubmitting creates a second order at a second price and, once the card works, a second charge. Fix the card and pay the order you already have.

## Receipts and records

- Every paid API order sends a confirmation email with the order code, a one-line summary and the shipping address.
- The email goes to every address in the order's email field, which defaults to your account address. Every one of them gets the full confirmation, total charged included, so an end customer you list receives the receipt for a charge on your card. On a reseller integration that is usually not what you want.
- The order itself is readable at /print/order/{code} and through GET /api/print/orders/{code}. Both keep working after the print ships.
- The card itself is added and replaced at /profile, in the same Developer API key panel that issues the key. The charges it takes appear in the payment history on /orders.

> **API orders are on your statement page** — /orders and /orders/statement read both order stores, so signed in as the key's account you see API orders beside your website orders, by their X3D- codes. The REST API itself has no endpoint that lists them, so store every code you get back at submission; the MCP tool list_my_orders lists your 20 most recent with the same key.

## How this relates to the website

|  | API (POST /api/print/orders) | Website print order |
| --- | --- | --- |
| Rates | Identical — the same quoting code runs for both | Identical |
| Payment | Saved card charged during the request | Payment link, paid in a browser |
| Result if payment fails | 201 with an unpaid order | Order waits at the payment step |
| Gift cards | Not accepted | Accepted in the cart |
| Subscription print credit | Not applied | Applied at checkout |
| First-order discount | Not applied | Applied to the goods, not the postage |
| Express postage | shippingSpeed=express, US only | Chosen at checkout, US only |
| Priority print | Not offered: queued as Normal print | +$5 per order at checkout |

Renting a printer by the hour is a different product with different economics — $1.50/hour or $15/day for the machine, plus filament at raw cost ($0.02/g PLA, $0.025/g PETG, $0.03/g ABS) — and it is not part of this API. Quoting the all-in per-gram rate against a reservation bills machine time twice. See /docs/farm/reservations.

- [Pricing model](https://x3dstudios.com/docs/pricing) — The full explanation of the per-gram rate, quality factors and bulk pricing.
- [Errors](https://x3dstudios.com/docs/api/errors) — Every status code, and why a 201 is not always a receipt.
- [Submit an order](https://x3dstudios.com/docs/api/submit-order) — Every field the endpoint accepts and what each one defaults to.
- [Managing keys](https://x3dstudios.com/docs/api/keys) — Issuing, rotating and revoking the key that carries the charge.

## Frequently asked

**When is my card charged for an API print order?**

At submission, not on completion. POST /api/print/orders writes the order, then immediately charges your saved card for the whole amount — printing and postage together — before it returns. There is no invoice at the end of the month and no charge when the part ships.

**What happens if my card is declined on an API order?**

You still get 201 and a real order code, with paid: false and status PENDING_PAYMENT. The order is not deleted and it is not queued — the farm refuses to put an unpaid order on a printer. The response carries a paymentUrl you can open to pay it by hand.

**Do API orders show up on my X3D statement?**

Yes. /orders and the statement at /orders/statement read both order stores, so an order placed with your key appears beside your website orders when you are signed in as the key's account. Each one also lives at /print/order/{code} and in GET /api/print/orders/{code}, and each paid one sends a confirmation email.

**Is API pricing different from the website's?**

The rates are identical — both run through the same quoting code. What differs is settlement, discounts and one option: the API charges a saved card instantly, while the website returns a payment link; gift cards, subscription print credit and the first-order discount apply only on the website; and Priority print is sold only at the website checkout.
