API
Quickstart: from a key to a price
Three requests gets you a real quote — sliced on a real engine against a real printer profile, not estimated from volume. This page is the short path; the full reference has every field.
Before you start
- A secret key (
sk_live_…) from your account page. - A STL, STEP or 3MF file up to 50 MB (this page walks an STL).
- At least one machine set up on your shop, since a quote is priced against a real machine profile.
There is no sandbox key to swap out later — your key is live from the first day of the free trial, so what you build against on this page is what ships. Quoting is metered by your plan (750 or 2,000 a month); slicing a model costs a quote whether or not anyone ever pays for it.
Base URL and auth
# Base URL https://api.3dash.in/v1 # Every request Authorization: Bearer $SK
Secret keys are server-side only. Secret-key routes return no CORS headers at all, so a browser physically cannot call them cross-origin — that is the enforcement, not a warning. If you need to quote from a browser, use your publishable key with the widget, or proxy our API through your own backend.
1Create a model
You do not post the file to us. You ask for somewhere to put it, and get back a signed upload URL.
curl -X POST https://api.3dash.in/v1/models \ -H "Authorization: Bearer $SK" \ -H "Content-Type: application/json" \ -d '{ "filename": "bracket.stl" }'
{
"id": "mdl_8fA2k9",
"upload": {
"method": "PUT",
"url": "https://storage.googleapis.com/…",
"headers": { "Content-Type": "application/octet-stream" },
"expires_at": "2026-08-10T12:05:00Z"
},
"status": "awaiting_upload"
}
2Upload the file
A plain PUT to the URL you were just given, with the headers it specified. This goes straight to storage and does not pass through our API.
curl -X PUT "<upload.url>" \ -H "Content-Type: application/octet-stream" \ --data-binary @bracket.stl
Once the upload lands, the model is inspected and moves to ready. Fetching it back gives you the geometry facts before you commit to a quote:
{
"id": "mdl_8fA2k9", "status": "ready", "filename": "bracket.stl",
"volume_mm3": 14203.7, "bbox_mm": [62.0, 41.5, 18.2],
"triangles": 24880, "is_watertight": true
}
A model id is good for 30 minutes, counted from the last time you used it — quoting resets the clock. Past that it is model_not_found. Upload as part of quoting rather than storing ids and coming back to them tomorrow.
3Create a quote
Slicing takes a moment, so you can either poll or ask the request to wait. ?wait=10 holds the connection for up to ten seconds, which is almost always enough.
curl -X POST "https://api.3dash.in/v1/quotes?wait=10" \ -H "Authorization: Bearer $SK" \ -H "Content-Type: application/json" \ -d '{ "material": "pla", "lines": [{ "model_id": "mdl_8fA2k9", "quantity": 2, "infill": 20, "layer_height_mm": 0.2, "supports": true }] }'
{
"id": "qte_M3xQ1", "status": "completed", "currency": "INR",
"machine_id": "mch_a1", "material": "pla",
"lines": [{
"model_id": "mdl_8fA2k9", "quantity": 2,
"grams": 41.8, "print_seconds": 9420,
"amount": 74000,
"engine": { "name": "prusa-slicer", "version": "2.9.6" },
"flags": [{ "code": "thin_feature", "severity": "warning",
"message": "A section under 1.2 mm may snap during removal." }]
}],
"subtotal": 74000, "delivery": 0, "total": 74000,
"expires_at": "2026-08-17T12:00:00Z"
}
Reading the response
| Field | Unit | Notes |
|---|---|---|
amount, subtotal, total | Integer paise | Never floats. 74000 is ₹740.00. Divide by 100 only at the point you render it. |
grams | Grams | What the spool actually loses, including supports — not model volume converted. |
print_seconds | Integer seconds | The slicer's own estimate for that machine. |
engine | — | Which slicer and version produced the number. Useful when a figure surprises you. |
flags | — | Printability warnings on the geometry, e.g. thin features. Advisory, not a rejection. |
expires_at | Timestamp | Seven days. Quotes are not open-ended; material prices move. |
A quote never contains a rate. No per-gram figure, no machine hourly cost, no multiplier or minimum — line amounts and totals only. That is deliberate and permanent: the widget lives in a public page and this API may be proxied into one, so pricing inputs stay on the server. Your own rate card is yours to read and edit under Manage your store; no endpoint returns it, at any key class or tier.
Money, precisely
Every amount in this API is an integer in paise with an explicit currency. There are no rupee floats in either direction. This is not fussiness — rupee-float rounding has already caused a payment refund to fail silently once, and integer paise is the fix.
When it doesn't finish in ten seconds
A slice is around a second for a typical part, but a big one, twenty lines, or a cold start can outrun any hold you are willing to put on an HTTP request. Past your wait you get 202 and the same object with null where the numbers go. Poll it:
curl "https://api.3dash.in/v1/quotes/qte_M3xQ1" \ -H "Authorization: Bearer $SK" # -> { "status": "pending" } keep going, a second or two apart # -> { "status": "completed" } the numbers are in # -> { "status": "failed", "error": { ... } }
A failed quote is a 200 with an error object inside it, not an HTTP error: your request was fine, the slice was not. Branch on status, never on the status code. Write the polling path even if wait covers you today — the shape is identical, so it costs you almost nothing.
What else is there
GET /v1/machines— your machines, so you can pin a part to one. Leavemachine_idoff and each part is routed for you.GET /v1/materials— what you offer, with the densities we compute grams from.GET /v1/health— which slicing engines this deployment has. No key needed.
That is the entire surface. There are no list endpoints, no webhooks and no orders endpoint in v1 — POST /v1/orders answers 501. If you want to take payment, the widget does checkout on your own Razorpay account today.
Every field, every limit and every error code is in the full API reference. If you would rather not build a front end at all, the drop-in widget does this whole flow for you.